diff --git a/.bra.toml b/.bra.toml index ba60f451517..e832e9beee5 100644 --- a/.bra.toml +++ b/.bra.toml @@ -1,17 +1,17 @@ [run] init_cmds = [ - ["go", "build", "-o", "./bin/grafana"], - ["./bin/grafana", "web"] + ["go", "build", "-o", "./bin/grafana-server"], + ["./bin/grafana-server"] ] watch_all = true watch_dirs = [ "$WORKDIR/pkg", - "$WORKDIR/src/views", + "$WORKDIR/public/views", "$WORKDIR/conf", ] watch_exts = [".go", ".ini"] build_delay = 1500 cmds = [ - ["go", "build", "-o", "./bin/grafana"], - ["./bin/grafana", "web"] + ["go", "build", "-o", "./bin/grafana-server"], + ["./bin/grafana-server"] ] diff --git a/.gitignore b/.gitignore index 94b9e97f0e1..c3eb5d8862c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,7 @@ docs/changed-files docs/changed-files # locally required config files -web.config -config.js -src/css/*.min.css +public/css/*.min.css # Editor junk *.sublime-workspace diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d7ae9eb315d..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: node_js -node_js: - - "0.10" -git: - depth: 1 -before_script: - - npm install -g grunt-cli -after_script: - - npm run coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e1527ad8c..b9133bf8601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,68 @@ -# 2.0.0 (unreleased) +# 2.0.3 (unreleased) + +**Fixes** +- [Issue #1872](https://github.com/grafana/grafana/issues/1872). Firefox/IE issue, invisible text in dashboard search fixed +- [Issue #1857](https://github.com/grafana/grafana/issues/1857). /api/login/ping Fix for issue when behind reverse proxy and subpath +- [Issue #1863](https://github.com/grafana/grafana/issues/1863). MySQL: Dashboard.data column type changed to mediumtext (sql migration added) + +# 2.0.2 (2015-04-22) + +**Fixes** +- [Issue #1832](https://github.com/grafana/grafana/issues/1832). Graph Panel + Legend Table mode: Many series casued zero height graph, now legend will never reduce the height of the graph below 50% of row height. +- [Issue #1846](https://github.com/grafana/grafana/issues/1846). Snapshots: Fixed issue with snapshoting dashboards with an interval template variable +- [Issue #1848](https://github.com/grafana/grafana/issues/1848). Panel timeshift: You can now use panel timeshift without a relative time override + +# 2.0.1 (2015-04-20) + +**Fixes** +- [Issue #1784](https://github.com/grafana/grafana/issues/1784). Data source proxy: Fixed issue with using data source proxy when grafana is behind nginx suburl +- [Issue #1749](https://github.com/grafana/grafana/issues/1749). Graph Panel: Table legends are now visible when rendered to PNG +- [Issue #1786](https://github.com/grafana/grafana/issues/1786). Graph Panel: Legend in table mode now aligns, graph area is reduced depending on how many series +- [Issue #1734](https://github.com/grafana/grafana/issues/1734). Support for unicode / international characters in dashboard title (improved slugify) +- [Issue #1782](https://github.com/grafana/grafana/issues/1782). Github OAuth: Now works with Github for Enterprise, thanks @williamjoy +- [Issue #1780](https://github.com/grafana/grafana/issues/1780). Dashboard snapshot: Should not require login to view snapshot, Fixes #1780 + +# 2.0.0-Beta3 (2015-04-12) + +**RPM / DEB Package changes (to follow HFS)** +- binary name changed to grafana-server +- does not install to `/opt/grafana` any more, installs to `/usr/share/grafana` +- binary to `/usr/sbin/grafana-server` +- init.d script improvements, renamed to `/etc/init.d/grafana-server` +- added default file with environment variables, + - `/etc/default/grafana-server` (deb/ubuntu) + - `/etc/sysconfig/grafana-server` (centos/redhat) + +- added systemd service file, tested on debian jessie and centos7 +- config file in same location `/etc/grafana/grafana.ini` (now complete config file but with every setting commented out) +- data directory (where sqlite3) file is stored is now by default `/var/lib/grafana` +- no symlinking current to versions anymore +- For more info see [Issue #1758](https://github.com/grafana/grafana/issues/1758). + +**Config breaking change (setting rename)** +- `[log] root_path` has changed to `[paths] logs` + +# 2.0.0-Beta2 (...) + +**Enhancements** +- [Issue #1701](https://github.com/grafana/grafana/issues/1701). Share modal: Override UI theme via URL param for Share link, rendered panel, or embedded panel +- [Issue #1660](https://github.com/grafana/grafana/issues/1660). OAuth: Specify allowed email address domains for google or and github oauth logins + +**Fixes** +- [Issue #1649](https://github.com/grafana/grafana/issues/1649). HTTP API: grafana /render calls nows with api keys +- [Issue #1667](https://github.com/grafana/grafana/issues/1667). Datasource proxy & session timeout fix (casued 401 Unauthorized error after a while) +- [Issue #1707](https://github.com/grafana/grafana/issues/1707). Unsaved changes: Do not show for snapshots, scripted and file based dashboards +- [Issue #1703](https://github.com/grafana/grafana/issues/1703). Unsaved changes: Do not show for users with role `Viewer` +- [Issue #1675](https://github.com/grafana/grafana/issues/1675). Data source proxy: Fixed issue with Gzip enabled and data source proxy +- [Issue #1681](https://github.com/grafana/grafana/issues/1681). MySQL session: fixed problem using mysql as session store +- [Issue #1671](https://github.com/grafana/grafana/issues/1671). Data sources: Fixed issue with changing default data source (should not require full page load to take effect, now fixed) +- [Issue #1685](https://github.com/grafana/grafana/issues/1685). Search: Dashboard results should be sorted alphabetically +- [Issue #1673](https://github.com/grafana/grafana/issues/1673). Basic auth: Fixed issue when using basic auth proxy infront of Grafana + +# 2.0.0-Beta1 (2015-03-30) **New features** +- [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site - [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site - [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes inbetween the user is promted with a warning if he really wants to overwrite the other's changes - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views @@ -19,6 +81,7 @@ - [Issue #599](https://github.com/grafana/grafana/issues/599). Graph: Added right y axis label setting and graph support - [Issue #1253](https://github.com/grafana/grafana/issues/1253). Graph & Singlestat: Users can now set decimal precision for legend and tooltips (override auto precision) - [Issue #1255](https://github.com/grafana/grafana/issues/1255). Templating: Dashboard will now wait to load until all template variables that have refresh on load set or are initialized via url to be fully loaded and so all variables are in valid state before panels start issuing metric requests. +- [Issue #1344](https://github.com/grafana/grafana/issues/1344). OpenTSDB: Alias patterns (reference tag values), syntax is: $tag_tagname or [[tag_tagname]] **Fixes** - [Issue #1298](https://github.com/grafana/grafana/issues/1298). InfluxDB: Fix handling of empty array in templating variable query diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 79b49db2f0a..dd08ac91121 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,5 +1,5 @@ { - "ImportPath": "github.com/torkelo/grafana-pro", + "ImportPath": "github.com/grafana/grafana", "GoVersion": "go1.3", "Packages": [ "./pkg/..." @@ -14,9 +14,12 @@ "Rev": "93de4f3fad97bf246b838f828e2348f46f21f20a" }, { - "ImportPath": "github.com/codegangsta/cli", - "Comment": "1.2.0-38-g9908e96", - "Rev": "9908e96513e5a94de37004098a3974a567f18111" + "ImportPath": "github.com/dalu/slug", + "Rev": "6dbd13912e9be466e2c1de349a2c7d1466c97e07" + }, + { + "ImportPath": "github.com/dalu/unidecode", + "Rev": "339814d47f3e32a6f7036a0a4c56ed9b373dd755" }, { "ImportPath": "github.com/go-sql-driver/mysql", @@ -25,12 +28,12 @@ }, { "ImportPath": "github.com/go-xorm/core", - "Rev": "a949e067ced1cb6e6ef5c38b6f28b074fa718f1e" + "Rev": "be6e7ac47dc57bd0ada25322fa526944f66ccaa6" }, { "ImportPath": "github.com/go-xorm/xorm", - "Comment": "v0.4.1-19-g5c23849", - "Rev": "5c23849a66f4593e68909bb6c1fa30651b5b0541" + "Comment": "v0.4.2-58-ge2889e5", + "Rev": "e2889e5517600b82905f1d2ba8b70deb71823ffe" }, { "ImportPath": "github.com/jtolds/gls", @@ -47,11 +50,11 @@ }, { "ImportPath": "github.com/macaron-contrib/session", - "Rev": "65b8817c40cb5bdce08673a15fd2a648c2ba0e16" + "Rev": "31e841d95c7302b9ac456c830ea2d6dfcef4f84a" }, { "ImportPath": "github.com/mattn/go-sqlite3", - "Rev": "d10e2c8f62100097910367dee90a9bd89d426a44" + "Rev": "e28cd440fabdd39b9520344bc26829f61db40ece" }, { "ImportPath": "github.com/smartystreets/goconvey/convey", @@ -68,12 +71,26 @@ }, { "ImportPath": "golang.org/x/oauth2", - "Rev": "e5909d4679a1926c774c712b343f10b8298687a3" + "Rev": "c58fcf0ffc1c772aa2e1ee4894bc19f2649263b2" + }, + { + "ImportPath": "gopkg.in/bufio.v1", + "Comment": "v1", + "Rev": "567b2bfa514e796916c4747494d6ff5132a1dfce" }, { "ImportPath": "gopkg.in/ini.v1", "Comment": "v0-16-g1772191", "Rev": "177219109c97e7920c933e21c9b25f874357b237" + }, + { + "ImportPath": "gopkg.in/redis.v2", + "Comment": "v2.3.2", + "Rev": "e6179049628164864e6e84e973cfb56335748dea" + }, + { + "ImportPath": "gopkgs.com/pool.v1", + "Rev": "c850f092aad1780cbffff25f471c5cc32097932a" } ] } diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml b/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml deleted file mode 100644 index baf46abc6f0..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: go -go: 1.1 - -script: -- go vet ./... -- go test -v ./... diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE b/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE deleted file mode 100644 index 5515ccfb716..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Copyright (C) 2013 Jeremy Saenz -All Rights Reserved. - -MIT LICENSE - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/README.md b/Godeps/_workspace/src/github.com/codegangsta/cli/README.md deleted file mode 100644 index e0fdace3198..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/README.md +++ /dev/null @@ -1,285 +0,0 @@ -[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) - -# cli.go -cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. - -You can view the API docs here: -http://godoc.org/github.com/codegangsta/cli - -## Overview -Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. - -**This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive! - -## Installation -Make sure you have a working Go environment (go 1.1 is *required*). [See the install instructions](http://golang.org/doc/install.html). - -To install `cli.go`, simply run: -``` -$ go get github.com/codegangsta/cli -``` - -Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: -``` -export PATH=$PATH:$GOPATH/bin -``` - -## Getting Started -One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. - -``` go -package main - -import ( - "os" - "github.com/codegangsta/cli" -) - -func main() { - cli.NewApp().Run(os.Args) -} -``` - -This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: - -``` go -package main - -import ( - "os" - "github.com/codegangsta/cli" -) - -func main() { - app := cli.NewApp() - app.Name = "boom" - app.Usage = "make an explosive entrance" - app.Action = func(c *cli.Context) { - println("boom! I say!") - } - - app.Run(os.Args) -} -``` - -Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. - -## Example - -Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! - -Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: - -``` go -package main - -import ( - "os" - "github.com/codegangsta/cli" -) - -func main() { - app := cli.NewApp() - app.Name = "greet" - app.Usage = "fight the loneliness!" - app.Action = func(c *cli.Context) { - println("Hello friend!") - } - - app.Run(os.Args) -} -``` - -Install our command to the `$GOPATH/bin` directory: - -``` -$ go install -``` - -Finally run our new command: - -``` -$ greet -Hello friend! -``` - -cli.go also generates some bitchass help text: -``` -$ greet help -NAME: - greet - fight the loneliness! - -USAGE: - greet [global options] command [command options] [arguments...] - -VERSION: - 0.0.0 - -COMMANDS: - help, h Shows a list of commands or help for one command - -GLOBAL OPTIONS - --version Shows version information -``` - -### Arguments -You can lookup arguments by calling the `Args` function on `cli.Context`. - -``` go -... -app.Action = func(c *cli.Context) { - println("Hello", c.Args()[0]) -} -... -``` - -### Flags -Setting and querying flags is simple. -``` go -... -app.Flags = []cli.Flag { - cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", - }, -} -app.Action = func(c *cli.Context) { - name := "someone" - if len(c.Args()) > 0 { - name = c.Args()[0] - } - if c.String("lang") == "spanish" { - println("Hola", name) - } else { - println("Hello", name) - } -} -... -``` - -#### Alternate Names - -You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. - -``` go -app.Flags = []cli.Flag { - cli.StringFlag{ - Name: "lang, l", - Value: "english", - Usage: "language for the greeting", - }, -} -``` - -#### Values from the Environment - -You can also have the default value set from the environment via `EnvVar`. e.g. - -``` go -app.Flags = []cli.Flag { - cli.StringFlag{ - Name: "lang, l", - Value: "english", - Usage: "language for the greeting", - EnvVar: "APP_LANG", - }, -} -``` - -That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. - -### Subcommands - -Subcommands can be defined for a more git-like command line app. -```go -... -app.Commands = []cli.Command{ - { - Name: "add", - ShortName: "a", - Usage: "add a task to the list", - Action: func(c *cli.Context) { - println("added task: ", c.Args().First()) - }, - }, - { - Name: "complete", - ShortName: "c", - Usage: "complete a task on the list", - Action: func(c *cli.Context) { - println("completed task: ", c.Args().First()) - }, - }, - { - Name: "template", - ShortName: "r", - Usage: "options for task templates", - Subcommands: []cli.Command{ - { - Name: "add", - Usage: "add a new template", - Action: func(c *cli.Context) { - println("new task template: ", c.Args().First()) - }, - }, - { - Name: "remove", - Usage: "remove an existing template", - Action: func(c *cli.Context) { - println("removed task template: ", c.Args().First()) - }, - }, - }, - }, -} -... -``` - -### Bash Completion - -You can enable completion commands by setting the `EnableBashCompletion` -flag on the `App` object. By default, this setting will only auto-complete to -show an app's subcommands, but you can write your own completion methods for -the App or its subcommands. -```go -... -var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} -app := cli.NewApp() -app.EnableBashCompletion = true -app.Commands = []cli.Command{ - { - Name: "complete", - ShortName: "c", - Usage: "complete a task on the list", - Action: func(c *cli.Context) { - println("completed task: ", c.Args().First()) - }, - BashComplete: func(c *cli.Context) { - // This will complete if no args are passed - if len(c.Args()) > 0 { - return - } - for _, t := range tasks { - fmt.Println(t) - } - }, - } -} -... -``` - -#### To Enable - -Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while -setting the `PROG` variable to the name of your program: - -`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` - - -## Contribution Guidelines -Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. - -If you are have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. - -If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app.go deleted file mode 100644 index f4c4af84c20..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/app.go +++ /dev/null @@ -1,251 +0,0 @@ -package cli - -import ( - "fmt" - "io/ioutil" - "os" - "time" -) - -// App is the main structure of a cli application. It is recomended that -// and app be created with the cli.NewApp() function -type App struct { - // The name of the program. Defaults to os.Args[0] - Name string - // Description of the program. - Usage string - // Version of the program - Version string - // List of commands to execute - Commands []Command - // List of flags to parse - Flags []Flag - // Boolean to enable bash completion commands - EnableBashCompletion bool - // Boolean to hide built-in help command - HideHelp bool - // Boolean to hide built-in version flag - HideVersion bool - // An action to execute when the bash-completion flag is set - BashComplete func(context *Context) - // An action to execute before any subcommands are run, but after the context is ready - // If a non-nil error is returned, no subcommands are run - Before func(context *Context) error - // The action to execute when no subcommands are specified - Action func(context *Context) - // Execute this function if the proper command cannot be found - CommandNotFound func(context *Context, command string) - // Compilation date - Compiled time.Time - // Author - Author string - // Author e-mail - Email string -} - -// Tries to find out when this binary was compiled. -// Returns the current time if it fails to find it. -func compileTime() time.Time { - info, err := os.Stat(os.Args[0]) - if err != nil { - return time.Now() - } - return info.ModTime() -} - -// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. -func NewApp() *App { - return &App{ - Name: os.Args[0], - Usage: "A new cli application", - Version: "0.0.0", - BashComplete: DefaultAppComplete, - Action: helpCommand.Action, - Compiled: compileTime(), - } -} - -// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination -func (a *App) Run(arguments []string) error { - // append help to commands - if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - a.appendFlag(HelpFlag) - } - - //append version/help flags - if a.EnableBashCompletion { - a.appendFlag(BashCompletionFlag) - } - - if !a.HideVersion { - a.appendFlag(VersionFlag) - } - - // parse flags - set := flagSet(a.Name, a.Flags) - set.SetOutput(ioutil.Discard) - err := set.Parse(arguments[1:]) - nerr := normalizeFlags(a.Flags, set) - if nerr != nil { - fmt.Println(nerr) - context := NewContext(a, set, set) - ShowAppHelp(context) - fmt.Println("") - return nerr - } - context := NewContext(a, set, set) - - if err != nil { - fmt.Printf("Incorrect Usage.\n\n") - ShowAppHelp(context) - fmt.Println("") - return err - } - - if checkCompletions(context) { - return nil - } - - if checkHelp(context) { - return nil - } - - if checkVersion(context) { - return nil - } - - if a.Before != nil { - err := a.Before(context) - if err != nil { - return err - } - } - - args := context.Args() - if args.Present() { - name := args.First() - c := a.Command(name) - if c != nil { - return c.Run(context) - } - } - - // Run default Action - a.Action(context) - return nil -} - -// Another entry point to the cli app, takes care of passing arguments and error handling -func (a *App) RunAndExitOnError() { - if err := a.Run(os.Args); err != nil { - os.Stderr.WriteString(fmt.Sprintln(err)) - os.Exit(1) - } -} - -// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags -func (a *App) RunAsSubcommand(ctx *Context) error { - // append help to commands - if len(a.Commands) > 0 { - if a.Command(helpCommand.Name) == nil && !a.HideHelp { - a.Commands = append(a.Commands, helpCommand) - a.appendFlag(HelpFlag) - } - } - - // append flags - if a.EnableBashCompletion { - a.appendFlag(BashCompletionFlag) - } - - // parse flags - set := flagSet(a.Name, a.Flags) - set.SetOutput(ioutil.Discard) - err := set.Parse(ctx.Args().Tail()) - nerr := normalizeFlags(a.Flags, set) - context := NewContext(a, set, ctx.globalSet) - - if nerr != nil { - fmt.Println(nerr) - if len(a.Commands) > 0 { - ShowSubcommandHelp(context) - } else { - ShowCommandHelp(ctx, context.Args().First()) - } - fmt.Println("") - return nerr - } - - if err != nil { - fmt.Printf("Incorrect Usage.\n\n") - ShowSubcommandHelp(context) - return err - } - - if checkCompletions(context) { - return nil - } - - if len(a.Commands) > 0 { - if checkSubcommandHelp(context) { - return nil - } - } else { - if checkCommandHelp(ctx, context.Args().First()) { - return nil - } - } - - if a.Before != nil { - err := a.Before(context) - if err != nil { - return err - } - } - - args := context.Args() - if args.Present() { - name := args.First() - c := a.Command(name) - if c != nil { - return c.Run(context) - } - } - - // Run default Action - if len(a.Commands) > 0 { - a.Action(context) - } else { - a.Action(ctx) - } - - return nil -} - -// Returns the named command on App. Returns nil if the command does not exist -func (a *App) Command(name string) *Command { - for _, c := range a.Commands { - if c.HasName(name) { - return &c - } - } - - return nil -} - -func (a *App) hasFlag(flag Flag) bool { - for _, f := range a.Flags { - if flag == f { - return true - } - } - - return false -} - -func (a *App) appendFlag(flag Flag) { - if !a.hasFlag(flag) { - a.Flags = append(a.Flags, flag) - } -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go deleted file mode 100644 index 81d11743e31..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/app_test.go +++ /dev/null @@ -1,423 +0,0 @@ -package cli_test - -import ( - "fmt" - "os" - "testing" - - "github.com/codegangsta/cli" -) - -func ExampleApp() { - // set args for examples sake - os.Args = []string{"greet", "--name", "Jeremy"} - - app := cli.NewApp() - app.Name = "greet" - app.Flags = []cli.Flag{ - cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, - } - app.Action = func(c *cli.Context) { - fmt.Printf("Hello %v\n", c.String("name")) - } - app.Run(os.Args) - // Output: - // Hello Jeremy -} - -func ExampleAppSubcommand() { - // set args for examples sake - os.Args = []string{"say", "hi", "english", "--name", "Jeremy"} - app := cli.NewApp() - app.Name = "say" - app.Commands = []cli.Command{ - { - Name: "hello", - ShortName: "hi", - Usage: "use it to see a description", - Description: "This is how we describe hello the function", - Subcommands: []cli.Command{ - { - Name: "english", - ShortName: "en", - Usage: "sends a greeting in english", - Description: "greets someone in english", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "name", - Value: "Bob", - Usage: "Name of the person to greet", - }, - }, - Action: func(c *cli.Context) { - fmt.Println("Hello,", c.String("name")) - }, - }, - }, - }, - } - - app.Run(os.Args) - // Output: - // Hello, Jeremy -} - -func ExampleAppHelp() { - // set args for examples sake - os.Args = []string{"greet", "h", "describeit"} - - app := cli.NewApp() - app.Name = "greet" - app.Flags = []cli.Flag{ - cli.StringFlag{Name: "name", Value: "bob", Usage: "a name to say"}, - } - app.Commands = []cli.Command{ - { - Name: "describeit", - ShortName: "d", - Usage: "use it to see a description", - Description: "This is how we describe describeit the function", - Action: func(c *cli.Context) { - fmt.Printf("i like to describe things") - }, - }, - } - app.Run(os.Args) - // Output: - // NAME: - // describeit - use it to see a description - // - // USAGE: - // command describeit [arguments...] - // - // DESCRIPTION: - // This is how we describe describeit the function -} - -func ExampleAppBashComplete() { - // set args for examples sake - os.Args = []string{"greet", "--generate-bash-completion"} - - app := cli.NewApp() - app.Name = "greet" - app.EnableBashCompletion = true - app.Commands = []cli.Command{ - { - Name: "describeit", - ShortName: "d", - Usage: "use it to see a description", - Description: "This is how we describe describeit the function", - Action: func(c *cli.Context) { - fmt.Printf("i like to describe things") - }, - }, { - Name: "next", - Usage: "next example", - Description: "more stuff to see when generating bash completion", - Action: func(c *cli.Context) { - fmt.Printf("the next example") - }, - }, - } - - app.Run(os.Args) - // Output: - // describeit - // d - // next - // help - // h -} - -func TestApp_Run(t *testing.T) { - s := "" - - app := cli.NewApp() - app.Action = func(c *cli.Context) { - s = s + c.Args().First() - } - - err := app.Run([]string{"command", "foo"}) - expect(t, err, nil) - err = app.Run([]string{"command", "bar"}) - expect(t, err, nil) - expect(t, s, "foobar") -} - -var commandAppTests = []struct { - name string - expected bool -}{ - {"foobar", true}, - {"batbaz", true}, - {"b", true}, - {"f", true}, - {"bat", false}, - {"nothing", false}, -} - -func TestApp_Command(t *testing.T) { - app := cli.NewApp() - fooCommand := cli.Command{Name: "foobar", ShortName: "f"} - batCommand := cli.Command{Name: "batbaz", ShortName: "b"} - app.Commands = []cli.Command{ - fooCommand, - batCommand, - } - - for _, test := range commandAppTests { - expect(t, app.Command(test.name) != nil, test.expected) - } -} - -func TestApp_CommandWithArgBeforeFlags(t *testing.T) { - var parsedOption, firstArg string - - app := cli.NewApp() - command := cli.Command{ - Name: "cmd", - Flags: []cli.Flag{ - cli.StringFlag{Name: "option", Value: "", Usage: "some option"}, - }, - Action: func(c *cli.Context) { - parsedOption = c.String("option") - firstArg = c.Args().First() - }, - } - app.Commands = []cli.Command{command} - - app.Run([]string{"", "cmd", "my-arg", "--option", "my-option"}) - - expect(t, parsedOption, "my-option") - expect(t, firstArg, "my-arg") -} - -func TestApp_Float64Flag(t *testing.T) { - var meters float64 - - app := cli.NewApp() - app.Flags = []cli.Flag{ - cli.Float64Flag{Name: "height", Value: 1.5, Usage: "Set the height, in meters"}, - } - app.Action = func(c *cli.Context) { - meters = c.Float64("height") - } - - app.Run([]string{"", "--height", "1.93"}) - expect(t, meters, 1.93) -} - -func TestApp_ParseSliceFlags(t *testing.T) { - var parsedOption, firstArg string - var parsedIntSlice []int - var parsedStringSlice []string - - app := cli.NewApp() - command := cli.Command{ - Name: "cmd", - Flags: []cli.Flag{ - cli.IntSliceFlag{Name: "p", Value: &cli.IntSlice{}, Usage: "set one or more ip addr"}, - cli.StringSliceFlag{Name: "ip", Value: &cli.StringSlice{}, Usage: "set one or more ports to open"}, - }, - Action: func(c *cli.Context) { - parsedIntSlice = c.IntSlice("p") - parsedStringSlice = c.StringSlice("ip") - parsedOption = c.String("option") - firstArg = c.Args().First() - }, - } - app.Commands = []cli.Command{command} - - app.Run([]string{"", "cmd", "my-arg", "-p", "22", "-p", "80", "-ip", "8.8.8.8", "-ip", "8.8.4.4"}) - - IntsEquals := func(a, b []int) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true - } - - StrsEquals := func(a, b []string) bool { - if len(a) != len(b) { - return false - } - for i, v := range a { - if v != b[i] { - return false - } - } - return true - } - var expectedIntSlice = []int{22, 80} - var expectedStringSlice = []string{"8.8.8.8", "8.8.4.4"} - - if !IntsEquals(parsedIntSlice, expectedIntSlice) { - t.Errorf("%v does not match %v", parsedIntSlice, expectedIntSlice) - } - - if !StrsEquals(parsedStringSlice, expectedStringSlice) { - t.Errorf("%v does not match %v", parsedStringSlice, expectedStringSlice) - } -} - -func TestApp_BeforeFunc(t *testing.T) { - beforeRun, subcommandRun := false, false - beforeError := fmt.Errorf("fail") - var err error - - app := cli.NewApp() - - app.Before = func(c *cli.Context) error { - beforeRun = true - s := c.String("opt") - if s == "fail" { - return beforeError - } - - return nil - } - - app.Commands = []cli.Command{ - cli.Command{ - Name: "sub", - Action: func(c *cli.Context) { - subcommandRun = true - }, - }, - } - - app.Flags = []cli.Flag{ - cli.StringFlag{Name: "opt"}, - } - - // run with the Before() func succeeding - err = app.Run([]string{"command", "--opt", "succeed", "sub"}) - - if err != nil { - t.Fatalf("Run error: %s", err) - } - - if beforeRun == false { - t.Errorf("Before() not executed when expected") - } - - if subcommandRun == false { - t.Errorf("Subcommand not executed when expected") - } - - // reset - beforeRun, subcommandRun = false, false - - // run with the Before() func failing - err = app.Run([]string{"command", "--opt", "fail", "sub"}) - - // should be the same error produced by the Before func - if err != beforeError { - t.Errorf("Run error expected, but not received") - } - - if beforeRun == false { - t.Errorf("Before() not executed when expected") - } - - if subcommandRun == true { - t.Errorf("Subcommand executed when NOT expected") - } - -} - -func TestAppHelpPrinter(t *testing.T) { - oldPrinter := cli.HelpPrinter - defer func() { - cli.HelpPrinter = oldPrinter - }() - - var wasCalled = false - cli.HelpPrinter = func(template string, data interface{}) { - wasCalled = true - } - - app := cli.NewApp() - app.Run([]string{"-h"}) - - if wasCalled == false { - t.Errorf("Help printer expected to be called, but was not") - } -} - -func TestAppVersionPrinter(t *testing.T) { - oldPrinter := cli.VersionPrinter - defer func() { - cli.VersionPrinter = oldPrinter - }() - - var wasCalled = false - cli.VersionPrinter = func(c *cli.Context) { - wasCalled = true - } - - app := cli.NewApp() - ctx := cli.NewContext(app, nil, nil) - cli.ShowVersion(ctx) - - if wasCalled == false { - t.Errorf("Version printer expected to be called, but was not") - } -} - -func TestAppCommandNotFound(t *testing.T) { - beforeRun, subcommandRun := false, false - app := cli.NewApp() - - app.CommandNotFound = func(c *cli.Context, command string) { - beforeRun = true - } - - app.Commands = []cli.Command{ - cli.Command{ - Name: "bar", - Action: func(c *cli.Context) { - subcommandRun = true - }, - }, - } - - app.Run([]string{"command", "foo"}) - - expect(t, beforeRun, true) - expect(t, subcommandRun, false) -} - -func TestGlobalFlagsInSubcommands(t *testing.T) { - subcommandRun := false - app := cli.NewApp() - - app.Flags = []cli.Flag{ - cli.BoolFlag{Name: "debug, d", Usage: "Enable debugging"}, - } - - app.Commands = []cli.Command{ - cli.Command{ - Name: "foo", - Subcommands: []cli.Command{ - { - Name: "bar", - Action: func(c *cli.Context) { - if c.GlobalBool("debug") { - subcommandRun = true - } - }, - }, - }, - }, - } - - app.Run([]string{"command", "-d", "foo", "bar"}) - - expect(t, subcommandRun, true) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete deleted file mode 100644 index 9b55dd990cb..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete +++ /dev/null @@ -1,13 +0,0 @@ -#! /bin/bash - -_cli_bash_autocomplete() { - local cur prev opts base - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) - return 0 - } - - complete -F _cli_bash_autocomplete $PROG \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete b/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete deleted file mode 100644 index 5430a18f957..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete +++ /dev/null @@ -1,5 +0,0 @@ -autoload -U compinit && compinit -autoload -U bashcompinit && bashcompinit - -script_dir=$(dirname $0) -source ${script_dir}/bash_autocomplete diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go b/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go deleted file mode 100644 index b7425458123..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/cli.go +++ /dev/null @@ -1,19 +0,0 @@ -// Package cli provides a minimal framework for creating and organizing command line -// Go applications. cli is designed to be easy to understand and write, the most simple -// cli application can be written as follows: -// func main() { -// cli.NewApp().Run(os.Args) -// } -// -// Of course this application does not do much, so let's make this an actual application: -// func main() { -// app := cli.NewApp() -// app.Name = "greet" -// app.Usage = "say a greeting" -// app.Action = func(c *cli.Context) { -// println("Greetings") -// } -// -// app.Run(os.Args) -// } -package cli diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go deleted file mode 100644 index 879a793dc22..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package cli_test - -import ( - "os" - - "github.com/codegangsta/cli" -) - -func Example() { - app := cli.NewApp() - app.Name = "todo" - app.Usage = "task list on the command line" - app.Commands = []cli.Command{ - { - Name: "add", - ShortName: "a", - Usage: "add a task to the list", - Action: func(c *cli.Context) { - println("added task: ", c.Args().First()) - }, - }, - { - Name: "complete", - ShortName: "c", - Usage: "complete a task on the list", - Action: func(c *cli.Context) { - println("completed task: ", c.Args().First()) - }, - }, - } - - app.Run(os.Args) -} - -func ExampleSubcommand() { - app := cli.NewApp() - app.Name = "say" - app.Commands = []cli.Command{ - { - Name: "hello", - ShortName: "hi", - Usage: "use it to see a description", - Description: "This is how we describe hello the function", - Subcommands: []cli.Command{ - { - Name: "english", - ShortName: "en", - Usage: "sends a greeting in english", - Description: "greets someone in english", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "name", - Value: "Bob", - Usage: "Name of the person to greet", - }, - }, - Action: func(c *cli.Context) { - println("Hello, ", c.String("name")) - }, - }, { - Name: "spanish", - ShortName: "sp", - Usage: "sends a greeting in spanish", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "surname", - Value: "Jones", - Usage: "Surname of the person to greet", - }, - }, - Action: func(c *cli.Context) { - println("Hola, ", c.String("surname")) - }, - }, { - Name: "french", - ShortName: "fr", - Usage: "sends a greeting in french", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "nickname", - Value: "Stevie", - Usage: "Nickname of the person to greet", - }, - }, - Action: func(c *cli.Context) { - println("Bonjour, ", c.String("nickname")) - }, - }, - }, - }, { - Name: "bye", - Usage: "says goodbye", - Action: func(c *cli.Context) { - println("bye") - }, - }, - } - - app.Run(os.Args) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command.go deleted file mode 100644 index 5622b38f75c..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/command.go +++ /dev/null @@ -1,144 +0,0 @@ -package cli - -import ( - "fmt" - "io/ioutil" - "strings" -) - -// Command is a subcommand for a cli.App. -type Command struct { - // The name of the command - Name string - // short name of the command. Typically one character - ShortName string - // A short description of the usage of this command - Usage string - // A longer explanation of how the command works - Description string - // The function to call when checking for bash command completions - BashComplete func(context *Context) - // An action to execute before any sub-subcommands are run, but after the context is ready - // If a non-nil error is returned, no sub-subcommands are run - Before func(context *Context) error - // The function to call when this command is invoked - Action func(context *Context) - // List of child commands - Subcommands []Command - // List of flags to parse - Flags []Flag - // Treat all flags as normal arguments if true - SkipFlagParsing bool - // Boolean to hide built-in help command - HideHelp bool -} - -// Invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c Command) Run(ctx *Context) error { - - if len(c.Subcommands) > 0 || c.Before != nil { - return c.startApp(ctx) - } - - if !c.HideHelp { - // append help to flags - c.Flags = append( - c.Flags, - HelpFlag, - ) - } - - if ctx.App.EnableBashCompletion { - c.Flags = append(c.Flags, BashCompletionFlag) - } - - set := flagSet(c.Name, c.Flags) - set.SetOutput(ioutil.Discard) - - firstFlagIndex := -1 - for index, arg := range ctx.Args() { - if strings.HasPrefix(arg, "-") { - firstFlagIndex = index - break - } - } - - var err error - if firstFlagIndex > -1 && !c.SkipFlagParsing { - args := ctx.Args() - regularArgs := args[1:firstFlagIndex] - flagArgs := args[firstFlagIndex:] - err = set.Parse(append(flagArgs, regularArgs...)) - } else { - err = set.Parse(ctx.Args().Tail()) - } - - if err != nil { - fmt.Printf("Incorrect Usage.\n\n") - ShowCommandHelp(ctx, c.Name) - fmt.Println("") - return err - } - - nerr := normalizeFlags(c.Flags, set) - if nerr != nil { - fmt.Println(nerr) - fmt.Println("") - ShowCommandHelp(ctx, c.Name) - fmt.Println("") - return nerr - } - context := NewContext(ctx.App, set, ctx.globalSet) - - if checkCommandCompletions(context, c.Name) { - return nil - } - - if checkCommandHelp(context, c.Name) { - return nil - } - context.Command = c - c.Action(context) - return nil -} - -// Returns true if Command.Name or Command.ShortName matches given name -func (c Command) HasName(name string) bool { - return c.Name == name || c.ShortName == name -} - -func (c Command) startApp(ctx *Context) error { - app := NewApp() - - // set the name and usage - app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) - if c.Description != "" { - app.Usage = c.Description - } else { - app.Usage = c.Usage - } - - // set CommandNotFound - app.CommandNotFound = ctx.App.CommandNotFound - - // set the flags and commands - app.Commands = c.Subcommands - app.Flags = c.Flags - app.HideHelp = c.HideHelp - - // bash completion - app.EnableBashCompletion = ctx.App.EnableBashCompletion - if c.BashComplete != nil { - app.BashComplete = c.BashComplete - } - - // set the actions - app.Before = c.Before - if c.Action != nil { - app.Action = c.Action - } else { - app.Action = helpSubcommand.Action - } - - return app.RunAsSubcommand(ctx) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go deleted file mode 100644 index c0f556ad242..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package cli_test - -import ( - "flag" - "testing" - - "github.com/codegangsta/cli" -) - -func TestCommandDoNotIgnoreFlags(t *testing.T) { - app := cli.NewApp() - set := flag.NewFlagSet("test", 0) - test := []string{"blah", "blah", "-break"} - set.Parse(test) - - c := cli.NewContext(app, set, set) - - command := cli.Command{ - Name: "test-cmd", - ShortName: "tc", - Usage: "this is for testing", - Description: "testing", - Action: func(_ *cli.Context) {}, - } - err := command.Run(c) - - expect(t, err.Error(), "flag provided but not defined: -break") -} - -func TestCommandIgnoreFlags(t *testing.T) { - app := cli.NewApp() - set := flag.NewFlagSet("test", 0) - test := []string{"blah", "blah"} - set.Parse(test) - - c := cli.NewContext(app, set, set) - - command := cli.Command{ - Name: "test-cmd", - ShortName: "tc", - Usage: "this is for testing", - Description: "testing", - Action: func(_ *cli.Context) {}, - SkipFlagParsing: true, - } - err := command.Run(c) - - expect(t, err, nil) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/context.go b/Godeps/_workspace/src/github.com/codegangsta/cli/context.go deleted file mode 100644 index c9f645b1890..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/context.go +++ /dev/null @@ -1,339 +0,0 @@ -package cli - -import ( - "errors" - "flag" - "strconv" - "strings" - "time" -) - -// Context is a type that is passed through to -// each Handler action in a cli application. Context -// can be used to retrieve context-specific Args and -// parsed command-line options. -type Context struct { - App *App - Command Command - flagSet *flag.FlagSet - globalSet *flag.FlagSet - setFlags map[string]bool - globalSetFlags map[string]bool -} - -// Creates a new context. For use in when invoking an App or Command action. -func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { - return &Context{App: app, flagSet: set, globalSet: globalSet} -} - -// Looks up the value of a local int flag, returns 0 if no int flag exists -func (c *Context) Int(name string) int { - return lookupInt(name, c.flagSet) -} - -// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists -func (c *Context) Duration(name string) time.Duration { - return lookupDuration(name, c.flagSet) -} - -// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists -func (c *Context) Float64(name string) float64 { - return lookupFloat64(name, c.flagSet) -} - -// Looks up the value of a local bool flag, returns false if no bool flag exists -func (c *Context) Bool(name string) bool { - return lookupBool(name, c.flagSet) -} - -// Looks up the value of a local boolT flag, returns false if no bool flag exists -func (c *Context) BoolT(name string) bool { - return lookupBoolT(name, c.flagSet) -} - -// Looks up the value of a local string flag, returns "" if no string flag exists -func (c *Context) String(name string) string { - return lookupString(name, c.flagSet) -} - -// Looks up the value of a local string slice flag, returns nil if no string slice flag exists -func (c *Context) StringSlice(name string) []string { - return lookupStringSlice(name, c.flagSet) -} - -// Looks up the value of a local int slice flag, returns nil if no int slice flag exists -func (c *Context) IntSlice(name string) []int { - return lookupIntSlice(name, c.flagSet) -} - -// Looks up the value of a local generic flag, returns nil if no generic flag exists -func (c *Context) Generic(name string) interface{} { - return lookupGeneric(name, c.flagSet) -} - -// Looks up the value of a global int flag, returns 0 if no int flag exists -func (c *Context) GlobalInt(name string) int { - return lookupInt(name, c.globalSet) -} - -// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists -func (c *Context) GlobalDuration(name string) time.Duration { - return lookupDuration(name, c.globalSet) -} - -// Looks up the value of a global bool flag, returns false if no bool flag exists -func (c *Context) GlobalBool(name string) bool { - return lookupBool(name, c.globalSet) -} - -// Looks up the value of a global string flag, returns "" if no string flag exists -func (c *Context) GlobalString(name string) string { - return lookupString(name, c.globalSet) -} - -// Looks up the value of a global string slice flag, returns nil if no string slice flag exists -func (c *Context) GlobalStringSlice(name string) []string { - return lookupStringSlice(name, c.globalSet) -} - -// Looks up the value of a global int slice flag, returns nil if no int slice flag exists -func (c *Context) GlobalIntSlice(name string) []int { - return lookupIntSlice(name, c.globalSet) -} - -// Looks up the value of a global generic flag, returns nil if no generic flag exists -func (c *Context) GlobalGeneric(name string) interface{} { - return lookupGeneric(name, c.globalSet) -} - -// Determines if the flag was actually set -func (c *Context) IsSet(name string) bool { - if c.setFlags == nil { - c.setFlags = make(map[string]bool) - c.flagSet.Visit(func(f *flag.Flag) { - c.setFlags[f.Name] = true - }) - } - return c.setFlags[name] == true -} - -// Determines if the global flag was actually set -func (c *Context) GlobalIsSet(name string) bool { - if c.globalSetFlags == nil { - c.globalSetFlags = make(map[string]bool) - c.globalSet.Visit(func(f *flag.Flag) { - c.globalSetFlags[f.Name] = true - }) - } - return c.globalSetFlags[name] == true -} - -// Returns a slice of flag names used in this context. -func (c *Context) FlagNames() (names []string) { - for _, flag := range c.Command.Flags { - name := strings.Split(flag.getName(), ",")[0] - if name == "help" { - continue - } - names = append(names, name) - } - return -} - -// Returns a slice of global flag names used by the app. -func (c *Context) GlobalFlagNames() (names []string) { - for _, flag := range c.App.Flags { - name := strings.Split(flag.getName(), ",")[0] - if name == "help" || name == "version" { - continue - } - names = append(names, name) - } - return -} - -type Args []string - -// Returns the command line arguments associated with the context. -func (c *Context) Args() Args { - args := Args(c.flagSet.Args()) - return args -} - -// Returns the nth argument, or else a blank string -func (a Args) Get(n int) string { - if len(a) > n { - return a[n] - } - return "" -} - -// Returns the first argument, or else a blank string -func (a Args) First() string { - return a.Get(0) -} - -// Return the rest of the arguments (not the first one) -// or else an empty string slice -func (a Args) Tail() []string { - if len(a) >= 2 { - return []string(a)[1:] - } - return []string{} -} - -// Checks if there are any arguments present -func (a Args) Present() bool { - return len(a) != 0 -} - -// Swaps arguments at the given indexes -func (a Args) Swap(from, to int) error { - if from >= len(a) || to >= len(a) { - return errors.New("index out of range") - } - a[from], a[to] = a[to], a[from] - return nil -} - -func lookupInt(name string, set *flag.FlagSet) int { - f := set.Lookup(name) - if f != nil { - val, err := strconv.Atoi(f.Value.String()) - if err != nil { - return 0 - } - return val - } - - return 0 -} - -func lookupDuration(name string, set *flag.FlagSet) time.Duration { - f := set.Lookup(name) - if f != nil { - val, err := time.ParseDuration(f.Value.String()) - if err == nil { - return val - } - } - - return 0 -} - -func lookupFloat64(name string, set *flag.FlagSet) float64 { - f := set.Lookup(name) - if f != nil { - val, err := strconv.ParseFloat(f.Value.String(), 64) - if err != nil { - return 0 - } - return val - } - - return 0 -} - -func lookupString(name string, set *flag.FlagSet) string { - f := set.Lookup(name) - if f != nil { - return f.Value.String() - } - - return "" -} - -func lookupStringSlice(name string, set *flag.FlagSet) []string { - f := set.Lookup(name) - if f != nil { - return (f.Value.(*StringSlice)).Value() - - } - - return nil -} - -func lookupIntSlice(name string, set *flag.FlagSet) []int { - f := set.Lookup(name) - if f != nil { - return (f.Value.(*IntSlice)).Value() - - } - - return nil -} - -func lookupGeneric(name string, set *flag.FlagSet) interface{} { - f := set.Lookup(name) - if f != nil { - return f.Value - } - return nil -} - -func lookupBool(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - val, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return false - } - return val - } - - return false -} - -func lookupBoolT(name string, set *flag.FlagSet) bool { - f := set.Lookup(name) - if f != nil { - val, err := strconv.ParseBool(f.Value.String()) - if err != nil { - return true - } - return val - } - - return false -} - -func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { - switch ff.Value.(type) { - case *StringSlice: - default: - set.Set(name, ff.Value.String()) - } -} - -func normalizeFlags(flags []Flag, set *flag.FlagSet) error { - visited := make(map[string]bool) - set.Visit(func(f *flag.Flag) { - visited[f.Name] = true - }) - for _, f := range flags { - parts := strings.Split(f.getName(), ",") - if len(parts) == 1 { - continue - } - var ff *flag.Flag - for _, name := range parts { - name = strings.Trim(name, " ") - if visited[name] { - if ff != nil { - return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) - } - ff = set.Lookup(name) - } - } - if ff == nil { - continue - } - for _, name := range parts { - name = strings.Trim(name, " ") - if !visited[name] { - copyFlag(name, ff, set) - } - } - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go deleted file mode 100644 index 7c9a4436fc8..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package cli_test - -import ( - "flag" - "testing" - "time" - - "github.com/codegangsta/cli" -) - -func TestNewContext(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Int("myflag", 12, "doc") - globalSet := flag.NewFlagSet("test", 0) - globalSet.Int("myflag", 42, "doc") - command := cli.Command{Name: "mycommand"} - c := cli.NewContext(nil, set, globalSet) - c.Command = command - expect(t, c.Int("myflag"), 12) - expect(t, c.GlobalInt("myflag"), 42) - expect(t, c.Command.Name, "mycommand") -} - -func TestContext_Int(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Int("myflag", 12, "doc") - c := cli.NewContext(nil, set, set) - expect(t, c.Int("myflag"), 12) -} - -func TestContext_Duration(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Duration("myflag", time.Duration(12*time.Second), "doc") - c := cli.NewContext(nil, set, set) - expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) -} - -func TestContext_String(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.String("myflag", "hello world", "doc") - c := cli.NewContext(nil, set, set) - expect(t, c.String("myflag"), "hello world") -} - -func TestContext_Bool(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - c := cli.NewContext(nil, set, set) - expect(t, c.Bool("myflag"), false) -} - -func TestContext_BoolT(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", true, "doc") - c := cli.NewContext(nil, set, set) - expect(t, c.BoolT("myflag"), true) -} - -func TestContext_Args(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - c := cli.NewContext(nil, set, set) - set.Parse([]string{"--myflag", "bat", "baz"}) - expect(t, len(c.Args()), 2) - expect(t, c.Bool("myflag"), true) -} - -func TestContext_IsSet(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - set.String("otherflag", "hello world", "doc") - globalSet := flag.NewFlagSet("test", 0) - globalSet.Bool("myflagGlobal", true, "doc") - c := cli.NewContext(nil, set, globalSet) - set.Parse([]string{"--myflag", "bat", "baz"}) - globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) - expect(t, c.IsSet("myflag"), true) - expect(t, c.IsSet("otherflag"), false) - expect(t, c.IsSet("bogusflag"), false) - expect(t, c.IsSet("myflagGlobal"), false) -} - -func TestContext_GlobalIsSet(t *testing.T) { - set := flag.NewFlagSet("test", 0) - set.Bool("myflag", false, "doc") - set.String("otherflag", "hello world", "doc") - globalSet := flag.NewFlagSet("test", 0) - globalSet.Bool("myflagGlobal", true, "doc") - globalSet.Bool("myflagGlobalUnset", true, "doc") - c := cli.NewContext(nil, set, globalSet) - set.Parse([]string{"--myflag", "bat", "baz"}) - globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) - expect(t, c.GlobalIsSet("myflag"), false) - expect(t, c.GlobalIsSet("otherflag"), false) - expect(t, c.GlobalIsSet("bogusflag"), false) - expect(t, c.GlobalIsSet("myflagGlobal"), true) - expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) - expect(t, c.GlobalIsSet("bogusGlobal"), false) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go b/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go deleted file mode 100644 index b30bca3019b..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/flag.go +++ /dev/null @@ -1,410 +0,0 @@ -package cli - -import ( - "flag" - "fmt" - "os" - "strconv" - "strings" - "time" -) - -// This flag enables bash-completion for all commands and subcommands -var BashCompletionFlag = BoolFlag{ - Name: "generate-bash-completion", -} - -// This flag prints the version for the application -var VersionFlag = BoolFlag{ - Name: "version, v", - Usage: "print the version", -} - -// This flag prints the help for all commands and subcommands -var HelpFlag = BoolFlag{ - Name: "help, h", - Usage: "show help", -} - -// Flag is a common interface related to parsing flags in cli. -// For more advanced flag parsing techniques, it is recomended that -// this interface be implemented. -type Flag interface { - fmt.Stringer - // Apply Flag settings to the given flag set - Apply(*flag.FlagSet) - getName() string -} - -func flagSet(name string, flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet(name, flag.ContinueOnError) - - for _, f := range flags { - f.Apply(set) - } - return set -} - -func eachName(longName string, fn func(string)) { - parts := strings.Split(longName, ",") - for _, name := range parts { - name = strings.Trim(name, " ") - fn(name) - } -} - -// Generic is a generic parseable type identified by a specific flag -type Generic interface { - Set(value string) error - String() string -} - -// GenericFlag is the flag type for types implementing Generic -type GenericFlag struct { - Name string - Value Generic - Usage string - EnvVar string -} - -func (f GenericFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s %v\t`%v` %s", prefixFor(f.Name), f.Name, f.Value, "-"+f.Name+" option -"+f.Name+" option", f.Usage)) -} - -func (f GenericFlag) Apply(set *flag.FlagSet) { - val := f.Value - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - val.Set(envVal) - } - } - - eachName(f.Name, func(name string) { - set.Var(f.Value, name, f.Usage) - }) -} - -func (f GenericFlag) getName() string { - return f.Name -} - -type StringSlice []string - -func (f *StringSlice) Set(value string) error { - *f = append(*f, value) - return nil -} - -func (f *StringSlice) String() string { - return fmt.Sprintf("%s", *f) -} - -func (f *StringSlice) Value() []string { - return *f -} - -type StringSliceFlag struct { - Name string - Value *StringSlice - Usage string - EnvVar string -} - -func (f StringSliceFlag) String() string { - firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") - pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) -} - -func (f StringSliceFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - newVal := &StringSlice{} - for _, s := range strings.Split(envVal, ",") { - newVal.Set(s) - } - f.Value = newVal - } - } - - eachName(f.Name, func(name string) { - set.Var(f.Value, name, f.Usage) - }) -} - -func (f StringSliceFlag) getName() string { - return f.Name -} - -type IntSlice []int - -func (f *IntSlice) Set(value string) error { - - tmp, err := strconv.Atoi(value) - if err != nil { - return err - } else { - *f = append(*f, tmp) - } - return nil -} - -func (f *IntSlice) String() string { - return fmt.Sprintf("%d", *f) -} - -func (f *IntSlice) Value() []int { - return *f -} - -type IntSliceFlag struct { - Name string - Value *IntSlice - Usage string - EnvVar string -} - -func (f IntSliceFlag) String() string { - firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ") - pref := prefixFor(firstName) - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage)) -} - -func (f IntSliceFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - newVal := &IntSlice{} - for _, s := range strings.Split(envVal, ",") { - err := newVal.Set(s) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - } - } - f.Value = newVal - } - } - - eachName(f.Name, func(name string) { - set.Var(f.Value, name, f.Usage) - }) -} - -func (f IntSliceFlag) getName() string { - return f.Name -} - -type BoolFlag struct { - Name string - Usage string - EnvVar string -} - -func (f BoolFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) -} - -func (f BoolFlag) Apply(set *flag.FlagSet) { - val := false - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - envValBool, err := strconv.ParseBool(envVal) - if err == nil { - val = envValBool - } - } - } - - eachName(f.Name, func(name string) { - set.Bool(name, val, f.Usage) - }) -} - -func (f BoolFlag) getName() string { - return f.Name -} - -type BoolTFlag struct { - Name string - Usage string - EnvVar string -} - -func (f BoolTFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage)) -} - -func (f BoolTFlag) Apply(set *flag.FlagSet) { - val := true - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - envValBool, err := strconv.ParseBool(envVal) - if err == nil { - val = envValBool - } - } - } - - eachName(f.Name, func(name string) { - set.Bool(name, val, f.Usage) - }) -} - -func (f BoolTFlag) getName() string { - return f.Name -} - -type StringFlag struct { - Name string - Value string - Usage string - EnvVar string -} - -func (f StringFlag) String() string { - var fmtString string - fmtString = "%s %v\t%v" - - if len(f.Value) > 0 { - fmtString = "%s '%v'\t%v" - } else { - fmtString = "%s %v\t%v" - } - - return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)) -} - -func (f StringFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - f.Value = envVal - } - } - - eachName(f.Name, func(name string) { - set.String(name, f.Value, f.Usage) - }) -} - -func (f StringFlag) getName() string { - return f.Name -} - -type IntFlag struct { - Name string - Value int - Usage string - EnvVar string -} - -func (f IntFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - -func (f IntFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - envValInt, err := strconv.ParseUint(envVal, 10, 64) - if err == nil { - f.Value = int(envValInt) - } - } - } - - eachName(f.Name, func(name string) { - set.Int(name, f.Value, f.Usage) - }) -} - -func (f IntFlag) getName() string { - return f.Name -} - -type DurationFlag struct { - Name string - Value time.Duration - Usage string - EnvVar string -} - -func (f DurationFlag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - -func (f DurationFlag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - envValDuration, err := time.ParseDuration(envVal) - if err == nil { - f.Value = envValDuration - } - } - } - - eachName(f.Name, func(name string) { - set.Duration(name, f.Value, f.Usage) - }) -} - -func (f DurationFlag) getName() string { - return f.Name -} - -type Float64Flag struct { - Name string - Value float64 - Usage string - EnvVar string -} - -func (f Float64Flag) String() string { - return withEnvHint(f.EnvVar, fmt.Sprintf("%s '%v'\t%v", prefixedNames(f.Name), f.Value, f.Usage)) -} - -func (f Float64Flag) Apply(set *flag.FlagSet) { - if f.EnvVar != "" { - if envVal := os.Getenv(f.EnvVar); envVal != "" { - envValFloat, err := strconv.ParseFloat(envVal, 10) - if err == nil { - f.Value = float64(envValFloat) - } - } - } - - eachName(f.Name, func(name string) { - set.Float64(name, f.Value, f.Usage) - }) -} - -func (f Float64Flag) getName() string { - return f.Name -} - -func prefixFor(name string) (prefix string) { - if len(name) == 1 { - prefix = "-" - } else { - prefix = "--" - } - - return -} - -func prefixedNames(fullName string) (prefixed string) { - parts := strings.Split(fullName, ",") - for i, name := range parts { - name = strings.Trim(name, " ") - prefixed += prefixFor(name) + name - if i < len(parts)-1 { - prefixed += ", " - } - } - return -} - -func withEnvHint(envVar, str string) string { - envText := "" - if envVar != "" { - envText = fmt.Sprintf(" [$%s]", envVar) - } - return str + envText -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go deleted file mode 100644 index bc5059ca17e..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/flag_test.go +++ /dev/null @@ -1,587 +0,0 @@ -package cli_test - -import ( - "fmt" - "os" - "reflect" - "strings" - "testing" - - "github.com/codegangsta/cli" -) - -var boolFlagTests = []struct { - name string - expected string -}{ - {"help", "--help\t"}, - {"h", "-h\t"}, -} - -func TestBoolFlagHelpOutput(t *testing.T) { - - for _, test := range boolFlagTests { - flag := cli.BoolFlag{Name: test.name} - output := flag.String() - - if output != test.expected { - t.Errorf("%s does not match %s", output, test.expected) - } - } -} - -var stringFlagTests = []struct { - name string - value string - expected string -}{ - {"help", "", "--help \t"}, - {"h", "", "-h \t"}, - {"h", "", "-h \t"}, - {"test", "Something", "--test 'Something'\t"}, -} - -func TestStringFlagHelpOutput(t *testing.T) { - - for _, test := range stringFlagTests { - flag := cli.StringFlag{Name: test.name, Value: test.value} - output := flag.String() - - if output != test.expected { - t.Errorf("%s does not match %s", output, test.expected) - } - } -} - -func TestStringFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_FOO", "derp") - for _, test := range stringFlagTests { - flag := cli.StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_FOO]") { - t.Errorf("%s does not end with [$APP_FOO]", output) - } - } -} - -var stringSliceFlagTests = []struct { - name string - value *cli.StringSlice - expected string -}{ - {"help", func() *cli.StringSlice { - s := &cli.StringSlice{} - s.Set("") - return s - }(), "--help '--help option --help option'\t"}, - {"h", func() *cli.StringSlice { - s := &cli.StringSlice{} - s.Set("") - return s - }(), "-h '-h option -h option'\t"}, - {"h", func() *cli.StringSlice { - s := &cli.StringSlice{} - s.Set("") - return s - }(), "-h '-h option -h option'\t"}, - {"test", func() *cli.StringSlice { - s := &cli.StringSlice{} - s.Set("Something") - return s - }(), "--test '--test option --test option'\t"}, -} - -func TestStringSliceFlagHelpOutput(t *testing.T) { - - for _, test := range stringSliceFlagTests { - flag := cli.StringSliceFlag{Name: test.name, Value: test.value} - output := flag.String() - - if output != test.expected { - t.Errorf("%q does not match %q", output, test.expected) - } - } -} - -func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_QWWX", "11,4") - for _, test := range stringSliceFlagTests { - flag := cli.StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_QWWX]") { - t.Errorf("%q does not end with [$APP_QWWX]", output) - } - } -} - -var intFlagTests = []struct { - name string - expected string -}{ - {"help", "--help '0'\t"}, - {"h", "-h '0'\t"}, -} - -func TestIntFlagHelpOutput(t *testing.T) { - - for _, test := range intFlagTests { - flag := cli.IntFlag{Name: test.name} - output := flag.String() - - if output != test.expected { - t.Errorf("%s does not match %s", output, test.expected) - } - } -} - -func TestIntFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_BAR", "2") - for _, test := range intFlagTests { - flag := cli.IntFlag{Name: test.name, EnvVar: "APP_BAR"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_BAR]") { - t.Errorf("%s does not end with [$APP_BAR]", output) - } - } -} - -var durationFlagTests = []struct { - name string - expected string -}{ - {"help", "--help '0'\t"}, - {"h", "-h '0'\t"}, -} - -func TestDurationFlagHelpOutput(t *testing.T) { - - for _, test := range durationFlagTests { - flag := cli.DurationFlag{Name: test.name} - output := flag.String() - - if output != test.expected { - t.Errorf("%s does not match %s", output, test.expected) - } - } -} - -func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_BAR", "2h3m6s") - for _, test := range durationFlagTests { - flag := cli.DurationFlag{Name: test.name, EnvVar: "APP_BAR"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_BAR]") { - t.Errorf("%s does not end with [$APP_BAR]", output) - } - } -} - -var intSliceFlagTests = []struct { - name string - value *cli.IntSlice - expected string -}{ - {"help", &cli.IntSlice{}, "--help '--help option --help option'\t"}, - {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, - {"h", &cli.IntSlice{}, "-h '-h option -h option'\t"}, - {"test", func() *cli.IntSlice { - i := &cli.IntSlice{} - i.Set("9") - return i - }(), "--test '--test option --test option'\t"}, -} - -func TestIntSliceFlagHelpOutput(t *testing.T) { - - for _, test := range intSliceFlagTests { - flag := cli.IntSliceFlag{Name: test.name, Value: test.value} - output := flag.String() - - if output != test.expected { - t.Errorf("%q does not match %q", output, test.expected) - } - } -} - -func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_SMURF", "42,3") - for _, test := range intSliceFlagTests { - flag := cli.IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_SMURF]") { - t.Errorf("%q does not end with [$APP_SMURF]", output) - } - } -} - -var float64FlagTests = []struct { - name string - expected string -}{ - {"help", "--help '0'\t"}, - {"h", "-h '0'\t"}, -} - -func TestFloat64FlagHelpOutput(t *testing.T) { - - for _, test := range float64FlagTests { - flag := cli.Float64Flag{Name: test.name} - output := flag.String() - - if output != test.expected { - t.Errorf("%s does not match %s", output, test.expected) - } - } -} - -func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_BAZ", "99.4") - for _, test := range float64FlagTests { - flag := cli.Float64Flag{Name: test.name, EnvVar: "APP_BAZ"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_BAZ]") { - t.Errorf("%s does not end with [$APP_BAZ]", output) - } - } -} - -var genericFlagTests = []struct { - name string - value cli.Generic - expected string -}{ - {"help", &Parser{}, "--help \t`-help option -help option` "}, - {"h", &Parser{}, "-h \t`-h option -h option` "}, - {"test", &Parser{}, "--test \t`-test option -test option` "}, -} - -func TestGenericFlagHelpOutput(t *testing.T) { - - for _, test := range genericFlagTests { - flag := cli.GenericFlag{Name: test.name} - output := flag.String() - - if output != test.expected { - t.Errorf("%q does not match %q", output, test.expected) - } - } -} - -func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) { - - os.Setenv("APP_ZAP", "3") - for _, test := range genericFlagTests { - flag := cli.GenericFlag{Name: test.name, EnvVar: "APP_ZAP"} - output := flag.String() - - if !strings.HasSuffix(output, " [$APP_ZAP]") { - t.Errorf("%s does not end with [$APP_ZAP]", output) - } - } -} - -func TestParseMultiString(t *testing.T) { - (&cli.App{ - Flags: []cli.Flag{ - cli.StringFlag{Name: "serve, s"}, - }, - Action: func(ctx *cli.Context) { - if ctx.String("serve") != "10" { - t.Errorf("main name not set") - } - if ctx.String("s") != "10" { - t.Errorf("short name not set") - } - }, - }).Run([]string{"run", "-s", "10"}) -} - -func TestParseMultiStringFromEnv(t *testing.T) { - os.Setenv("APP_COUNT", "20") - (&cli.App{ - Flags: []cli.Flag{ - cli.StringFlag{Name: "count, c", EnvVar: "APP_COUNT"}, - }, - Action: func(ctx *cli.Context) { - if ctx.String("count") != "20" { - t.Errorf("main name not set") - } - if ctx.String("c") != "20" { - t.Errorf("short name not set") - } - }, - }).Run([]string{"run"}) -} - -func TestParseMultiStringSlice(t *testing.T) { - (&cli.App{ - Flags: []cli.Flag{ - cli.StringSliceFlag{Name: "serve, s", Value: &cli.StringSlice{}}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.StringSlice("serve"), []string{"10", "20"}) { - t.Errorf("main name not set") - } - if !reflect.DeepEqual(ctx.StringSlice("s"), []string{"10", "20"}) { - t.Errorf("short name not set") - } - }, - }).Run([]string{"run", "-s", "10", "-s", "20"}) -} - -func TestParseMultiStringSliceFromEnv(t *testing.T) { - os.Setenv("APP_INTERVALS", "20,30,40") - - (&cli.App{ - Flags: []cli.Flag{ - cli.StringSliceFlag{Name: "intervals, i", Value: &cli.StringSlice{}, EnvVar: "APP_INTERVALS"}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.StringSlice("intervals"), []string{"20", "30", "40"}) { - t.Errorf("main name not set from env") - } - if !reflect.DeepEqual(ctx.StringSlice("i"), []string{"20", "30", "40"}) { - t.Errorf("short name not set from env") - } - }, - }).Run([]string{"run"}) -} - -func TestParseMultiInt(t *testing.T) { - a := cli.App{ - Flags: []cli.Flag{ - cli.IntFlag{Name: "serve, s"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Int("serve") != 10 { - t.Errorf("main name not set") - } - if ctx.Int("s") != 10 { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run", "-s", "10"}) -} - -func TestParseMultiIntFromEnv(t *testing.T) { - os.Setenv("APP_TIMEOUT_SECONDS", "10") - a := cli.App{ - Flags: []cli.Flag{ - cli.IntFlag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Int("timeout") != 10 { - t.Errorf("main name not set") - } - if ctx.Int("t") != 10 { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run"}) -} - -func TestParseMultiIntSlice(t *testing.T) { - (&cli.App{ - Flags: []cli.Flag{ - cli.IntSliceFlag{Name: "serve, s", Value: &cli.IntSlice{}}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.IntSlice("serve"), []int{10, 20}) { - t.Errorf("main name not set") - } - if !reflect.DeepEqual(ctx.IntSlice("s"), []int{10, 20}) { - t.Errorf("short name not set") - } - }, - }).Run([]string{"run", "-s", "10", "-s", "20"}) -} - -func TestParseMultiIntSliceFromEnv(t *testing.T) { - os.Setenv("APP_INTERVALS", "20,30,40") - - (&cli.App{ - Flags: []cli.Flag{ - cli.IntSliceFlag{Name: "intervals, i", Value: &cli.IntSlice{}, EnvVar: "APP_INTERVALS"}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.IntSlice("intervals"), []int{20, 30, 40}) { - t.Errorf("main name not set from env") - } - if !reflect.DeepEqual(ctx.IntSlice("i"), []int{20, 30, 40}) { - t.Errorf("short name not set from env") - } - }, - }).Run([]string{"run"}) -} - -func TestParseMultiFloat64(t *testing.T) { - a := cli.App{ - Flags: []cli.Flag{ - cli.Float64Flag{Name: "serve, s"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Float64("serve") != 10.2 { - t.Errorf("main name not set") - } - if ctx.Float64("s") != 10.2 { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run", "-s", "10.2"}) -} - -func TestParseMultiFloat64FromEnv(t *testing.T) { - os.Setenv("APP_TIMEOUT_SECONDS", "15.5") - a := cli.App{ - Flags: []cli.Flag{ - cli.Float64Flag{Name: "timeout, t", EnvVar: "APP_TIMEOUT_SECONDS"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Float64("timeout") != 15.5 { - t.Errorf("main name not set") - } - if ctx.Float64("t") != 15.5 { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run"}) -} - -func TestParseMultiBool(t *testing.T) { - a := cli.App{ - Flags: []cli.Flag{ - cli.BoolFlag{Name: "serve, s"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Bool("serve") != true { - t.Errorf("main name not set") - } - if ctx.Bool("s") != true { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run", "--serve"}) -} - -func TestParseMultiBoolFromEnv(t *testing.T) { - os.Setenv("APP_DEBUG", "1") - a := cli.App{ - Flags: []cli.Flag{ - cli.BoolFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, - }, - Action: func(ctx *cli.Context) { - if ctx.Bool("debug") != true { - t.Errorf("main name not set from env") - } - if ctx.Bool("d") != true { - t.Errorf("short name not set from env") - } - }, - } - a.Run([]string{"run"}) -} - -func TestParseMultiBoolT(t *testing.T) { - a := cli.App{ - Flags: []cli.Flag{ - cli.BoolTFlag{Name: "serve, s"}, - }, - Action: func(ctx *cli.Context) { - if ctx.BoolT("serve") != true { - t.Errorf("main name not set") - } - if ctx.BoolT("s") != true { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run", "--serve"}) -} - -func TestParseMultiBoolTFromEnv(t *testing.T) { - os.Setenv("APP_DEBUG", "0") - a := cli.App{ - Flags: []cli.Flag{ - cli.BoolTFlag{Name: "debug, d", EnvVar: "APP_DEBUG"}, - }, - Action: func(ctx *cli.Context) { - if ctx.BoolT("debug") != false { - t.Errorf("main name not set from env") - } - if ctx.BoolT("d") != false { - t.Errorf("short name not set from env") - } - }, - } - a.Run([]string{"run"}) -} - -type Parser [2]string - -func (p *Parser) Set(value string) error { - parts := strings.Split(value, ",") - if len(parts) != 2 { - return fmt.Errorf("invalid format") - } - - (*p)[0] = parts[0] - (*p)[1] = parts[1] - - return nil -} - -func (p *Parser) String() string { - return fmt.Sprintf("%s,%s", p[0], p[1]) -} - -func TestParseGeneric(t *testing.T) { - a := cli.App{ - Flags: []cli.Flag{ - cli.GenericFlag{Name: "serve, s", Value: &Parser{}}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"10", "20"}) { - t.Errorf("main name not set") - } - if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"10", "20"}) { - t.Errorf("short name not set") - } - }, - } - a.Run([]string{"run", "-s", "10,20"}) -} - -func TestParseGenericFromEnv(t *testing.T) { - os.Setenv("APP_SERVE", "20,30") - a := cli.App{ - Flags: []cli.Flag{ - cli.GenericFlag{Name: "serve, s", Value: &Parser{}, EnvVar: "APP_SERVE"}, - }, - Action: func(ctx *cli.Context) { - if !reflect.DeepEqual(ctx.Generic("serve"), &Parser{"20", "30"}) { - t.Errorf("main name not set from env") - } - if !reflect.DeepEqual(ctx.Generic("s"), &Parser{"20", "30"}) { - t.Errorf("short name not set from env") - } - }, - } - a.Run([]string{"run"}) -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go b/Godeps/_workspace/src/github.com/codegangsta/cli/help.go deleted file mode 100644 index 5667d3a8c59..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/help.go +++ /dev/null @@ -1,224 +0,0 @@ -package cli - -import ( - "fmt" - "os" - "text/tabwriter" - "text/template" -) - -// The text template for the Default help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var AppHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} - -USAGE: - {{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...] - -VERSION: - {{.Version}}{{if or .Author .Email}} - -AUTHOR:{{if .Author}} - {{.Author}}{{if .Email}} - <{{.Email}}>{{end}}{{else}} - {{.Email}}{{end}}{{end}} - -COMMANDS: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}}{{if .Flags}} -GLOBAL OPTIONS: - {{range .Flags}}{{.}} - {{end}}{{end}} -` - -// The text template for the command help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var CommandHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} - -USAGE: - command {{.Name}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} - -DESCRIPTION: - {{.Description}}{{end}}{{if .Flags}} - -OPTIONS: - {{range .Flags}}{{.}} - {{end}}{{ end }} -` - -// The text template for the subcommand help topic. -// cli.go uses text/template to render templates. You can -// render custom help text by setting this variable. -var SubcommandHelpTemplate = `NAME: - {{.Name}} - {{.Usage}} - -USAGE: - {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] - -COMMANDS: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}}{{if .Flags}} -OPTIONS: - {{range .Flags}}{{.}} - {{end}}{{end}} -` - -var helpCommand = Command{ - Name: "help", - ShortName: "h", - Usage: "Shows a list of commands or help for one command", - Action: func(c *Context) { - args := c.Args() - if args.Present() { - ShowCommandHelp(c, args.First()) - } else { - ShowAppHelp(c) - } - }, -} - -var helpSubcommand = Command{ - Name: "help", - ShortName: "h", - Usage: "Shows a list of commands or help for one command", - Action: func(c *Context) { - args := c.Args() - if args.Present() { - ShowCommandHelp(c, args.First()) - } else { - ShowSubcommandHelp(c) - } - }, -} - -// Prints help for the App -var HelpPrinter = printHelp - -// Prints version for the App -var VersionPrinter = printVersion - -func ShowAppHelp(c *Context) { - HelpPrinter(AppHelpTemplate, c.App) -} - -// Prints the list of subcommands as the default app completion method -func DefaultAppComplete(c *Context) { - for _, command := range c.App.Commands { - fmt.Println(command.Name) - if command.ShortName != "" { - fmt.Println(command.ShortName) - } - } -} - -// Prints help for the given command -func ShowCommandHelp(c *Context, command string) { - for _, c := range c.App.Commands { - if c.HasName(command) { - HelpPrinter(CommandHelpTemplate, c) - return - } - } - - if c.App.CommandNotFound != nil { - c.App.CommandNotFound(c, command) - } else { - fmt.Printf("No help topic for '%v'\n", command) - } -} - -// Prints help for the given subcommand -func ShowSubcommandHelp(c *Context) { - HelpPrinter(SubcommandHelpTemplate, c.App) -} - -// Prints the version number of the App -func ShowVersion(c *Context) { - VersionPrinter(c) -} - -func printVersion(c *Context) { - fmt.Printf("%v version %v\n", c.App.Name, c.App.Version) -} - -// Prints the lists of commands within a given context -func ShowCompletions(c *Context) { - a := c.App - if a != nil && a.BashComplete != nil { - a.BashComplete(c) - } -} - -// Prints the custom completions for a given command -func ShowCommandCompletions(ctx *Context, command string) { - c := ctx.App.Command(command) - if c != nil && c.BashComplete != nil { - c.BashComplete(ctx) - } -} - -func printHelp(templ string, data interface{}) { - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - t := template.Must(template.New("help").Parse(templ)) - err := t.Execute(w, data) - if err != nil { - panic(err) - } - w.Flush() -} - -func checkVersion(c *Context) bool { - if c.GlobalBool("version") { - ShowVersion(c) - return true - } - - return false -} - -func checkHelp(c *Context) bool { - if c.GlobalBool("h") || c.GlobalBool("help") { - ShowAppHelp(c) - return true - } - - return false -} - -func checkCommandHelp(c *Context, name string) bool { - if c.Bool("h") || c.Bool("help") { - ShowCommandHelp(c, name) - return true - } - - return false -} - -func checkSubcommandHelp(c *Context) bool { - if c.GlobalBool("h") || c.GlobalBool("help") { - ShowSubcommandHelp(c) - return true - } - - return false -} - -func checkCompletions(c *Context) bool { - if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { - ShowCompletions(c) - return true - } - - return false -} - -func checkCommandCompletions(c *Context, name string) bool { - if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { - ShowCommandCompletions(c, name) - return true - } - - return false -} diff --git a/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go b/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go deleted file mode 100644 index cdc4feb2fcd..00000000000 --- a/Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package cli_test - -import ( - "reflect" - "testing" -) - -/* Test Helpers */ -func expect(t *testing.T, a interface{}, b interface{}) { - if a != b { - t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} - -func refute(t *testing.T, a interface{}, b interface{}) { - if a == b { - t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) - } -} diff --git a/Godeps/_workspace/src/github.com/dalu/slug/README.md b/Godeps/_workspace/src/github.com/dalu/slug/README.md new file mode 100644 index 00000000000..ddefc36ff04 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/README.md @@ -0,0 +1,50 @@ +slug +==== + +Package `slug` generate slug from unicode string, URL-friendly slugify with +multiple languages support. + +[![GoDoc](https://godoc.org/github.com/dalu/slug?status.png)](https://godoc.org/github.com/dalu/slug) + +[Documentation online](http://godoc.org/github.com/dalu/slug) + +## Example + + package main + + import( + "github.com/gosimple/slug" + "fmt" + ) + + func main () { + text := slug.Make("Hellö Wörld хелло ворлд") + fmt.Println(text) // Will print hello-world-khello-vorld + + someText := slug.Make("影師") + fmt.Println(someText) // Will print: ying-shi + + enText := slug.MakeLang("This & that", "en") + fmt.Println(enText) // Will print 'this-and-that' + + deText := slug.MakeLang("Diese & Dass", "de") + fmt.Println(deText) // Will print 'diese-und-dass' + + slug.CustomSub = map[string]string{ + "water": "sand", + } + textSub := slug.Make("water is hot") + fmt.Println(textSub) // Will print 'sand-is-hot' + } + +## Installation + + go get -u github.com/dalu/slug + +## License + +The source files are distributed under the +[Mozilla Public License, version 2.0](http://mozilla.org/MPL/2.0/), +unless otherwise noted. +Please read the [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html) +if you have further questions regarding the license. diff --git a/Godeps/_workspace/src/github.com/dalu/slug/default_substitution.go b/Godeps/_workspace/src/github.com/dalu/slug/default_substitution.go new file mode 100644 index 00000000000..847c85d1fc8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/default_substitution.go @@ -0,0 +1,16 @@ +// Copyright 2013 by Dobrosław Żybort. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package slug + +var defaultSub = map[rune]string{ + '"': "", + '\'': "", + '’': "", + '‒': "-", // figure dash + '–': "-", // en dash + '—': "-", // em dash + '―': "-", // horizontal bar +} diff --git a/Godeps/_workspace/src/github.com/dalu/slug/doc.go b/Godeps/_workspace/src/github.com/dalu/slug/doc.go new file mode 100644 index 00000000000..39f57b30eb4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/doc.go @@ -0,0 +1,39 @@ +// Copyright 2013 by Dobrosław Żybort. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/* +Package slug generate slug from unicode string, URL-friendly slugify with +multiple languages support. + +Example: + + package main + + import( + "github.com/dalu/slug" + "fmt" + ) + + func main () { + text := slug.Make("Hellö Wörld хелло ворлд") + fmt.Println(text) // Will print hello-world-khello-vorld + + someText := slug.Make("影師") + fmt.Println(someText) // Will print: ying-shi + + enText := slug.MakeLang("This & that", "en") + fmt.Println(enText) // Will print 'this-and-that' + + deText := slug.MakeLang("Diese & Dass", "de") + fmt.Println(deText) // Will print 'diese-und-dass' + + slug.CustomSub = map[string]string{ + "water": "sand", + } + textSub := slug.Make("water is hot") + fmt.Println(textSub) // Will print 'sand-is-hot' + } +*/ +package slug diff --git a/Godeps/_workspace/src/github.com/dalu/slug/languages_substitution.go b/Godeps/_workspace/src/github.com/dalu/slug/languages_substitution.go new file mode 100644 index 00000000000..49ebb48dbf4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/languages_substitution.go @@ -0,0 +1,26 @@ +// Copyright 2013 by Dobrosław Żybort. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package slug + +var deSub = map[rune]string{ + '&': "und", + '@': "an", +} + +var enSub = map[rune]string{ + '&': "and", + '@': "at", +} + +var plSub = map[rune]string{ + '&': "i", + '@': "na", +} + +var esSub = map[rune]string{ + '&': "y", + '@': "en", +} diff --git a/Godeps/_workspace/src/github.com/dalu/slug/slug.go b/Godeps/_workspace/src/github.com/dalu/slug/slug.go new file mode 100644 index 00000000000..85d614f941e --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/slug.go @@ -0,0 +1,122 @@ +// Copyright 2013 by Dobrosław Żybort. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package slug + +import ( + "github.com/dalu/unidecode" + "regexp" + "strings" +) + +var ( + // Custom substitution map + CustomSub map[string]string + // Custom rune substitution map + CustomRuneSub map[rune]string + + // Maximum slug length. It's smart so it will cat slug after full word. + // By default slugs aren't shortened. + // If MaxLength is smaller than length of the first word, then returned + // slug will contain only substring from the first word truncated + // after MaxLength. + MaxLength int +) + +//============================================================================= + +// Make returns slug generated from provided string. Will use "en" as language +// substitution. +func Make(s string) (slug string) { + return MakeLang(s, "en") +} + +// MakeLang returns slug generated from provided string and will use provided +// language for chars substitution. +func MakeLang(s string, lang string) (slug string) { + slug = strings.TrimSpace(s) + + // Custom substitutions + // Always substitute runes first + slug = SubstituteRune(slug, CustomRuneSub) + slug = Substitute(slug, CustomSub) + + // Process string with selected substitution language + switch lang { + case "de": + slug = SubstituteRune(slug, deSub) + case "en": + slug = SubstituteRune(slug, enSub) + case "pl": + slug = SubstituteRune(slug, plSub) + case "es": + slug = SubstituteRune(slug, esSub) + default: // fallback to "en" if lang not found + slug = SubstituteRune(slug, enSub) + } + + slug = SubstituteRune(slug, defaultSub) + + // Process all non ASCII symbols + slug = unidecode.Unidecode(slug) + + slug = strings.ToLower(slug) + + // Process all remaining symbols + slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-") + slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-") + slug = strings.Trim(slug, "-") + + if MaxLength > 0 { + slug = smartTruncate(slug) + } + + return slug +} + +// Substitute returns string with superseded all substrings from +// provided substitution map. +func Substitute(s string, sub map[string]string) (buf string) { + buf = s + for key, val := range sub { + buf = strings.Replace(s, key, val, -1) + } + return +} + +// SubstituteRune substitutes string chars with provided rune +// substitution map. +func SubstituteRune(s string, sub map[rune]string) (buf string) { + for _, c := range s { + if d, ok := sub[c]; ok { + buf += d + } else { + buf += string(c) + } + } + return +} + +func smartTruncate(text string) string { + if len(text) < MaxLength { + return text + } + + var truncated string + words := strings.SplitAfter(text, "-") + // If MaxLength is smaller than length of the first word return word + // truncated after MaxLength. + if len(words[0]) > MaxLength { + return words[0][:MaxLength] + } + for _, word := range words { + if len(truncated)+len(word)-1 <= MaxLength { + truncated = truncated + word + } else { + break + } + } + return strings.Trim(truncated, "-") +} diff --git a/Godeps/_workspace/src/github.com/dalu/slug/slug_test.go b/Godeps/_workspace/src/github.com/dalu/slug/slug_test.go new file mode 100644 index 00000000000..b996c3686b8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/slug/slug_test.go @@ -0,0 +1,337 @@ +// Copyright 2013 by Dobrosław Żybort. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package slug + +import ( + "testing" +) + +//============================================================================= + +func TestSlugMake(t *testing.T) { + var testCases = []struct { + in string + want string + }{ + {"DOBROSLAWZYBORT", "dobroslawzybort"}, + {"Dobroslaw Zybort", "dobroslaw-zybort"}, + {" Dobroslaw Zybort ?", "dobroslaw-zybort"}, + {"Dobrosław Żybort", "dobroslaw-zybort"}, + {"Ala ma 6 kotów.", "ala-ma-6-kotow"}, + + {"áÁàÀãÃâÂäÄąĄą̊Ą̊", "aaaaaaaaaaaaaa"}, + {"ćĆĉĈçÇ", "cccccc"}, + {"éÉèÈẽẼêÊëËęĘ", "eeeeeeeeeeee"}, + {"íÍìÌĩĨîÎïÏįĮ", "iiiiiiiiiiii"}, + {"łŁ", "ll"}, + {"ńŃ", "nn"}, + {"óÓòÒõÕôÔöÖǫǪǭǬø", "ooooooooooooooo"}, + {"śŚ", "ss"}, + {"úÚùÙũŨûÛüÜųŲ", "uuuuuuuuuuuu"}, + {"y̨Y̨", "yy"}, + {"źŹżŹ", "zzzz"}, + {"·/,:;`˜'\"", ""}, + {"2000–2013", "2000-2013"}, + {"style—not", "style-not"}, + {"test_slug", "test_slug"}, + {"Æ", "ae"}, + {"Ich heiße", "ich-heisse"}, + + {"This & that", "this-and-that"}, + {"fácil €", "facil-eu"}, + {"smile ☺", "smile"}, + {"Hellö Wörld хелло ворлд", "hello-world-khello-vorld"}, + {"\"C'est déjà l’été.\"", "cest-deja-lete"}, + {"jaja---lol-méméméoo--a", "jaja-lol-mememeoo-a"}, + {"影師", "ying-shi"}, + } + + for index, st := range testCases { + got := Make(st.in) + if got != st.want { + t.Errorf( + "%d. Make(%#v) = %#v; want %#v", + index, st.in, got, st.want) + } + } +} + +func TestSlugMakeLang(t *testing.T) { + var testCases = []struct { + lang string + in string + want string + }{ + {"en", "This & that", "this-and-that"}, + {"de", "This & that", "this-und-that"}, + {"pl", "This & that", "this-i-that"}, + {"es", "This & that", "this-y-that"}, + {"test", "This & that", "this-and-that"}, // unknown lang, fallback to "en" + } + + for index, smlt := range testCases { + got := MakeLang(smlt.in, smlt.lang) + if got != smlt.want { + t.Errorf( + "%d. MakeLang(%#v, %#v) = %#v; want %#v", + index, smlt.in, smlt.lang, got, smlt.want) + } + } +} + +func TestSlugMakeUserSubstituteLang(t *testing.T) { + var testCases = []struct { + cSub map[string]string + lang string + in string + want string + }{ + {map[string]string{"'": " "}, "en", "That's great", "that-s-great"}, + {map[string]string{"&": "or"}, "en", "This & that", "this-or-that"}, // by default "&" => "and" + {map[string]string{"&": "or"}, "de", "This & that", "this-or-that"}, // by default "&" => "und" + } + + for index, smust := range testCases { + CustomSub = smust.cSub + got := MakeLang(smust.in, smust.lang) + if got != smust.want { + t.Errorf( + "%d. %#v; MakeLang(%#v, %#v) = %#v; want %#v", + index, smust.cSub, smust.in, smust.lang, + got, smust.want) + + } + } +} + +func TestSlugMakeSubstituteOrderLang(t *testing.T) { + // Always substitute runes first + var testCases = []struct { + rSub map[rune]string + sSub map[string]string + in string + want string + }{ + {map[rune]string{'o': "left"}, map[string]string{"o": "right"}, "o o", "left-left"}, + {map[rune]string{'&': "down"}, map[string]string{"&": "up"}, "&", "down"}, + } + + for index, smsot := range testCases { + CustomRuneSub = smsot.rSub + CustomSub = smsot.sSub + got := Make(smsot.in) + if got != smsot.want { + t.Errorf( + "%d. %#v; %#v; Make(%#v) = %#v; want %#v", + index, smsot.rSub, smsot.sSub, smsot.in, + got, smsot.want) + + } + } +} + +func TestSubstituteLang(t *testing.T) { + var testCases = []struct { + cSub map[string]string + in string + want string + }{ + {map[string]string{"o": "no"}, "o o o", "no no no"}, + {map[string]string{"'": " "}, "That's great", "That s great"}, + } + + for index, sst := range testCases { + got := Substitute(sst.in, sst.cSub) + if got != sst.want { + t.Errorf( + "%d. Substitute(%#v, %#v) = %#v; want %#v", + index, sst.in, sst.cSub, got, sst.want) + } + } +} + +func TestSubstituteRuneLang(t *testing.T) { + var testCases = []struct { + cSub map[rune]string + in string + want string + }{ + {map[rune]string{'o': "no"}, "o o o", "no no no"}, + {map[rune]string{'\'': " "}, "That's great", "That s great"}, + } + + for index, ssrt := range testCases { + got := SubstituteRune(ssrt.in, ssrt.cSub) + if got != ssrt.want { + t.Errorf( + "%d. SubstituteRune(%#v, %#v) = %#v; want %#v", + index, ssrt.in, ssrt.cSub, got, ssrt.want) + } + } +} + +func TestSlugMakeSmartTruncate(t *testing.T) { + var testCases = []struct { + in string + maxLength int + want string + }{ + {"DOBROSLAWZYBORT", 100, "dobroslawzybort"}, + {"Dobroslaw Zybort", 100, "dobroslaw-zybort"}, + {"Dobroslaw Zybort", 12, "dobroslaw"}, + {" Dobroslaw Zybort ?", 12, "dobroslaw"}, + {"Ala ma 6 kotów.", 10, "ala-ma-6"}, + {"Dobrosław Żybort", 5, "dobro"}, + } + + for index, smstt := range testCases { + MaxLength = smstt.maxLength + got := Make(smstt.in) + if got != smstt.want { + t.Errorf( + "%d. MaxLength = %v; Make(%#v) = %#v; want %#v", + index, smstt.maxLength, smstt.in, got, smstt.want) + } + } +} + +func BenchmarkMakeShortAscii(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + Make("Hello world") + } +} +func BenchmarkMakeShort(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + Make("хелло ворлд") + } +} + +func BenchmarkMakeShortSymbols(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + Make("·/,:;`˜'\" &€£¥") + } +} + +func BenchmarkMakeMediumAscii(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + Make("ABCDE FGHIJ KLMNO PQRST UWXYZ ABCDE FGHIJ KLMNO PQRST UWXYZ ABCDE") + } +} + +func BenchmarkMakeMedium(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + Make("ヲァィゥェ ォャュョッ ーアイウエ オカキクケ コサシスセ ソタチツテ トナニヌネ ノハヒフヘ ホマミムメ モヤユヨラ リルレロワ") + } +} + +func BenchmarkMakeLongAscii(b *testing.B) { + longStr := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi " + + "pulvinar sodales ultrices. Nulla facilisi. Sed at vestibulum erat. Ut " + + "sit amet urna posuere, sagittis eros ac, varius nisi. Morbi ullamcorper " + + "odio at nunc pulvinar mattis. Vestibulum rutrum, ante eu dictum mattis, " + + "elit risus finibus nunc, consectetur facilisis eros leo ut sapien. Sed " + + "pulvinar volutpat mi. Cras semper mi ac eros accumsan, at feugiat massa " + + "elementum. Morbi eget dolor sit amet purus condimentum egestas non ut " + + "sapien. Duis feugiat magna vitae nisi lobortis, quis finibus sem " + + "sollicitudin. Pellentesque eleifend blandit ipsum, ut porta arcu " + + "ultricies et. Fusce vel ipsum porta, placerat diam ac, consectetur " + + "magna. Nulla in porta sem. Suspendisse commodo, felis in molestie " + + "ultricies, arcu ipsum aliquet turpis, elementum dapibus ipsum lorem a " + + "nisl. Etiam varius imperdiet placerat. Aliquam euismod lacus arcu, " + + "ultrices hendrerit est pellentesque vel. Aliquam sit amet laoreet leo. " + + "Integer eros libero, mollis sed posuere." + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + Make(longStr) + } +} + +func BenchmarkSubstituteRuneShort(b *testing.B) { + shortStr := "Hello/Hi world" + subs := map[rune]string{'o': "no", '/': "slash"} + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + SubstituteRune(shortStr, subs) + } +} + +func BenchmarkSubstituteRuneLong(b *testing.B) { + longStr := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi " + + "pulvinar sodales ultrices. Nulla facilisi. Sed at vestibulum erat. Ut " + + "sit amet urna posuere, sagittis eros ac, varius nisi. Morbi ullamcorper " + + "odio at nunc pulvinar mattis. Vestibulum rutrum, ante eu dictum mattis, " + + "elit risus finibus nunc, consectetur facilisis eros leo ut sapien. Sed " + + "pulvinar volutpat mi. Cras semper mi ac eros accumsan, at feugiat massa " + + "elementum. Morbi eget dolor sit amet purus condimentum egestas non ut " + + "sapien. Duis feugiat magna vitae nisi lobortis, quis finibus sem " + + "sollicitudin. Pellentesque eleifend blandit ipsum, ut porta arcu " + + "ultricies et. Fusce vel ipsum porta, placerat diam ac, consectetur " + + "magna. Nulla in porta sem. Suspendisse commodo, felis in molestie " + + "ultricies, arcu ipsum aliquet turpis, elementum dapibus ipsum lorem a " + + "nisl. Etiam varius imperdiet placerat. Aliquam euismod lacus arcu, " + + "ultrices hendrerit est pellentesque vel. Aliquam sit amet laoreet leo. " + + "Integer eros libero, mollis sed posuere." + subs := map[rune]string{ + 'o': "no", + '/': "slash", + 'i': "done", + 'E': "es", + 'a': "ASD", + '1': "one", + 'l': "onetwo", + } + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + SubstituteRune(longStr, subs) + } +} + +func BenchmarkSmartTruncateShort(b *testing.B) { + shortStr := "Hello-world" + MaxLength = 8 + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + smartTruncate(shortStr) + } +} + +func BenchmarkSmartTruncateLong(b *testing.B) { + longStr := "Lorem-ipsum-dolor-sit-amet,-consectetur-adipiscing-elit.-Morbi-" + + "pulvinar-sodales-ultrices.-Nulla-facilisi.-Sed-at-vestibulum-erat.-Ut-" + + "sit-amet-urna-posuere,-sagittis-eros-ac,-varius-nisi.-Morbi-ullamcorper-" + + "odio-at-nunc-pulvinar-mattis.-Vestibulum-rutrum,-ante-eu-dictum-mattis,-" + + "elit-risus-finibus-nunc,-consectetur-facilisis-eros-leo-ut-sapien.-Sed-" + + "pulvinar-volutpat-mi.-Cras-semper-mi-ac-eros-accumsan,-at-feugiat-massa-" + + "elementum.-Morbi-eget-dolor-sit-amet-purus-condimentum-egestas-non-ut-" + + "sapien.-Duis-feugiat-magna-vitae-nisi-lobortis,-quis-finibus-sem-" + + "sollicitudin.-Pellentesque-eleifend-blandit-ipsum,-ut-porta-arcu-" + + "ultricies-et.-Fusce-vel-ipsum-porta,-placerat-diam-ac,-consectetur-" + + "magna.-Nulla-in-porta-sem.-Suspendisse-commodo,-felis-in-molestie-" + + "ultricies,-arcu-ipsum-aliquet-turpis,-elementum-dapibus-ipsum-lorem-a-" + + "nisl.-Etiam-varius-imperdiet-placerat.-Aliquam-euismod-lacus-arcu,-" + + "ultrices-hendrerit-est-pellentesque-vel.-Aliquam-sit-amet-laoreet-leo.-" + + "Integer-eros-libero,-mollis-sed-posuere." + MaxLength = 256 + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + smartTruncate(longStr) + } +} diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/.gitignore b/Godeps/_workspace/src/github.com/dalu/unidecode/.gitignore new file mode 100644 index 00000000000..836562412fe --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/LICENSE b/Godeps/_workspace/src/github.com/dalu/unidecode/LICENSE new file mode 100644 index 00000000000..ad410e11302 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/README.md b/Godeps/_workspace/src/github.com/dalu/unidecode/README.md new file mode 100644 index 00000000000..589d955593c --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/README.md @@ -0,0 +1,6 @@ +unidecode +========= + +Unicode transliterator in Golang - Replaces non-ASCII characters with their ASCII approximations. + +View other available versions, documentation and examples at http://gopkgs.com/unidecode diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/decode.go b/Godeps/_workspace/src/github.com/dalu/unidecode/decode.go new file mode 100644 index 00000000000..028533bf425 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/decode.go @@ -0,0 +1,44 @@ +package unidecode + +import ( + "compress/zlib" + "encoding/binary" + "io" + "strings" + "sync" +) + +var ( + decoded = false + mutex sync.Mutex + transliterations [65536][]rune + transCount = rune(len(transliterations)) + getUint16 = binary.LittleEndian.Uint16 +) + +func decodeTransliterations() { + r, err := zlib.NewReader(strings.NewReader(tableData)) + if err != nil { + panic(err) + } + defer r.Close() + tmp1 := make([]byte, 2) + tmp2 := tmp1[:1] + for { + if _, err := io.ReadAtLeast(r, tmp1, 2); err != nil { + if err == io.EOF { + break + } + panic(err) + } + chr := getUint16(tmp1) + if _, err := io.ReadAtLeast(r, tmp2, 1); err != nil { + panic(err) + } + b := make([]byte, int(tmp2[0])) + if _, err := io.ReadFull(r, b); err != nil { + panic(err) + } + transliterations[int(chr)] = []rune(string(b)) + } +} diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/make_table.go b/Godeps/_workspace/src/github.com/dalu/unidecode/make_table.go new file mode 100644 index 00000000000..028b62e7b39 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/make_table.go @@ -0,0 +1,71 @@ +// +build none + +package main + +import ( + "bytes" + "compress/zlib" + "encoding/binary" + "fmt" + "go/format" + "io/ioutil" + "strconv" + "strings" +) + +func main() { + data, err := ioutil.ReadFile("table.txt") + if err != nil { + panic(err) + } + var buf bytes.Buffer + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, "/*") || line == "" { + continue + } + sep := strings.IndexByte(line, ':') + if sep == -1 { + panic(line) + } + val, err := strconv.ParseInt(line[:sep], 0, 32) + if err != nil { + panic(err) + } + s, err := strconv.Unquote(line[sep+2:]) + if err != nil { + panic(err) + } + if s == "" { + continue + } + if err := binary.Write(&buf, binary.LittleEndian, uint16(val)); err != nil { + panic(err) + } + if err := binary.Write(&buf, binary.LittleEndian, uint8(len(s))); err != nil { + panic(err) + } + buf.WriteString(s) + } + var cbuf bytes.Buffer + w, err := zlib.NewWriterLevel(&cbuf, zlib.BestCompression) + if err != nil { + panic(err) + } + if _, err := w.Write(buf.Bytes()); err != nil { + panic(err) + } + if err := w.Close(); err != nil { + panic(err) + } + buf.Reset() + buf.WriteString("package unidecode\n") + buf.WriteString("// AUTOGENERATED - DO NOT EDIT!\n\n") + fmt.Fprintf(&buf, "var tableData = %q;\n", cbuf.String()) + dst, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + if err := ioutil.WriteFile("table.go", dst, 0644); err != nil { + panic(err) + } +} diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/table.go b/Godeps/_workspace/src/github.com/dalu/unidecode/table.go new file mode 100644 index 00000000000..b7e41fa70ff --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/table.go @@ -0,0 +1,5 @@ +package unidecode + +// AUTOGENERATED - DO NOT EDIT! + +var tableData = "x\xda,\x9c\x05\\\x1cW\xd7\xc6\xf7\xcc\xec.]\x96e\x17[\xa8\x84\x92ThB\x02qwwwe\x80\t;\xd8\x00\xcbB ^\xb7\xd4ݛ\xba\xb7\x91\xb6\x91\xba[jI=\xa9\xbb\xc4]\xde\xef9\x0f\xdf\xef\xfd\x9d\xff\xb9\xf7̝;w\xce}\xe6\xec얼\x1e\x8f\xe8\xff\xc4\xf0\x88az\xc4\xf4z\xc4\xeb\xf3\x88\xcf\xef\x11\u007f\x92G\x92N\xf3\xc8i\x01\x8f\x04\x92=\x92\x1c\xf4H0\xc5#)!\x8f\x84R=\x92\x1a\xf6H8\xe2\x91H\x9aG\xd2\xd2=\x92\x9eᑌL\x8fdfy$+\xea\x91h\xb6G\xb2s<\x92s\xbaGN?\xc3#g\x9c\xe9\x913\xcf\xf2\xc8Ym<\xd2&\xd7#\xb9g{\xe4\xec<\x8f\xe4\xb5\xf5H\xdbv\x1eiw\x8eG\xce9\xd7#\xe7\x9e\xe7\x91\xf3\xce\xf7\xc8\xf9\xf9\x1eɿ\xc0#\x17\xb4\xf7H\xfb\x0e\x1e\xe9P\xe0\x91\x82\x8e\x1e\xe9\xd8\xc9#\x9d\n=RX䑢\xce\x1e\xe9\xdc\xc5#]\xbaz\xa4k7\x8ft\xeb\xee\x91\xee=<ң\xa7Gz\xf6\xf2H\xaf\xde\x1e\xe9\xdd\xc7#}\xfaz\xa4o?\x8f\xf4\xeb\xef\x91\xfe\x03<2`\xa0G\x06\x0e\xf2Ƞ\xc1\x1e\x19<\xc4#C\x86zd\xe80\x8f\f\x1b\xee\x91\xe1#<2b\xa4GF\x8e\xf2Ȩ\xd1\x1e\x19=\xc6#c\xc6zd\xec8\x8f\x8c\x1b\xef\x91\xf1\x13<2a\xa2G&N\xf2Ȥ\xc9\x1e\x99<\xc5#S\xa6zd\xea4\x8fL\x9b\xee\x91\xe93<2c\xa6Gf\xce\xf2Ȭ\xd9\x1e\x99=\xc7#s\xe6zd\xee<\x8f,\x98\xef\x91\xf9\v\xe0\x17zd\xe1\"\x8f,*\xf6H\xb1\xe5\x11\xab\xc4#%\xa5\x1e)-\xf3H\x99\xed\x11{\xb1G\x16\x97{\xa4<摘\xe3\x11\xa7\xc2#\x15\x95\x1e\xa9\xac\xf2HU\xb5G\xaak\x87m\x87\xed\x80}\x01\xfb\x12\xc9\xf8\n\xfekl\xd07\xb0oa\xdf\xc1vb\x83vy\x8c\xe9\xb1\xef=F<\xfe\x03\xee\xf1G\xd8O\xb0\x9fa\xbf\xc0~\x85\xfd\xe61,\xfbw\xec\xd1\x1f؟?a\u007f\xc1\xfe\x86\xfd\x83\xfd\xf9\x17\xf6\x1fl7l\x0f\xf6p/\xf6g\x1fnu?\xec\x00\xec \xec\x10\xec04{\x04\xfe(Rs\fv\x1cv\x02v\x12{t\xcac4\xc4\xfe\x87\x06\x9e\xc7!\"b\x19\xf0&\xbc\x17\xde\a\xef\x17\x19\x96$Rz\x1a|\x00>\x19>\b\x9f\x02\x1f\x82O\x15\x19\x1e\x16)\x8b\xc0\xa7\xc1\xa7\x8b\x8c\xc8\x10\xb13\xe1\xb3\xe0\xa3\xf0\xd9\xf09\xf0\xa7ß\x01\u007f&\xfcY\"\xa3ڈ\x94\xe7\u009f\r\x9f\a\xdf\x16\xbe\x1d\xfc9\xf0犌>O$v>|>\xfc\x05\"cڋ8\x1d\xe0\v\xe0;\xc2w\x82/\x84/\x82\xef\f\xdf\x05\xbe\xab\x18c\xc6v\x13é\xe8.2\xb6\x87HEO\x91q\xbdD*{\xc3\xfa\x88\x8c\xef+R\xd5\x0f\xbe?\xfc\x00\xf8\x81\xf0\x83\xe0\a\xc3#\x05\xe3\x87\xc2\x0f\x13\x998\\\xa4f\x04\xfcH\xf8Q\xf0\xa3\xe1Lj\x91_3V\x8c\x9a\xf2qbL\x1c5^d\xd2\x04\x11w\"\xfc$\xf8\xc9\xf0S\u09ca1i\xc441\\{\xba\xc8\xd4\x19\"\xf53\xe1g\xc1φ\x9f\x03?Wd\xda<\x91\xf8|\xf8\x05\xf0\v\xe1\x17\xc1\x17\xc3[\xf0%\"\xd3KE\x1a\xca\xe0m\xf8\xc5\xf0\xe5\xf01\x91\x19\x8eH\xa2\x02\xbe\x12\xbe\n\xbe\x1a\xbe\x06ޅ\xaf\x85\xaf\x83\xaf\x87\x8f\xc37\x88\xccJ\x8845\x8a\xcci\x12i^\x02\xdf,2\xb7E\xf0l\xc2/\x83_\x0e\xbf\x02~%\xae\xbbJ\xa4d\xb5\xc8\xd0\va\x17\xa1}\xb1H\xcfK`\x97\xe2\xce.Ö_\x8e-\xbf\x02[}%\xec*\xd8\xd5\xd8\xf25\xb0kD\xba]+2\xf8:l\xef\xf5\"#o\x10Y|#\xb6\xf2&\xd8\xcdb\xc4\x1ao\xc1\xfe\xdc\n\xbb\r\xbbq;v\xe2\x0ed\xf9N\xd8]X\xe0\xdd\xc8\xee=\xc8\uef78\xc8}\xb0\xfb\x91µH\xe1\x98\a\x90B\xe7A\x91\xc9\x0f\x89\xd4>,Ɯ\xa9\x8f\x88t}\x14\xf6\x98\x18\xd3F?.F<\xf6\x04\xb2\xf2$\xb2\xf3\x14\xfc\xd3\xf0\xcf\xe0Οŝ?\x87;]'2s=\xfc\x06\xdc\xf9F\xdc\xe5\xf3\xb8\xcb\x17Ę;\xfaE\xc5&1Zb\x9b\x15[0\xe3V\x91\x1e/\xc1^\x16\xa3!\xfe\nR\xf6\xaaȲ\xd7\xc4X\xb6\xecu`\xc0\x1b\"m\xdf\x14c\xf8ܷ\x80\x96\xb7\xc5(kyG\x8c\xf1c\xdf\x05*\xde\x13\xa3\xaa\xe2}(b\xec\a@Ň\x10H\xc5Gx\x80\xb6\xe1\x01\xfa\x187\xfd\t\x84\xf9)n\xec3\xdc\xd8\xe7X\xdfv\xaco\a\xfc\x17\xf0_\xc2\u007f\x05\xff5\xfc7\xf0\xdf\xc2\u007f\a\xbf\x13\xd9܅9\xbe\xc7\x1c?\xc0\xff\b\xff\x93\xa0\"\xfd,\xa8\t\xbf \xaf\xbf\xe21\xf9\r\xfew\xf8?\x90\xd6?\x91ֿp\x95\xbfq\x95\u007f\xe0\xff\x85\xffOot\xb7\xde\xe3\x1e<\n{\xf5\x06\xf6\xe9\r\xec\xd7\x1b8\x80\x93\x0f\xe2\xe4Cb\x8c\x9ey\x18\x1bq\x04\x1bq\x14\x1bq\f\xd7;\x8e\xeb\x9d\xd0\xeb\x9d\xd4\xeb\x9d\xc2|\xff\xc3|\x1e\x03e\xc2@\x99\x807\u1f46\x8c\xf0\x19b\xfb\xe1\x93\xe0O3dL\xc0\x10'\x19>\b\x9fbȤ\x90!n*|\x18>b\xc8\xd44C\xea\xd3\xe13\xe03\r\x99\x91eH\"\n\x9f\r\x9fcȴ\xd3\r\x89\x9fa\xc8\xf43\ri8ː9m\fi\xce5d\xf4ن\xc4\xf2\f\x99\xd8\u0590\xb2v\x861i\xc69\x86\xe1&\xce5d\xeey\x86\xb4\x9c\x8f\x15\xe5cE\x17`%\xed\xb1\x92\x0e\xb8b\x01\xae\xd8\x11\xbe\x13|!|\x11|g\xf8.\xf0]1s7\xcc\xdcݐ\xaa\x1e\x86\xd4\xf4\xc4\xd5z\x19R\xd1\xdb0\xcaJ\xfa\x18F]m_\xcc\xd8ϐa\xfd\r)\x1d`\xa0P`M\x83\xb0\xb6\xc1\xb8\xda\x10Ü7h\xc1Pr\x98!C\x87c\xfd#\fY8\x12W\x1f\x85\xab\x8f6d\xec\x18\xcc6\u0590\xbaq\xb0\xf1\xb8\xdf\t\xb8߉\xb8\xea$\\u2V:\x056\x156͐\x92\xe9X\xd1\f\\g&\xeem\x16l6\xe6\x98c\xc8\u0e70yhχ-\x80-\x84-¼ņ\x94\xa3V\x97\x97\xc0Jaeȝ\x8d\xb9\x17#G\xe5\xb0\x18r\xef\xc0*\xb0\x0f\x95\xb8\xc3*X5\xac\xc60\xaa溆̪\x85\xd5\x19R]\x8f[\x8f\xc3\x1a`\t,\xa2\x11\x89\x1dф\x1c-1dd3V\xdc\x02[\n[\x06[\x0e[\x01[\t[\x85;Z\r\xbb\x10\x19\xb9\b\xbbv1Vu\t\xfc\xa5\xb0ː\xcb\xcbaW`UW\"3W\x19\xd2x5\xb2\xb3Ɛ\xa6kp\xf7\xd7b\xa5\xd7!\x8b\xd7\xc3n\xc0\xfe\xdd\b\xbbɐA7\xc3n\x81݊\xac߆;\xbf\x1d\x99\xbd\x03\x19\xbdӐQwA\x01w\xe3\x1a\xf7\x18Ry/v\xe3>d\xf5~\x8c]\v{\x00{\xd6\xf2 0\xf7!m=l\xe0\x11~\x04\x98\xf6(0\xec1\xc3X<\xf1q\xdcx\xfc\t\xa0\xe5IØ5\xeb)\xc3X\xb0\xe0i$\xea\x19س\x98\xf29\xf8u\x98~=nm\x03l#\xecy\xd8\vX\xf2\x8bX\xf2&C\xf27\x1b\xd2n\x8b!\xc5[\xd1~\t\xfee\xd8+h\xbf\x8a%\xbc\x06{ݐ\xfeo\x182\xf0M\xdc\xea[\x86\xcc|\x1b\xfe\x1d\xf8w1\xe6=C:\xbdoH\xd1\a\x86\xcc\xffА\x8e\x1f\x19\xb2h\x1b\xda\x1f#\xf6\x89!}?5\xa4\xf03\xcc\xf79\xc6n\xc7y;p\xde\x17\x86\x14|\x89\xf3\xbeB\xfbk\x1c\xff\x06)\xf9\x16\xe7~gȊ\x9dX\xcb.\xa4\xff{Cf\xff\x80\xf4\xfc\x88\xcd\xfd\t[\xf1\xb3!K~\xc1Z\xfe\xc19\xff\x1a2\xe0?\x8c\xdbM\x99\xee!\xf7\x92\xfb\xc8\xfd\xe4\x01\xf2 y\x88\xb9\x80\\H.\"KM\xb1\xcaL\xb1mS\x9cŦ\xb8\xe5\xa6$b\xa6\x94:\xa6\x94U\x98\x12\xab4\xa5\xbaʔ\xfajS\x1ajLitMYR\xcb3\xeb\xc8z2N6\x98\x92\x9f0\xa5c#{M\xe4\x12\xb2\x99\\J.#\x97\x93+L\x19\xb4\x92\xadU\xe4j\xf2B\xf2\"\xf2RS\x86\\fJ\xbf\xcbM\x19q\x05\xecJS\xc6\\\xc5#W\x9b2i\r[ט2\xe3Z\xf4\xaeñ\xeb1\xfe\x06S\x86\xdeh\xe2cҔ\xe17\xe3\x9c[L\x99{+\xfcm&\xde\foǠ;L\x19w\xa7)\xe3\xef2e\xc2\xdd&>&Mc\\\xfc^\xccp\x9f)\x93\xef7e\xeaZ\xce\xfb\x80)\xd3\x1e4e\xfaC\x98\xffaӘ\x1c{\x04\xe3b\x8f\xa2\x15\u007f\f\x83\x1f\xc7LO\xe0ГH\xe1SH\xe1Ӱg\x90\xc6g\x91\xc2\xe7\x10[gJ\xc9zS\xca7 \x95\x1bq\xecy\x13\x9f\x93\xf0/\x9ax;܄\x81\x9bM\xa9\xdcbJ\xd5V\xe4\xf8%Sj^Fn_\xc1\x1e\xbcjJ\xedk\xc8\xf9\xeb\xa6\xc4߀\xbd\x89ܿ\x85I\xdf6\x8d\xda\xd8;\xa6Q\x19{\x17\xad\xf8{\x18\xfa>\xa6\xf9\x00\x87>D\xfb#\xf8m\xf0\x1fs\xf1\x9f\xe0\xea\x9f\xea\x95>\xc3\x12?\x87m\x87\xed\xd0\x19\xbe\xc0\xf4_\x9ar\xfeW\x1c\xf75\xf9\x8diLk\xf8\xd64\xe2\rߙ2k\xa7)M\xbbL\x99\xf2\xbd)u?\xe0H\xed\x8f8R\xfb\x13Z\xb1\x9fъ\xfdb\xca\xc8_MY\xfc\x9b\xe6\xe3w]\xd0\x1f\xa6\x8c\xfe\x13r\xf9\vi\xff\x1b\xf7\xfc\x8fi\f\x1b\xfd\xafi\x94\xc6\xfeCҝ\xddX\x88\xb3\a\xb7\xbb\x17\xb7\xb5\x0f\xf2\xdaoJ\xc5\x01^\xf9 y\x88t[\xfc^\x19\x93\xe45\xe68\xa7yel\xc0\x8b7\x8ad/\xde#\x82^sz<\x96\xe25\xc6U\x840\"\xd5+3\xc2^sxK,\xe2\x95!i^\x19\x9a\ue559\x19^\x19\x95\xe9\x95\xe1Y^\x19\x11\xf5\x1asc\xd9^\x99\x9b\x83\xe1\xa7\xc3\xce\xf0ʸ3\xbd2\xfe,\xafLh㕉\xb9^\x99t\xb6W&\xe7yej[\xafLk\xe7\x95\xe9\xe7`\xdas\xbd2\xf2<\\'v\xbeט\x1e\xcf\xf7\x1a\xc3b\x17x\x91\xc3\xf6^\xef\xb4Xi\xac\x83W\xf2\v\xbc2\xa7#|'\\\xa7\x10+O\x14\x01Vg\xafX]\xbcR\xd2\xd5+\x8dݼR\xde\xdd+e=\xbcb\xf7\xf4╣\x97WZz{\xc5\xe9\x03\xeb\xeb\x95\xca~^\xa9\xea\xef\x95\xea\x01^\xa9\x19\xe8\x15w\x90Wj\a{\xa5~\x88W\xe2C\xbd\xd20\xcc+\x89\xe1^Y<\u008b}\x1a\xe9E\x11\x1f\xe5ž\x8c\xf6b#\xc7x\xbdq\xacd,V0\xce+\xcd\xe3\xe1'\xe0:\x13\xbd\x86\x93\x98\x04X\x93\x01{\n\xe0N\xf5\x1ae\x15ӼFy\xc5t\x8d\xcd@\xb7e&\x960\xcbk4;\xb3\xbdR1Nj\u05f5\xb9^\xbc\xa4\xcd\xf3\x9a\r\xf1\xd8|\\\xafb\x01F,\xc4\x02\x16yͲ\x96X1\x12\x85\xef@n\t\xee\xb6\x14\xd7)ӽ\xb2u\xbaň\x94#\x12ӈ\xa3\x91\n\x8c\xad\xc4\xd8*\xdd\xdej]A\rR\x19w1k\xbc\u058bG\xb0\u038bg\xa0\x1e)\x8e\xe3\xe6\x1a\x90\xc7\x04\xee\xa0\x11\xbe\t~\t\xae\xd9\fk\xc1,K1\xcb2\xf8\xe5\xf0+\xbcƤ\x86\x95^\xc3mX\xe5\x95)\xab\xbdRw\xa1\xd7ߡK\xe7Ν;\\\xe6UI]\xee\rh\xb7P#Wx\x83\x1d\xbah\x8b\xbd+y\xfc*\xf2j\xaf\xb4[\x03\xbb\xc6kL\u037f\xd6k\xd4\xe7_\a\xed\xe5_\x8f\xf4\xe4ߠ\xad\x1b\xb5u\x93\xb6n\xd6\xd6-^sn,\xffV\xaf\xd9\x12˿\rrʿ\x1d;\x99\u007f\an(\xffN\xdcP\xfe]ں[[\xf7h\xeb^mݧ\xad\xfb\xb5\xb5\x16\xb2\xcd\u007f\x00\x99\xcd\u007f\x10\xad\xf2\x87\xd0*\u007f\x18)\xc8\u007f\x04)\xc8\u007fT\x15\xf6\x98n\xee\xe3\x10W\xfe\x13\xd8\xd7\xfc'!\xb8\xfc\xa7\xb0\xd7\xf9OC\x87\xcf \x11\xcfz\x8d\x19\xf9\xcfy\x8dD\xfe:\xaf9.\x96\xbf\xdekV\xc6\xf27\xe0Yh\x88o\xc4~5ğ\xf7\x9a\xc3b\xf9/x\xcd\xd2X\xfe\x8blob{\xb3WFo\xf1Jl\xab\xea\xf7%\x95\xce\xcb<\xfa\n\x8f\xbe\xea\x95\xe2\xd7\xf4\x01y]\xb5\xf9\x86\xae\xf9M]\xf3[\xcc\xd3\xdb\xe4;\xba\xfewu\xfd\xef\xb1\xff>\xf9\x81\xce\xf7\xa1\xce\xf7\x11\xfb\xdbȏ\xc9O\xf0\x00|\n\xfb\f\x8f\xe4\xe7\xf0۽\xc6\x10{\x87\x17\xaf\xcc_\xa8@\xbeT\x81|\xe5\x95\xc1_þ\x81}\v\xfbNW\xb1SW\xb1\v\xcf\xea\xf7xJ~\xd0*\xf0\xa3\n\xf5'<\xb5?C\x8a\xbf\xc0\xff\n\xff\x1b\xe4\xf0;\xe4\xf0\a\xfc\x9f\xf0\u007f\xc1\xff\r\xff\x0fd\xf8/d\xf8\x1f\xb2\xb6\x1bY\xdb\x03\xbf\x17~\x1f\xfc~\xf8\x03\xba惺\xe6C\\\xe7a\xf2\bDw\x14\xa2;\xc6\xdeq\xf2\x04y\x92\xd4g\x8c\x8a\r\xf3a\xbf\x86\xfbd\xc2\b\x9f\xcc\x19铉\xa3|\xa8\xbf\xa3}2i\x8c\x0f\x1a.\x1e\xeb\x93\xc9\xe3|2v\xbcϘ\x1a\x9b\xe0\x93i\x13}2s\x92O\xa6O\xf6\xc9\xd4)>|P\x14O\xf5ɬi>cr\xf1t\\\xa7x\x06N\x9d铑\xb3\xb8\xd8\xd9\xe4\x1c\x9f\xf4\x9f\xeb\x93\xfcy>)\x9a\uf4f6\v|\xd2q\xa1O\x06-\xf2Ia1GX>\xb1J|RR\xea\xc3w\n\x9f\x94\xd9>\xb1\x17\xfb\xa4\xa5\x1c>\x06s|FCq\x85\x0fҭ\xf4\x89S\xe5÷\n\x1f\x9e\xe7\x1a\x84\xe3\xaeO*k}\x12\xab\xf3A\xc7\xf5>\xa3<\x16\xf7A\u007f\r>\xa9N\xf8P\xf0|R\xd3\xe4C)_\xe2\x13\xb7هG\xb2\xb8\xc5'\xb5K}R\xb1\xccg\xd4ǖ\xfb$\xbe\xc2'\x8d+}Ұ\xca'\xf5\xab}\xa8\xcd\xc5\x17\xfa\xa4\xe9\"\x9fQ[|1\xaeS|\tN\xbd\xd4'\x8b/\xf3\x19v\xd3\xe5\\\xf1\x15X\xfb\x95>\xe9t\x15{W\x93k\xc8k\xc8k\xc9\xebȵ\xe4\xb3>\x19\xfc\x1c\xeee\x1d\xeeu=&܀;و\xfe\xf3\xb0\x17\x10{\x11\xb6\t\xf1\xcd\x1c\xbd\xc5'\x89\xad\xc8\xd9\x1b>\xe9\xfb\x16#o\x93\xef\x90\xef\x92\xef\x91\xef\x93\x1f\x90\x1f\x92\x1f\x91\xdbȏ\xc9O\x91\xdbϐ\xdbϑ\xdb\xed\xc8\xd5\x0e\xdc\xf0\x17\xc8\uf5daůp\xe7_#S\xdf \x91\xdf¾Czw\"{\xbb`\xdf#{?\xc0~D\x96~\xf2I\xf1\xcfH\xdd/\xb0_5\xf3\xbf)~\xf7I\xdd\x1f\xc8۟\x9a\xe3\xbf0\xd5\u07fc\xe2?\xe4\xbf\xe4\u007f\xe4nr\x0f\xf4\xb3\xd7g\xb8\xcd\xfbp\xfb\xfbq\x83\a|\xd2\xee \x0f\x1d\"\x0f\x93Gȣ\xe41\xf28y\x82\x15~9\xaf\xd2/\x85Uزj\xbft\xa8\xe1\xd9.Y\x875\xd4\xc3\xe2\xb0\x04\xac\xd1o\xe475\x01\x89%@s3\xf2\xd9\xd0\xe2\xc7\xc7yl)Ҽ\f)^\x8et\xad\xf0K\xedJM\xf5*\xbfQ\x12[\x8d\x91\xb1\v\x91\xf0\x8b\xfcFM\xf3\xc5Hv\xf3%\xe8]\xeaGY\xb9\xcco\x96\x96\xc6.G\xac\xec\n\xe4\xfaJ\xd8U~cx\xc3պ%k\xfcfYY\xec\x1a\xecе\x88_\a\xbb\xdeo\xd4\xd7\xdf\xe0\x97\xa97\xc2n\x82\xdd\f\xbb\x05v+\xec6\xec\xeb\xed\xf0w`\xe3\xee\x84\xdd\x05\xbb\x1bv\x0f\xec^\xac\xea>\xbf1j\xf4\xfd~\x19\xb9\x16\xf6\x00\xecA\xbf4>\x84\xfdz؏o)\x8f\xf8eʣ\xb0\xc7T\x15\x8fc\xf7\x9e\xf0˸'aOa\xd5\xe5O\xa3\xf1\x8c_ʟ\x85\x00\x9e\xf3\xcb\xc4u\xf0\xeba\x1b`\x1b\xfd2\xfey\xd8\v\xb0\x17a\x9bp|3l\vl+\xec%\xd8\xcb\xd8\xfeW\xfc\xf8px\xd5oĚ_C\xefu\xa4\xe0\r\xc8\xf2M\xec\xf6[~õ\xdfV\xbc\x03\r\xbc\v\xd5&\xdeS\xbc\x8fc\x1f`\x91\x1fB0\x1f\xf9e\xce6\xd8Lj}\x86\xfe\xe7\xaa\xed\xedض\x1dP\x92\xbd\x133\xed\xf2\xcb9\u007f\xfae\xe1\u007fܹ\xdd\xe4\x1eh`/4\xb0\x0f\x1a\xd8\x0f\r\x1c\x80\x06\x0eB\x03\x87\xa0\x81\xc3\xd0\xc0\x11h\xe0(4p̏ϩ\xe3\xc8\xf0\t$)v\xd2/\xe7\x9f\xf2\x1b\x05՞$\xa3\xa8H\x92\xa4\xc8H\x92\x8ef\x92\xb4\xf5\xc2|I\xd2ɏ~\x12\xec\xb4$\xe9\x17H\x92A\xc9I\xb2\"\x98$KS\x92dy(I:\xa4&\xf1qO\x92\xfc\xf4$)\xc9H\x92\xf2LXV\x92\x94Ea\xd9I\x12\xcbI\x92\xa6ӓ\xa4\xe5\x8c$\x19}f\x924\x9c\x05k\x93$\u0379IFs\xec\xec$\xa9\xccK\x92\xaa\xb6IR\xdd.Ij\xceI\x92\xf8\xb9\xb0\xf3\x92\xa4\xf8\xfc$\xa9͇]\x90$\xd3\xda'I]\x87$\xa9/H\xc2\x03\xdb\x113t\xe2u\v\xc9\"\xb2s\x12\xbe\xdb\xc0\xbaº%ɐ\xee\xb0\x1e\xb0\x9eIb\xf7\x82\xf5\x86\xf5I\x92\x11}\x93\xc4\xe9\a\xeb\x9f$\x89\x01\xb0\x81\xb0AI\xe2\x0e\xc1%\x87\xe2>F$\xc9\xec\x91I2eT\x92\f\x1e\r\x1b\x93$\xcb\xc6&I\xc18^e<9\x81\x9cHN\"'\x93Sȩ\xe44r:9\x83\x9cI\xce\"g\x93sȹ\xe4\xb9\x80\\H.\"\x8bI\x8b,!K\xc92\xd2&\x17\x93\xe5d\x8ct\xc8\n\xb2\x92\xac\"\xab\xc9\x1a\xd2%k\xc9:\xb2\x9e\x8c\x93\rd\x82l$\x9b\xc8%d3\xd9B.%\x97\x91\xcb\xc9\x15\xe4Jr\x15\x94\xb0Z7\xefB\xec\xf2E\xd8ɋ!\x97K\x92d\xfc\xa5P\xc0eH\xfb\xe5I\xd2x\x05Tpe\x92,\xbe*\tE\xe1\xea$ԕ5\xd0\xc65\x10ԵI\xa8(\xd7A\x18\xd7CU7@M7B\x037AE7C \xb7$IŭI(2\xb7ᔆۓ\x8cX\xec\x8e$<\xdcw\xea\fwa\xf0\xddz\xe1{p\xf6\xbd8\xfb>\x9cy?\x82k\xb1\xdf\x0f$\xe1\xb5\xe7Ah\xeb!\x88\xf4a\b\xe7\x91$ò\x1e\x85:\x1eK2l\xfbqH\xe3\x89$\xc3u\x9f\x84v\x9eB\xa4\xf9i\b\xe5\x19D\xac\xe7xW\xeb\xc8\xf5\xe4\x06r#\xf9<\xf9\x02\xf9\"\xb9\x89\xdcLn!\xb7\x92/\x91/\x93\xaf\x90\xaf\x92\xaf\x91\xaf\x93o\x90o\x92o\x91o\x93\xef\x90\xef\x92\xef\x91\xef\x93\x1f\x90\x1f\x92\x1f\x91\xdbȏ\xc9O\xc8O\xc9\xcf\xc8\xcf\xc9\xed\xe4\x0e\xf2\v\xf2K\xf2+\xf2k\xf2\x1b\xf2[\xf2;r'\xb9\x8b\xfc\x9e\xfc\x81\xfc\x91\xfc\x89\xfc\x99\xfc\x85\xfc\x95\xfc\x8d\xfc\x9d\xfc\x83\xfc\x93\xfc\x8b\xfc\x9b\xfc\x87\xfc\x97\xfc\x8f\xdcM\xee!\xf7\x92\xfb\xc8\xfd\xe4\x01\xf2 y\x88`\xd6\xd4\xd4t\bHmA\x00\x1fs\x1d\x03R\xd2)\x80O\xe5\u0080T\x17\x05\xa4\xb9s@\xea\xbb\x04\xf0\xb1\xda5 U\xdd`\xdd\x03fUUU\x8f\x804\xf6\f\xe0\xf9\xe8\x05\xc4{\a$\xde' \xb1\xbe\xbc\xf1~d\xff\x80\xe4\x0f\x80\r\xd4\x14\fB\n\x06k\n\x86 \x05C5\x05Ð\x82\xe1\x01c\xea\xd4\x11\x9a\x83\x91\xb8\xffQ\xb0њ\x831\x9a\x83\xb1\xb8\xffq\xb0\U0005a0c9\x9cq\x1299`\x0e\x991a\nf\x9e\n\x9b\x16\x90\xe2\xe9\xf03xl&9\x8b\x9c\x1d\x90\xba9\x01\xb32\x16\x9b\x1b0\xcbc\xb1y\x01i\x99\x1f\xf0\x96!\x1d\vp?\xb1\x85\x01Y\xbc(`47\x17\xeb*\xac\x801~|\t\xb6\xa4T\x1be\x013\xaf(\xcf\x0ex\xf3\x8a\x8a\xf2\x16\a\xa4sy@\xba\xc4\x02\xd2\xd5\tH\xb7\x8a\x80t\xaf\fH\x8f\xaa\x80\xf4\xac\x0eH\xaf\x9a\x80\xf4v\x03ҧ6 \x85u\xbct=\x19'\x1b\xc8\x04\xd9H6\x91K\xc8f\xb2\x85\\J.#\x97\x93+ȕ\xe4*r5\x14v!\xec\"\xa8\xecbF.\x81\xca.\xd5\x14_\x86\x14_\xae)\xbe\x02)\xbeRS|\x15R|\xb5\xde\xdc\x1a\x8e\xbc\x86\xbc\x16Y\xbeN\xb3|=\xbb7\x907\"\xd17i\xa2o\x86\xd8nQ\xb1\xdd\n\xb1ݦb\xbb]\xc5v\a\xc4v\xa7\x8a\xed.\x88\xedn\x15\xdb=*\xb6{Ul\xf7Ql\xf7\xab\xd8\xd6Rl\x0f\xa8\xd8\x1e\x84\xd8\x1eR\xb1=\f\xb1=\xa2b{\x14b{\x8cW{\x1cb{B\xc5\xf6$\xc4\xf6\x94\x8a\xedi\x88\xed\x19\x88\xedY\x88\xed9\x8eY\a\xa5\xadgk\x03\xb9\x91|^\x15\xf7\x82*\xeeE(n\x13\x14\xb7\x99\xf1-\xe4V\xe8\xe0%\xb6^ք\xbc\x82\x84\xbc\xaa\ty\r\ty]\x13\xf2\x06\x12\xf2\xa6&\xe4-\x8ez\x9b|\a\tyW\x13\xf2\x1e\xbb\xef\x93\x1f !\x1fjB\xb6\xb1\xfb1\xf9\t\xf9)\xf9\x19\xf99\xb9\x9d\xdcA~A~\x19\x90\x82\xaf\xd8\xfa\x9a\xfc\x86\xfc\x96\xfcN\x1f\xa6\x9d\xaa\xc0]\xec\u007f\xaf\x1a\xfcAW\xf5\xa3J\xef'h\xf0gm\xfc£\xbf\x92\xbfA\x82\xbfC\x82\u007f@\x82\u007fB\x82\u007fA\x82\u007fC\x82\xff@\x82\xffB\x82\xffA\x82\xbb!\xc1=\x986\u007f/P\xbc\x0f\x13\xc6\xf7+\x0e\x04\x8c.E\a\x03FעC\x01\xa3[\xd1\xe1\x80ѽ\xe8H )\xafK^\xa7\xbc.EG\x03fQ\x97\x9e\xc7y\x95\x13\xe4I\xf2\x14\xe9Ifi#\x8dd\x147\xb6\xbc\xa4/\x19\xe5-Y\xcb[2\xca[\xb2\x96\xb7d\x94\xb7d-o\x1c\x90B\x86\xc8T2\x9c\x8cO\xc7H\xb2\x969\xf6\xd3Ɍd|Tf&k\xa9KF\xa9K\xd6R\x97\x8cR\x97\xac\xa5.YK]2J]\xb2\x96\xbad\x94\xbad-u\xc9Z꒵\xd4%\xb3\xd4%k\xa9Kf\xa9K\xd6R\x97\x8cR\x97\xac\xa5.\x19\xa5.YK]2J\x1d/\xd7!\x19\xa5.YK]2J]\xb2QRR\x98\x8cR\x97\x8cR\x97\x8cR\xc71]\x93Q뒍\xaa\xaa\xee\xec\xf6HF\xb1K\xd6b\xc7n\xefdT\xbbdT;\xf6\xfa\x91\xfd\x93Q\xed\xd8\x1a\xa89\x19\x84\x9c\f֜\fAN\x86jN\x86\xf1\xe0pr\x049\x92\x1c\xa59\x19\xad9\x19\xc3\xfeXr\x9c\xe6d\xbc\xe6d\"\xfb\x93\xc8\xc9\xe4\x14r*9\x8d\x9cN\xce g\x92\xb3\xc8\xd9\xe4\x9cd\x16\xc1d\x16\xc1d\x14\xc1d\bp\x01\x0f-LF\rd\xab\x98\xb4\xc8\x12\xb2\x94,#mrq2\na2\na2\na2\na2\na2\na2\na2\na2\na2\n!\x94R\x97,\xa3\x1b\x92\xfd\xa3\nG\x14N*L\xf0\xf4F\xb2\x89\\B6\x93-\xe4Rr\x19\xb9\x9c\\A\xae$W\x91\xab1\U00045c0b0\xf9Ō\\\x02\x11^\xaa\t\xbf\f\t\xbf\\\x13~\x05\x12~\xa5&\xfc\xaadT?\x0eZ\x83\x14O\xbc\x86\xcdk\x93Q\xfd4\xd9\xd7#\xc1\x13o`\xec\xc6d\x14?M\xf5͐\xdf-*\xbf[!\xbf\xdbT~\xb7\xab\xfc\xee\x80\xfc\xeeT\xf9\xdd\x05\xf9ݭ\xf2\xbbG\xe5w\xaf\xca\xef>\xca\xef~\x95\xdfZ\xca\xef\x01\x95߃\x90\xdfC*\xbf\x87!\xbfGT~\x8fB~\x8f\xf1j\x8fC~O\xa8\xfc\x9e\x84\xfc\x9eJ\xd6\xe2\a\xf9=\x93l4[\xcfB\u007f\xcfq\xd0:\xe8o\xbd\xeao\x03\xbb\x1b\xa1\xbf\xe7U\u007f/$k\xe9\x83\xfc6A~\x9byl\v\xb9\x15\xf2{\t\xf6\xb2\xe6\xe2\x15\xe4\xe2U\xcd\xc5k\xc8\xc5뚋7\x90\x8b7\x93\xb5\xf0i*\xde\xe6)\xef \x15\xefj*\xde\xd3T\xbc\xcf\xd8\aHŇ\x9a\x8am\xec~L~\x92\xac\x1f\xb8\x9f\xb2\xfd\x19\xf99\xb9\x9d\xdcA~A~I~E~M~C~K~G\xee$w\x91ߓ?\xe8\xc2~d\xf3'\xf2g\xf2\x17\xf2W\xf27(\xefw(\xef\x0f(\xefO(\xef/(\xefo(\xef\x1f(\xef_(\xef?(o7\x94\xb7\x87\xa3\xf7\x92\xfb\xc8\xfd\xe4\x01\xf2 y\x88\b\x05>\xc4\xee\xc3\xe4#\xe4\xa3P\xe1cA\xfd\xc2\xf18T\xf8\x04cO\x92O\x91OC\x89\xcf@\x89\xcfB\x89\xcf鞭\x83\x10\u05eb\x107\x04\xf5[\xc7\xc6 \n!G\xbe\xa0j|\x11j\xdc\x045nfh\v\xb9\x95|\x89|Y\xf3\xf3\n\xf2\xf3\xaa\xe6\xe75\xe4\xe7u\xcd\xcf\x1b<\xf8&\xf9\x16\xf96\xf2\xf3\x8e\xe6\xe7]\xcd\xcf{\x8c\xbd\x8f\xfc|\xa0\xf9\xf9P\U000f3371\x8f\xc9O\xc8O\xc9\xcf\xc8\xcf\xc9\xed\xe4\x0e\xf2\v\xf2Kh\xf2+\xb6\xbe&\xbf!\xbf%\xbf#w\x92\xbb\xc8\xef\xc9\x1f\xc8\x1fɟȟ\xc9_\xc8_\xc9\xdf \xcc\xdf!\xcc? \xcc?!̿ ̿!\xcc\u007f \xcc\u007f!\xcc\xff \xcc\xdd\x10枠\xb7\xa0K炽A\x1f\\\xe7\x82}A\xbf\xfa\xce\x05\xfb9\xcb\x01\xf2 y\x88j\xf0~\xd5\xe0Zj\xf0\x01\xd5\xe0\x83\xd0\xe0C\xaa\xc1\x87\xa1\xc1GT\x83\x8fB\x83\x8f\xf1\x92\x8fC\x83O\xa8\x06\x9f\x84\x06\x9fR\r>\r\r>\x03\r>\v\r>\xa7\x1a\\\a\r\xaeW\rn\xe0\x19\x1b\xa1\xc1\xe7U\x83/\xa8\x06_\x84\x067A\x83\x9byl\v\xb9\x95|\x89|Ys\xf1\nr\xf1\xaa\xe6\xe25\xe4\xe2u\xcd\xc5\x1b\xc8ś\xba\xddoq\xd4\xdbH\xc6;\x9a\x8cw5\x19\xef1\xf6>\x92\xf1\x81&\xe3CM\xc66\xc6>&?!?%?#?'\xb7\x93;\xa0\xbf/`_\xb2\xf7\x15\xf95\xf9\r\xf9-\xf9\x1d\xb9\x93ܕ\xa2U\xfe{\xb6\u007f\xd0E\xfd\xa8\x1a\xfc\x89\xfd\x9f\xc9_\xc8_\xc9ߠ\xc1ߡ\xc1?\xa0\xc1?\xa1\xc1\xbf\xa0\xc1\xbf\xa1\xc1\u007f\xa0\xc1\u007f\xa1\xc1\xff\xa0\xc1\xdd\xd0\xe0\x1e\x8e\xdeK\xee#\xf7\x93\aȃ\xe4!\xf20y\x84#Uz\xccL\x95\x9e\xb3R\xa5\xd7\xecT\xe9='U\xfa\xccM\xe5\u007f\xab\x98\x97\xea\x83+ʛϋ- \x17\x92\x8b\xc8b\xd2\"K\xc8R\xb2\x8c\xb4\xc9\xc5d9\x19#\x1d\xb2\x82\xac$\xab\xc8j\xb2\x86t\xc9Z\xb2\x8e\xac'\xe3d\x03\x99 \x1b\xc9&r\t\xd9L\xb6\x90K\xc9e\xe4rr\x05\xb9\x92\\E\xae\x86\xc0.T1]\xc4\xee\xc5ڼ\x84\xcdK\xc9\xcbTc\x97\xab\x9c\xae`\xffJ\xec\xebUl]M\xae\xc1\xf1\xe6kؼ\x96\xbc\x8e\xbc\x9e\xbc\x81\xbc\x91\xbc\tr\xbb\x19\"\xb8EEv\xab\xe26\xc6o\x87\xde\xee\x80\xde\xee\x84\xde\xeeRu\xdd\r\x99ݣ\x8d{Ѹ\x8fc\xee\x87\xe0\xd6Bp\x0f@p\x0f2\xf2\x10d\xf50[\x8f@t\x8f\xb2\xf5\x18\xf98\x16\xf8\x04\xae\xf3${OAz\xcf@v\xcfBr\xebTr\xebUr\x1b \xb9\x8d*\xb9\xe71\xe9\v\xa9Fs\xf3\x8b\x10\xdc&\x15\xdcf\x9e\xb6\x05ZڊK\xbc\xa4w\xf72C\xaf\x90\xafBm\xaf\xa5\x1a\xb6\xf3:F\xbc\x81\xb9\x9a\xdfT\xb5\xbdŃoCn\xef\xb0\xf5\x11D\xb7\x8d\xad\x8f\xc9O \xbdO!\xbd\xcf \xbd\xcf!\xbd\xed\x90\xde\x0eH\xef\vH\xefKH\xef+H\xefkH\xef\x1b\x8e\xfe\x96\xfc.Ո\xd5\xec\x04\xaaw\xb1\xff=\xf9\x03\xf9#\xf9\x13\xf93\xf9\v\xf9+\xf9\x1b\xf9;\xf9\a\xf9'\xf9\x17\xf97\xf9\x0f\xf9/\xf9\x1f\xb9\x9b\xdcC\xee%\xf7\x91\xfb\xc9\x03\xe4A\xf2\x10y\x987\xac\u007f\x95?\x0f#[\xe6\x87\xf5\xaf\xf1\x17\x84\xa5ia\xd8h\x89-\nKKqX\xf2\xad\xb04\x97\x84\xa5\xbe4,Uea|P\xdba3\x1e\x8f-\x0eK\xbc<,\xb1XX,'lV\xc6\xe3\x15\x18T\xc9UV\x91\xd5d\r钵d]\x18\x0fS}X\x9cx\x18\xcfQCX\x12\x890\x1e\x9fưLm\n\xe3SpIX\xc67\x87\xf1I\xd8\x12\x16{i\x18\x9f\xcf\xcb\xc2\xe2.\x0f\xe3\xa3yEX&\xac\f\xcb\xe8U8{\xb5\x9e}5g\\C^C^K^\x87\x8c^\xaf\x19\xbd\x01\x19\xbdQ3z\x93f\xf4fd\xf4\x16\xcd\xe8\xad\xc8\xe8m\x1cy\xbbf\xf4\x0e\xcd\xe8\x9d\xcc\xe8]\x9aѻ\x99\xd1{4\xa3\xf7\"\xa3\xf7iF\xefGF\xd7jF\x1f@F\x1fDF\x1fҌ>\x8c\x8c>\xa2\x19}\x14\x19}L3\xfa83\xfa\x84f\xf4If\xf4)d\xf4i\xcd\xe83\xc8\xe8\xb3\xc8\xe8s\xc8\xe8:$k=2\xbaA3\xba\x11\x88?\x8f\x84\xbe\x80\x84\xbe\x88\x84nbB7\xe3\xc4-\x18\xbb\x15c_\xe2b_\x0e\xcb\xecW ⾳\xfb潪b\x9eT\x94\xf7\x9az\xb7(\xefu\xf8\xf9\xee\xfc\xbc7\xe0/\x98\xd4>\xef#\x9e\xb1\x8d\xfc\x84\xfc\x94\xfc\x8c\xfc\x9c\xdcN\xee \xbf \xbf$\xbf\"\xbf&\xbf!\xbf%\xbf#w\x92\xbb\xc8\xef\xc9\x1f\xc8\x1fɟȟ\xc9_\xc8_\xc9\xdf\xc8\xdf\xc9?\xc8?ɿȿ\xc9\u007f\xc8\u007f\xc9\xff\xc8\xdd\xe4\x1er/\xb9\x8f\xdcO\x1e \x0f\x92\x87\xc8\xc3\xe4\x11\xf2(y\x8c\xb9\x80\\H.\"\x8bI\x8b,!K\xc92\xd2&\x17\x93\xe5d\x8ct\xc8\n\xb2\x92\xac\"\xab\xc9\x1a\xd2%k\xc9:\xb2\x9e\x8c\x93\rd\x82l$\x9b\xc8%d3\xd9B.%\x97\x91\xcb\xc9\x15\xe4Jr\x15\xb9\x9a\xbc\x90\xbc\x88\xbc\x98\xbc\x84\xbc\x94\xbc\x8c\xbc\x9c\xbc\x82\xbc\x92\xbc\x8a\xbc\x9a\\C^C^K^G^O\xde@\xdeH\xdeD\xdeL\xdeB\xdeJ\xdeF\xdeN\xdeA\xdeI\xdeE\xdeM\xdeC\xdeK\xde\x17\x91!\xf7Gd\xe8ڈ\x8cz \"\xc3\x1f\x8cȈ\x87\"2\xf3\xe1\x88\xcc}$bL/~4\"c\x1e\x8bȸDZ\xabODd\u0093\xd0\xdeS\x11\x99\xf4tD&?\x131\xe6ƞž?\x17\x91i\xeb\"2}}Dfl\x88\x18\x93\x8b7F\x8cq\xc5\xcfG\x8cQ\xf9/Ddʋ\x11cZlSD\xff\x99\xc6\xe6\x881\xacx\v\xce\xcb\xdf\x1a\x91a/\xa1\x17{9\x82\xfa\x18\x91\xb1\xafFd\xf4k\xb8\xfc\xeb\x11\x99\xf3FDf\xbd\x191f\xc7ފ\x18\x93F\xbcͥ\xbeC\xbeK\xbeG\xbeO~@~H~Dn#?&?\xc1#\xf9)\x1e\xe0\xcfP*>\xc7\xe3\xbd\x1d\x8fՎ\x884~\x11\x91\x96/\xf1\xcc\x17\u007f\x85'\xefk\x14\x94o\xf0@\u007f\x8b\a\xfc;<\xfa;\xf1\x84\xee\xc2\xe3\xff}\x04\x9f\x00?\xe0i\xff\x11O\xf9O\x11i\xf8\x19O\xe7/\xa8\tſ\xa2\xfc\x14\xff\x86ғ\xff{D\xea\xfe\xd0\xe7\xe2ψ\xfe\x93\x8d\xbfP\x80\x8a\xff\xc6y\xf9\xff\xa0\x1c\xfd\xab\xe5迈,ٍ\x92\xb4\aeb/.\xbf\x0f%d?Jǁ\x88\xb1$v0b\xb8\xf6\xa1\x88,>\xcc\xe5\x1e!\x8f\x92\xc7\xc8㭏\xe3\tvN\x92\xa7HO\x9a\x94K\x9aQ^n\xa4I\x8d\x99&e\xde4\xd45_\x9a\xd4\xfbӤ:)MJNK3JJ\x02i\x12ON\xc3#\x9b\x92&\x15\xa14\xa3\xa2\"5MJ\xc3iR\x19I\x93\x86\xb44\xa9MO\x93XF\x1a\nh&P\x93\x05\x94E\x81\x92l\xccW\x9e\x93f\xd4ל\x0eԟ\x01\xc4\xce\x04&\x9e\x95fT\x97\xb4\x01&\xe6\xe2\n\xe5g\x035m\x81x\xbb4\xb3$^~\x8e\xb2\xe1\\e\xc9y\xca\xf8\xf9ʊ|\f\xa9\xb8\x00(m\x0f4t\x00j\v\x80\x89\x1dq\xb8db',\xb2\xbc\x10\xa8)\x02\xca:\x03\xf5]\x80\xea\xae@I\xb743^R\xde\x1d\x8c\xc7{\xe0\x96z\"X\xd1\v(\xed\rT\xf6\x01\x1a\xfa\x02\xb5\xfd\x80\xd8\xe04\x99;\x04\xf9\x19\x8a\xb4\fC6\x86#\x1b#p\xd6H\xc4G#\x0fc\x90\x82\xb1\xb8\xfdq\xb8\xfd\xf1i2q\x02bSӌ\xd2\xcai@lf\x9aQ[2\v\x988;͈\xc5\xe6\xa4ɔ\xb9i\xace\xe4|r\x01\xb9\x90\xb4\xd2\xc4*I×\xd2\xd24\xa3\xd9*K3\x9b-\xdbN3lwq\x9a\xd8\xe5\xe8\xdan\fGl'M܊4\xa3ɪL3\x9b,\xbb*\r{_\x8d\x03nM\x9a$\\\xc4l\xb7\x16\x87\xed:\xc0\xa9ǁD\x1c\xb3$\x1a\xd0r\x12i\xe24\xa6\x99V'\xb7I\x99X\x92\xe6mF\xa79\xcd\a\xd7춤ym\xb7\x93\xbb\x94.\xb1,\xcd\ag'\x96㨆W\xb4\xfa\xc4\xca4/\xc2\xee\xaa4\x13n\xb5v\x9a\xed\v\xb5\xe3^\xa4L\\\x8cq\x88Y\x97\xa4\xf9\xe9\xedK[\x1b\xb6{\x19.\x87a\x97\xd39W\xa4\x99\x89N֕i^о*-)щ\x97\xbbZ\x03\xcd\xf6\x1a=\x9a\xb8\x06Cq\xf8ZL\xa9\x87\xafc\u05fe\x1e\x13\xea\x18\xf7\x06ƛ\xed\x1b\x19O\xdcD\xe7܌\xf5\xa3w\v֏\xc1\x89[\x11u:%nK3\x9dN\xd6\xedi^\ak\xbaC;\xee\x9d\xca\xc4]\x1a\xb2\x13wkg\xc6=i2\xe3\xde4\xef\f\\\xec\xbe4sF\xa7\xc4\xfdJgm\x9a1c\xc6\x03ܧ\aɇȇ\xc9G\xc8G!\x95\xc7\xf4Qz\x1c\x88?\x81\xe7\xe9I<\x00\x15O\x01\xb1\xa7!\xa1gҤ\xea\xd94\xa3\xaa\xfc9\xa0z\x1dP\xb2\x1e\x88o\x00\x1a6\x02\xb5\xcf\x03\xb1\x17\xa0\xb4\x17\xa1\xb4M\xfa,l\x86ܶ\xe8s\xb7U\x1f\xad\x97 \xb0\x97!\xbaW\xf0ܽ\n\xe1\xbd\x06\u1f4e\xe7\xee\r\\\xb0\xea\xcd4\xb3<^\xfe\x96\x8e{[\x9f\xbew\x80\xf8\xbb\xc0\xdc\xf7\x80\x86\xf7\xf59\xfc \xcdh\xa8\xfa0ͬ*\x8f\u007f\x84k\xd5l\x03\xca>F\xbf!\xf6\t\x9aU\x9f\xa2Y]\xfe\x992\xfe9X\x12߮\x8c\xedH3\xeb'\xd6~\x81v<\xfe%\x06\xce\xfd\n\xa8\xfc\x1a\x98\xf2\r\x9e\xe0\xf2o\x81\xaa\xef\xf4\x81\xde\t\xc4w\xa5\x99\xd5\xf1\xf8\xf7h\xce\xfd\x01(\xfd\x11\x88\xfd\xa4\x8f\xfaϸ\xa9\xaa_\xf4\x99\xfd\x15OF\xec7}<~\xd7G\xf6\x0f}Z\xff\x04\xaa\xfe\xd2\a\xf5o\x05\x12\x9f\x01\xce\xe7\xe9f\xb1em\am{\a\x02\xf6\x17\x80\xfb%\x87~\x95n\xb4X_\x03\x89o\x00\xe7\xdbt\x13\xc5\xe2;жw\"`\xef\x02\xdc\xef\xd1o\xb2~\x00c֏\xca\xc4OJ\xe7\xe7t/\"\xd6/\xeal\xfbW\x8dٿ)\xdd\xdf5\xd4d\xfd\x91\x8e\xb7\x94?\x81\xc4_\x80\xf3w:\xdeW\xac\u007f@\xdb\xfe\x17\x01\xfb?\xc0\xddͅ\xecI7ʬ\xbd@b\x1f\xe0\xecO7\xcb,\xeb\x00h\xdb\a\x11\xb0\x0f\x01\xeea\xf4\x9b\xac#`\x99uT\x998\xa6t\x8e\xa7{\x11\xb1N\xa8\xb3\xed\x93\x1a\xb3O)\xdd\xffi\xa8\xc9\xf2d\x18\x15\x96\x00\t\x03p\xcc\f\xb3\x02\xb5\x1cD-G\xc0\xf6\x03n\x12\xfaM\xd6i\x19F\xb9\x15\x00\x12ɀ\x13\xcc0\xcbQ\xcbA\xd4r\x04\xecT\xc0\rg\xf0\xdf\xc6!\xdad\xa5\xb1\x9d\xaem'#Ë\x88\x95\xa9\x0e\xb5\\cv\x94dz\xc9\x1cDʭӕ\x893\x94Ι\x18\x8a\v\x9c\xa5\x0e\xf5\\cv\xae\xd2=\x9bg\xe4e\x98\r1\xab\xad2\xd1N霓\xe1E\xc4:W\x1d*\xba\xc6\xec\xf3\x95n\xbe\x86P\xd13\xf0\xfd\xc0j\xafLtP:\x05\x19^D\xac\x8e\xeaP\xd65f\x17*\xdd\"\r\xa1\xb2g\x98\xb51\xab\x8b2\xd1U\xe9t\xcb\xf0\"buW\x87\xf2\xae1\xbb\xa7\xd2\xed\xa5!Tx\\2n\xf5Q&\xfa*\x9d~\xb8<>\x10\xfa\xabC\x85ט=P\xe9\x0e\xd2\x10*<:-\xd6\x10eb\xa8\xd2\x19\x86\x03\x10\xdcpu(\xf3\x1a\xb3G*\xddQ\xbc\xfb\xd1\x19\xc6bk\f\x90\x18\v8\xe32\xcc\xc5(\xf2 j<\x02\xf6D\xc0\x9d\x84~\x9359è\xb5\xa6\x00\x89\xa9\x803\rkEu\aQ\xdc\x11\xb0g\x02\xee,\xf4\x9b\xac\xd9\x19f}\xb35'ìn\xb6\xe6\xe2\xe4fk\x1e\xaf6\x9f\\@.$\x17\x91Ť\x95!y%\x19RX\x9a!\x1d\xcb2\xa4\x9f\x9d!}\x17g\x98}\xfb\xe6\x95gȠX\x86QT\xe4dH\x97\x8a\f\xe9Z\x99!ݪ2\xa4{u\x86\xf4\xa8ɐ\x9en\x86\xf4\xaa͐\xdeu\x19ҧ>\xc3\xecҹ \x9eav\xed\\Аav\xeb\\\x90\xc80\xbbw.h\xcc0{t.h\xca0{v.X\x92a\xf6\xea\\Мa\xf6\xee\\Вa\xf6\xe9\\\xb04ë\u007f\x1d\xb6,#\xa9K\xe7\x8e\xfa\xe7a˹\xa6\x15\xe4Jr\x15\xb9\x9a\xbc\x90\xbc\x88\xbc\x98\xbc\x84\xbc\x94\xbc\x8c\xbc\x9c\xbc\x82\xbc\x92\xbc\x8a\xbc\x9a\\C^C^K^G^O\xde@\xdeH\xdeD\xdeL\xdeB\xdeJ\xdeF\xdeN\xdeA\xdeI\xdeE\xdeM\xdeC\xdeKޗ!\xd6\xfd\x19b\xaf\xcd\x10\xe7\x81\fq\x1f̐\xc4C\x19\xd2\xf8\xb0>\x90\x8fd\xa0\x86?\xaa\xcf\xdfc\xfaT>\xae\x0f\xe1\x13\xfa\x90>\t4>\x95\x81W\xb5\xa7\x01\xfb\x19\xc0y\x16p\x9f\x03\x12\xeb\x80\xc6\xf5\x19x;\xdb\x00\xd8\x1b\x01\xe7y\xc0}\x01H\xbc\b4n\xca\xc0\xdb\xd5f\xc0\xde\x028[\x01\xf7% \xf1r\x06>\xc0^\xc90c5֫\x19\xf8\xf0\x8a\xbd\x86\x80\xfd:\xe0\xbc\x01\xb8o\x02\x89\xb7\x80Ʒ3̺\x84\xf5\x8e\xd2~W鼧t\xdfW&>P6~\x98\x81w\xa7\x8f2$\xbe\r\r\xfbc\xc0\xf9\x04p?\x05\x12\x9f\x01\x8d\x9fg\xa0\x02n\xcf\xc0\a\xfd\x0e\xb4\xec/в\xbfD\xcb\xf9\n-\xe7k\xb4\xdco\x80ķ@\xe3w\x19fY\x95\xb5\x13\x8fJ\x95\xb5Ki\u007f\xaft~P\xba?*\x13?)\x1b\u007f\xe6s\xfa\v\x9f\xc6_\xf9\x9c\xfe\xc6g\xf2w>\xb9\u007f(\x1b\xff\xcc\xc0g\xcb_\x80\xfd7\xe0\xfc\x03\xb8\xff\x02\x89\xff\x80\xc6\xdd\x19\xa8\xe2{\x00{/\xe0\xec\x03\xdc\xfd@\xe2\x00\xd0x\x90\x9bw\x88\x9d3\xbd(GN\x17\xb4\x9d\xae\x99f\xad\xe3tC\xd3펦\xeb\xf6 {f\xeaW\x8b^\x99\x10po\x1c\xb3\xfadj\x05\xeb\v6\xd9\xfd\xc8\xfeJg\x009\x10\x13\xe2F\a\xb5\xba\xc1\x1as\x87\x90C5\xe4\xba\xc3Z\xddp\x8dY#ȑ\x1a\xb2\xacQ\xadnt\xab\x1b\x93)\xb5ca\xe32%6>\x13\x9a\x9b\x90\xe9\xc5˥3\x11mgR\xa6\xd9\xe08\x93\xd1t\xa7\xa0\xe9\xbaS\xc9i\x99\xfa\xb9<=\x13✁c\xd6\xccL}\x1f\x9d\x056ٳ\xc99Jg.9\x0f\x13b\x8d\xf3[\xdd\x02\x8d\xb9\v\xc9E\x1ar\xdd\xe2Vgi\xcc*!K5\x84\x17\xe7Vg\xb7\xba\xc5ؐr\x1cn\xb0cJ\xc7Q\xba\x15J\xab2\x13orU\x99^\xbc\xda9\xd5h;5\x99f\xa5\xe3\xb8h\xba\xb5h\xban\x1dY\x8f\x80\x15\xcf\xd4W\xc0\x86L}\x99K\x90\x8dJ\xa7\x89\\\x82Y\xb0\xd0\xe6Vע1w)\xb9LC\xae\xbb\xbcխИ\xb5\x92\\\x95\xc9\xd7\xc4խ\xee\xc2Vw\x11\xc4s1\xae\xd7t\t\xc6رKuE\xb1\xcbt\x19\xb1\xcbu\x05\xb1+2\xf1B~e\xa6\x17o\xe8\xceUh;Wg\x9a\xa5\x8e\xb3\x06M\xf7\x1a4]\xf7Z\xf2:\x04\xac\xeb3\xf5M\xfe\x06\xb0ɾ\x91\xbcI\xe9\xdcLނY\xb0\xda[[\xddm\x1aso'\xefА\xeb\xde\xd9\xea\xeeҘu7y\x8f\x86,\xeb\xdeVw_\xab\xbb\x1fr^\x8bM\x8d=\x90\x89\x9a\xf8`\xa6\x17_6\x9d\x87\xd0v\x1e\xce4\xab\x1d\xe7\x114\xddG\xd1t\xdd\xc7\xc8\xc7\x11\xb0\x9e\xc8\xd4/\xa5O\x82M\xf6S\xe4\xd3J\xe7\x19\xf2ŶU=\xd7\xea\xd6i\xcc]OnА\xebnlu\xcfk\xccz\x81|QC\x96\xb5\xa9\xd5mnu[2\xa5z+\xec%\\3\xf62\x1a\xaf\xc0^\xcdD]~-Ӌ\xef\x17\xce\xeb\x99Z\x9e3\xcd\x1a\xc7y\x13M\xf7-4]\xf7m\xf2\x1d\x04\xacw3\xf5{\xc8{`\x93\xfd>\xf9\x81\xd2\xfa\x90\xfc\b\xb3\xe02\xdbZ\xddǭ\xee\x13<\xee\x9f\xe2\xdc\xf2π\xd8\xe7\x99\xf8 ٞ\xe9\xc57|g\a\xda\xce\x17\x99f\x95\xe3|\x89\xa6\xfb\x15\x9a\xae\xfb5\xf9\r\x02ַ\x99\xfaK\xc0w`\x93\xbd\x93ܥt\xbe'\u007f\xc0,\xc8Ǐ\xad\xee'\x8d\xb9?\x93\xbfh\xc8u\u007fmu\xbfi\xcc\xfa\x9d\xfcCC\x96\xf5g\xab\xfb\v5\xe7o\xd8?\xb0\u007fQ\xa6\xec\xff2\xbdx\x05sv\xa3\xed\xec\xc94㎳\x17Mw\x1f\x9a\xae\xbb\x9f<\x80\x80u0S\xbf\xbb\x1f\x02\x9b\xec\xc3\xe4\x11\xa5s\x94<\x86Y\xb0\xa2\xe3\xad\xee\x84\xc6ܓ\xe4)\r\xb9\xee\xffZ\x9d'K\xbf\xeb\vid!dYf\xab\xf3\xb6:_\x96\xc4\xfd\xb0\xa4,#\xdet\x1a\x1a\x014*\x93qBeS\x10\xcdY)\x18\x87*\x14\x82\xc3\x13\x9e\n\x87\a'\f\a)F\xb2\xf47\x824\xa5\x93\x8eP\xccq2\xb4\xe3fj\aE];V4\x8b\xbf+d\xabk\xb2sZ\xdd\xe9t\xce\x19\xad\xee\xcc,\x9f:\xe7\xac\xff\xf7m\x18vs[\xddٌ\xban\xde\xff\xfb\xb6\f[\xedZ\xdd9\x8c\xe2u\xfb\xff\xfdyXu\xec\xfc,|\x18\xe6gy\xf1mƹ\x00m\xa7}\x96\xd9\xec8\x1d\xd0t\v\xd0tݎd'\x04\xac\xc2,\xfd\xd6S\x046ٝ\xc9.J\xa7+\xd9\r\xb3`I\xdd[]\x0f\x8d\xb9=\xc9^\x1ar\xddޭ\xae\x8fƬ\xbed?\r\xe1E\xbb\xd5\rhu\x03\xb3\xa4y\x10l0l\x88\xaeih\x96Qo\x0fS\fςdGdy\xeb\xb1ܑ\b8\xa3\xb2\xccz\xc7\x19\x8d\xa6;\x06M\xd7\x1d\x8b\x11\xee8\xf4\xad\xf1Y\xfa\xfb\xcf\x04\xf4\xad\x898\x03\x13Oju\x93\xb3\xa4~\nl*lZ\x16\u07bd\xa7gy\xf12\xee\xcc@ۙ\x99e.v\x9cYh\xba\xb3\xd1t\xdd9hZs\xb3\xf4u}\x1e\xc6\xe9o&\xadnA\x96,^\x98\xa5\xdfY\x16\x91\xc5J\xc7\"K\xb2\xf0\xfd\xc5qJ[]\x99\xc6\\[;\xae\xbbX;Vy\x16\xbf\xfaIJ|\xfa=\xc7r\xfe\xdfWd\xa1\\U\xe2\x18櫢s\xaa\xe9\xdc\x1a:\xcb\xcd\xd2\xff\x1c[\v6\xdbuJ\xa7^\xe9ƕVC\x16^\x1c\x13\x80Әe\xe2\xc2Mh\xbaK\xd0t\xddf4\xad\x96,\xfd\xe5oi\x96Ė\xc1\x96#T\xb9\"\xcb[\x87\xeb\xaf\xcc2\xea\x9cUYf\x9d\xe3\xacFӽ\x10M\u05fd\bM\xeb\xe2,\xfd\xf9\xea\x92,\xa9\xbb\x14k\xa8\x8aٗ\xd19\x97ӹW\xd0YW\xea\xf6\\\xa5[r\xb5n\xc6\x1a݁k\xb2|5\xf8F\xe8\\\x9be֔;\xd7eyA\xe7z\xed\xb87h\xc7uoԎu\x93v,\xeb\xe6,T\xa6[\xb2\xf4\x8f9o\xe5\x03s\x1b\x1f\x98\xdb\xf9\xa4\xdc\xc1G\xe4Nf\xfa.\xe6\xf8nf\xf5\x1e\xa6\xf3^\xcd\xdb}Y&\x96u\u007f\x96\x17t\xd6j\xc7}@;\xae\xfb\xa0v\xac\x87\xb4cY\x0fC\x11\xb1G8ѣ\x9c\xe8\xb1\xd6]z\x9c\xf3=ѺKOrڧZw\xe9i\x9d\xfd\x99,)y6K\xec\xe7\xb2\xc4Y\x97%\xee\xfa,\xb16d\xe1\xbdm#\xe0<\x0f\xb8/\x00\u058b\xb8\v{\x13\xe0l\x06\xdc-\x80\xb55\v\x1f\xe9/\x01\xceˀ\xfb\n`\xbd\xaa{\xf5\x9a\xee\xd5\xeb\xbaKo\xe8\xfe\xbc\x99e\x96\xc7\x12o)ݷ\x95\xf6;Y^\xd0~W;\xce{J\xeb}\xa46\xf1\x81&\xf9C\xcd\xf9GY\xfa\x03\xe76\xcd\xfcǚ\xf4O\xb0\x88ħ\xba\x9c\xcftu\x9fg\xe9\xef*\xdbu\x8d;ty_@\x02M\x89/\x95\xeeWJ\xfb\xeb,\xaf\xfe\x02\xf8\x8dv\x9co\x95\xd6wz\xf7\x89\x9dL\xc8.f\xea\xfb,~G\xff\x81\t\xfb\x91\xc9\xf9I\xa5\x98\xf8Y\xe9\xfe\xa2\xb4\u007fU\x8d\xda\xf6o\xdaq~WZ\u007fd\xe1k쟀\xfb\x17`\xff\x9d\xa5\xdfb\xffA\xd3\xf9\x17\xb0\xfe˒\xda\xddY\xf8\xf2\xb3\ap\xf7\x02\xf6\xbe,\xfd\x81b?\x9a\xce\x01\xc0:\x98eV\xc6\x12\x87\x94\xeea\xa5}$\xcb\v\xdaG\xb5\xe3\x1cSZ\xc7\xc1\xca\xc4\t\xa5{Ri\x9f\u00a0J\xdb\xfe\x9fv\x1cO\x14\xb4$jTV\x1aQ|\xd71\x01\xd7\vؾ\xa8\xfe\x8c\xe7G\xd3I\x02\xacӢ\xf8\xbe\x14\x00\xdcd\xc0\x0eF\xf5\xb7\xe9\x144\x9d\x10`\xa5F\xf1e!\f\xb8\x11\xc0N\x8b\xea\x0f@\xe9h:\x19\x80\x95\x195*\x12Y\x8a(\xe0f\x03vNT\u007f\x9b9\x1dM\xe7\fř\x80u\x16\x82\x15\x896J7Wi\x9f\x1d\xf5\x82v\x9ev\x9c\xb6J\xab]\x14\xdf\xec\xce\x01\xdcs\x01\xfb\xbc\xa8\xfe\n\u007f>\x9aN>`]\x10\xc5\x17\xa7D{\xa5\xdbAi\x17D\xbd\xa0\xddQ;N'\xa5U\x88\x93b\x89\"\xa5\xdbYiw\x89\xe2)\xb0\xed\xae\xdaq\xba)\xad\xeeQ}\x84\x13=\xe8ܞtv\xaf\xa8O\x9dݛ]\xa7\x0f\x9d\xd57\xaa\xdf\xc7\xfa)\xdd\xfeJ{\x80\x1e\xb0\xed\x81\xdaq\x06)\xad\xc1Q\xa3%1\x04p\x87\x02\xf6\xb0\xa8\xfe\b7\x1cMg\x04`\x8d\x8cJ\xcb(\xd8h\xac\xb0%1F\xe9\x8eU\xda\xe3p\x03\x18:^;\xce\x04\xa551\x8ao\x95\x93\x00w2`O\x89\xeao\xf4S\xd1t\xa6\x01\xd6\xf4\xa8\xfe.?C\xe9\xceTڳ\xa2\xfcY~\xb6v\x9c9Jk.F\xc6\xe6E\xf5[\xe2|\xa5\xbb@i/\x8c\xf2\xe7\x9dE\xdaq\x8a\x95\x96\x15՟\x9aJ\x94n\xa9\xd2.\x8b\xf2\x97&[;\xceb\xa5U\x8e\xf30U\x8c\xceu\xe8\xec\n\xa4Kg\xabdש\xa2\xb3\xaa\xa32\xbb&*\x85n\x145\u0529\x8dj\x9ds\xea\xe0P\t\xeb\xa3>uN\x9c]\xb7\x81]\xd7M\xb0k5\xb2kYMQ\xfe\x8d\x05\xd9L\xb6\x90K\xc9e\xe4rr\x05\xb9\x92\\\x15\x95\xbc\xd5Q)\xb90*U\x17Ee\xf1\xc5Q\x89_\x12\x95\x9aK\xa3\x12\xbb,*e\x97G\xa5ከ\x94^\x19\x95\xba\xab\xa2R}uT\xca\xd7@\xff\xe5\xd7`_\xae\x8dJ\xfduQ\xb1\xae\x8f\x8a{CT\x127Fž)*\xce\xcdQ\xa34vKT\xff85j\xd4\xc6n\x8bJ\xed\xedQYr\a\xfc\x9dQ\xe9\u007fWT\x06\xdeͫ\xdfC\xdeKއ\xab\xdf\x1f\x95Ƶ\x98\xe8\x01<#\xf5\x0fF\xa5\xf9\xa1\xa84=\xac\x13=\xa2x\x14\xd7z\f\xd7z\xb9\x80\\H.\"\x8bI\x8b,!K\xc92\xd2&\x17\x93\xe5d\x8ct\xc8\n\xb2\x92\xac\"\xab\xc9\x1a\xd2%k\xc9:\xb2\x9e\x8c\x93\rd\x82l$\x9b\xc8%d3\xd9B.%\x97\x91\xcb\xc9\x15\xe4JrU\xb6T\xae\xce6*c\x17fK\xf9E\xd9Fy\xec\xe2l<×dK\xe9\xa5\xd9\xfa\u007f\x12\x99-\x15\x97g\x1b\x15\xb1+\x10n\xbe2[\x1a\xae\xca\xd6w\U000abce5lM6\xff?\"q\xa0\xe6Z\x1c\xb8.\x1b\xd5\xedz\xc4o\xc86\xcab7fK\xcdM\xd9R{s6\x8a\xe7-\xd9Rrk\xb6Q\x12\xbb-[\xaaoϖ\xe6;\xb2\xa5\xfe\xcel\xa9\xba+[\x1a\xef\xce\xd6\xff+8 ~o\xb6\xc4\xef˖\xd8\xfd8\xb26[\xea\x1e\xc8\x16\xeb\xc1lò\x1e\xca\x16\xe7\xe1l\xc3q\x1eɖģ\xd9F\xa2\xf21 \xf1x\xb6\x99H4>\x91m\xd47?\x99m\xd677?\x95mT5?\x9dmV57?\x93-\xf6\xb38\xd7y.\xdbp\xddu\x8a\xf5\xe8&6`\u038d:\xe7\xf3\x8a\x170\xf1\x8b:\xf1&,js\xb6\xd1ܼ\x05Wت\x93\xbf\x04X/\xe3<\xfb\x15ĭW1\xca~\r\x93\xbe\x8e\xf3\xec7t\xe67uҷtҷ\xb3e\xc2;\xd92\xfa]t\x8a?Ľm˖\xb6۳\xa5pG6\xff6\xeb\x8bl\xe9\xfbe\xb6\x14|\x95m\x14\x14|\x9d\xad\u007f\xea\xfeMv\xeb\xbf&\xf96\xdb\x187\xf5\xbbl\xc9\xdf\xc9\xfd\xd8E~O\xfe\x90-\x9d\u007f̖.?eKן\xb3\xa5\xdb/\xd9\xd2\xfd\xd7l\xe9\xf1[\xb6\xf4\xfc=[z\xfd\x91-\xbd\xff̖>\u007fq\xf4\xdf\xe4?\xe4\xbf\xe4\u007f\xe4nr\x0f\xb9\x97\xdcG\xee'\x0f\x90\a\xc9C\xe4a\xf2\by\x94\x1b#\xe7\xe0\x92ss\x8c\x8a\xe6y\x98\xa7y~\x8eQֲ\x00WZ\x88+-\xca1\x9c\xe6b\\\xc2\xc2\xe5J\xf42\xa58\xa9\f'\xd98i1.W\xae7\x1a\xc3u\x1c\\\xa7\x02\x97\xa8\xc4}Ua@5\x06\xd4\xe8\xe5\\\xdcU-n\xbaN\x17Q\xaf7\x14\xc7\xcc\r\x98'\x81h#\x867ip\t\xb3\xdfL\xb6\x90K\xc9e\xe4rr\x05\xb9\x92\\E\xaeΑ\xd1\x17\xe6\xc8\xec\x8brd\xd6\xc592\xe1\x12(\xa8[ޥPP\xb7n\xdd\xf2.\xc3^^\x8e\x8b]\x81\x8b]\xa9\xab\xbf*GJ\xafƢ\x1a\xd6\xe4h\xe5\xb9\x06\xf7Zv-\x0e\xd4\\\x87;\xb8\x1ewp\x03n\xe9\xc6\x1c\x94\x99\x9bp_\xf1\x9bua\xb7`\xfd\xb7b\xa2\xdb0\xe4v\rܑ\x83\xd2v\xa7Nw\x17\xa6\xbb;G\xffA\xb9Ngݛ\xa3E\xec>\x8c\xbb\x1f\x13\xc7\xd6\xea\x14\x0f\u0b9b\x1f\xd4\xd3\x1e\xc2<\x0f#\x8d\x8f \xfd\x8f栀=\x96#\xf9\x8f\xf3.\x9e \x9f$\x9f\"\x9f&\x9f!\x9f%\x9f#ב\xeb\xc9\r\xe4F\xf2y\xf2\x05\xf2Er\x13\xb9\x99\xdcBn%_\"_&_!_%_#_'\xdf \xdf$\xdf\"\xdf&\xdf!\xdf%\xdf#\xdf'? ?$?\"\xb7\x91\x1f\x93\x9f\x90\x9f\x92\x9f\x91\x9f\x93\xdb\xc9\x1d\xe4\x17\xe4\x97\xe4W\xe4\xd7\xe47\xe4\xb7\xe4w\xe4Nr\x17\xf9=\xf9\x03\xf9#\xf9\x13\xf93\xf9\v\xf9+\xf9\x1b\xf9;\xf9\a\xf9'\xf9\x17\xf97\xf9\x0f\xf9/\xf9\x1f\xb9\x9b\xdcC\xee%\xf7\x91\xfb\xc9\x03\xe4A\xf2\x10y\x98\x04\x9f\n\x1f\x86\x8f\xc0\xa7\xc1\xa7\xc3g\xc0g\xe6ʈ\xac\\\xb1\xa3\xf0\xd9\xf09\xf0\xa7ß\x01\u007f&\xfcY\xf0m\xe0sse\xe4ٹ\xb28/WF\xb5͕\xf2v\xb92\xfa\x9c\\\x89\x9d\v\u007f\x1e\xfc\xf9\xf0\xf9\xf0\x17\xc0\xb7\x87\xef\x00_\x00\xdf1W\xc6t\xca\x15\xa7\x10\xbe\b\xbes\xae\x8c\xeb\x92+\x95]\xe1\xbb\xc1w\x87\xef\x01\xdf3W\xc6\xf7ʕ\xaa\xde\xf0}\xe0\xfb\xc2\xf7\x83\xef\x0f?\x00~`\xaeL\x18\x94+Ճ\xe1\x87\xc0\x0f\x85\x1f\x06?t\x01t!t\x11t1t\tt)t\x19t9t\x05t%t\x15t5t\rt-\xdcm\x1d\xdcm=t\x03t#\xaa{\x13\xaa{3t\vt+t\x1bt;t\at't\x17t7t\x0ft/t\x1ft?\xf4\x00\xf4 \xf4\x10\xf40\xf4\b\xf4(\xf4\x18\xf48\xf4\x04\xf4$\xaa\xeb\x14\xaa\xeb4\xf4\f\xf4,\xf4\x1c\xf4<\xf4\x02\xf4\"\xf4\x12\xf42\xf4\n\xf4*\xf4\x1a\xf4:\xaa\xe5\x06\x8a\xfb&\xf4\x16\xf46\xf4\x0e\xf4.\xf4\x1e\xf4>K\xfb\x01\xf9\x90|D>&\x9f(\xa5\x855\x98\tf\x86%\xc0\x12aE`Ea\xc5Ji\xf5\x8b\xc3J\xc0\x9e\x84=\x05{\x1a\xf6\f\xecY\xd8s\xa5\xb4\xc8\xf3\xb0\x17`/\xc2^\x82\xbd\f{\xa5\x14\x97\x9f\xe4k\xa5\xd0!\xc0ހ\xbd\t{\v\xf66\xac$\xaf\x96\"u<\x93\x04+\r+\x03K\x86\x95\x85\xbd\x03+\a\xb3\xe0\xfe\x14XyX\x05XEX%XeX\x15\x98\xb5\x94\x16\xb5\xc1RavXUX5XuX\r\x98\xa3\x94ּ&\xec]X-XmX\x1dX]X=\xd8{\xa5\xb4X}X\x03XCX#XcX\x13\xa6\xae)٬\x14fY\xb0\x16\xb0\x96\xb0V\xb04\x98\x93W]\xa4\xbb\x94\x16\xf7\xc0\xbc0\x1f\xcc\x0f\v\xc0\x82\xb0ְ6\xbc\xabm)-Ў\xa1\xf6\bu`(\x84PG\x86:!\xd4\x19\xef\x0eú\xc0\xd2a\x19\xb0\b\xac+,\x13\x96\x85\xf7Fa\xdd`\xddaٰ\x1cX.,\x06끺{\x1f\x96\x87\xb2ˇ\x15\xc0Ⱎ(\x8b^\xb0ވ\xa3\x0f\xac/\xd2\xd4\x0f\xd6\x1f\xe1\x01\xb0\x81L\xc1 \xf2\x03<\xff!l0l\bl(l\x18l8l\x04l$\xea\u007f\x14l4\xec#\xd8ǰO`\x9f\xc2>\x83}\x8e\xf7}\x01\xfb\x12\xf6\x15\xeck\xd87\xb0oa\xdf\xc1\xbeG\xbd\xfd\x00\xfb\x11\xf6\x13\xecg\xd8/\xb0_a\xbf\xc1~Gz\xfe\x80\xfd\t\x1b\x03\x1b\v\xfb\v6\x0e6\x1e6\x01\xf9\xfc\x1b6\x11\xf6\x0fl\x12l2l\nl*l\x1a\xd28\x1d6\x036\x136\v6\x9b9\x9b\x83п\xb0\xb9H\xe5<\xd8|\xd8\x02\xd8BآRZ\xb9\xc5(\xa1%Х\xa5\xb4\x81\xcbJ\x99J\x0f\\\x8e䮀\xad\x84\xadb\x14\xab\x11Z\x03[\x8b\x84\xae\x83\xad\x87m\x80m\x84m*e*\xd7y3Pn\v0p+b\xdb\x06\xdb\x0e\xdb\x01\xdb\xc9\bv\x91\xbbq\xbc\a\xb6\x17>\xb8\x0f\xb6\x1fv\x00v\x90W\x0f\x952u.wX\xe1\b0\xf0(*\xea\x18\xec8\xec\x04\xecd)\xcd{\nv\x1a\xe13\xb0\xb3p\x9cs\xb0\xf3\xb0\v\xb0\x8b\xb8v\t\xc9\xef|\x19(w\xa5\x94\xd6\xf9*\xa3\xbdF^G\x11ހ݄\xdd\xe2\x99\xdb\b݁\xddE\xe1݃݇=\x80=\x84=Bq\xb9\x80\\H.\"\x17\x93Kȥ\xe42r9\xb9\x82\\I\xae\"W\x93kȵ\xe4:r=\xb9\x81\xdcHn\"7\x93[ȓ\xe4)\xf24y\x86IkY!I\xab\x9f\x9a\xa45\xa9\x95\xc4}^\xb2\x0eY\x97\xacG\xbeG\xd6'\x1b\x90\r\xc9Fdc\xb2\tٔlF6'[\x90-\xc9Vd\x1a\xe9\xc4[]\f\xb9I\x0f\xe9%}I\x89\xba\xad\x8a]\xf7CS\xa1\x01\x1eWӃ<\xae\xa6\xb7\x86ڡm\xa0U\xa1my\xbd\xba\xde\x0eZ\rڞ\xc7\x0e\xbd\x03\xefs\xe8!\x9ew\xe8\x1d\xa15\xa0\x9d\x92̸\xde9Ik\x1eN25o\xde%\xc9ܼy\xf3t\x04\x83\x19IZ0\x92d\n6\xef\x9ad\x0e6o\x9e\x99\x94\x006\xcf¥6\xd1$\xadM\xb7$S\x9b\xe6ݓ\xccm\x9a7\xcfN\xd2Z\xe5$i\rs\x93\xb4F\xb1$-\xadG\x92\x16}?\xc9\x14\x8d\xe6%\x99\xa3\xd1h>\x82=\v\x92\xb4\x9e\xf1$S\xcfh\xcf$s\xcfh\xb4WR\x02\x18\xed\x8dK\xbd\xfb$i\xbd\xfb&\x99zG\xfb%\x99{G\xa3\xfd\x93\xb4\xec\x01IZ\xfa\xc0$-cP\x92\x96\xf3A\x92\xc9\xd2\xe8\xc3$S\xa3\x94\xc1I\x89\x16K\xff\x94\x94!IZ\xcaP\x96\xce0r89\x82\x1cI\x8e\"G\x93\x1f\x91\x1f\x93\x9f\x90\x9f\x92\x9f\x91\x9f'i\x95\xbeH\xd2\xfa\u007f\t\xfd\n\xfa5\xf4\x1b\xe8\xb7IZ\x87\uf4b4*\xdfC\u007f\x80\xfe\x88\xf3?\xc1~N\xd2\x06\xfe\x02\xfb\x15\xe1\xdfp\xdf\xef\xd0?\xa0\u007fB\xc7\xc0\xc6\xc2\xfe\xc2\xf18\xe8x\xe8\x04\xd8\xdf\bO\x84\xfd\x03\x9b\x04\x9b\f\x9b\x02\x9b\x8ak\xd3`\xd3a3`3a\xb3`\xb3as\x92\xb4\x8e\xff\xa2\x06\xe6\xe2\xfd\xf3\x92\xb4:\xf3\x11^\x80s\v\xf1\xdc\"\xd8bܳ\x04\xb6\x14\xe1e\xb0\xe5\b\xaf\x80\xadĽ\xab\xa0\xab\xa1k`k\x11^\a]\x0f\xdd\x00\xdd\b\xdb\x04\xdb\f\xdb\x02\xdb\nۆkۡ;\xa0;\xa1\xbb\xa0\xbb\xf1\xde=\xc8\xf7^\xe8>\xe8~\x9c?\x00;\x88\xbc\x1f\x82\x1d\xc6=G`G\xf1\xeec\xd0\xe3\xd0\x13Г\xd0S\xb0Ӱ38>\v=\a=\x0f\xbb\x00\xbb\b\xbb\x04\xbb\f\xbb\x02\xbb\x8a\xeb\xd7\xf0\x8e\xeb\xb0\x1b8\xbe\xc9Z\xb9E\xde&\xef\x90w\xc9{\xe4}\xf2\x01\xf9\x90|D>&\x9f(\xcd/a\xa4\x894\x93\td\"Y\x84,J\x16#\x8b\x93%\xc8'ɧȧ\xc9g\xc8g\xc9\xe7\xc8\xe7\xc9\x17\xc8\x17ɗȗ\xc9W\xc8W\xc9\xd7\xc8\xd7\xc97\xc87ɷȷɒd)R'\x93\xc8\xd2d\x192\x99,K\xbeC\x96#-d\nY\x9e\xac@V$+\x91\x95\xc9*\xa4\x95\xb4\x91\xa9\xa4\x9d\xacJV#\xab\x935H\aY\x93|\x97\xacE\xd6&\xeb\x90u\xc9z\xe4{d}\xb2\x01ِlD6&\x9b\x90M\xc9fds\xb2\x05ْlE\xa6\x91N\xd2E\xbaI\x0f\xe9%}\xa4\x9f\f\x90A\xb25نlK\xb6#ۓ\x1d\xc8\x10ّ\xecDv&\xc3d\x172\x9d\xcc #dW2\x93\xcc\"\xa3d7\xb2;\x99M搹d\x8c\xecA\xbeO\xe6\x91\xf9d\x01\x19'{\x92\xbd\xc8\xded\x1f\xb2/ُ\xecO\x0e \a\x92\x83\xc8\x0f\xc8\x0f\xc9\xc1\xe4\x10r(9\x8c\x1cN\x8e G\x92\xa3\xc8\xd1\xe4G\xe4\xc7\xe4'\xe4\xa7\xe4g\xe4\xe7\xe4\x17\xe4\x97\xe4W\xe4\xd7\xe47\xe4\xb7\xe4w\xe4\xf7\xe4\x0f\xe4\x8f\xe4O\xe4\xcf\xe4/\xe4\xaf\xe4o\xe4\xef\xe4\x1f\xe4\x9f\xe4\x18r,\xf9\x179\x8e\x1cON \xff&'\x92\xff\x90\x93\xc8\xc9\xe4\x14r*9\x8d\x9cN\xce g\x92\xb3\xc8\xd9\xe4\x1c\xf2_r.9\x8f\x9cO. \x17\x92\x8b\xc8\xc5\xe4\x12r)\xb9\x8c\\N\xae W\x92\xab\xc8\xd5\xe4\x1ar-\xb9\x8e\\On 7\x92\x9b\xc8\xcd\xe4\x16r+\xb9\x8d\xdcN\xee w\x92\xbb\xc8\xdd\xe4\x1er/\xb9\x8f\xdcO\x1e \x0f\x92\x87\xc8\xc3\xe4\x11\xf2(y\x8c \x1f\x92\x8f\xc8\xc7\xe4\x13e\xd8\xf3\x93&\xd2L&\x90\x89d\x11\xb2(Y\x8c,N\x96 \x9f$\x9f\"\x9f&\x9f!\x9f%\x9f#\x9f'_ _$_\"_&_!_%_#_'\xdf \xdf$\xdf\"\xdf&K\x92\xa5H\x9dL\"K\x93e\xc8d\xb2,\xf9\x0eY\x8e\xb4\x90)dy\xb2\x02Y\x91\xacDV&\xab\x90V\xd2F\xa6\x92v\xb2*Y\x8d\xacN\xd6 \x1ddM\xf2]\xb2\x16Y\x9b\xacC\xd6%\xeb\x91\xef\x91\xf5\xc9\x06dC\xb2\x11٘lB6%\x9b\x91\xcd\xc9\x16dK\xb2\x15\x99F:I\x17\xe9&=\xa4\x97\xf4\x91~2@\x06\xc9\xd6d\x1b\xb2-َlOv CdG\xb2\x13ٙ\f\x93]\xc8t2\x83\x8c\x90]\xc9L2\x8b\x8c\x92\xdd\xc8\xeed6\x99C\xe6\x921\xb2\a\xf9>\x99G\xe6\x93\x05d\x9c\xecI\xf6\"{\x93}Ⱦd?\xb2?9\x80\x1cH\x0e\"? ?$\a\x93Cȡ\xe40r89\x82\x1cI\x8e\"G\x93\x1f\x91\x1f\x93\x9f\x90\x9f\x92\x9f\x91\x9f\x93_\x90_\x92_\x91_\x93ߐߒߑߓ?\x90?\x92?\x91?\x93\xbf\x90\xbf\x92\xbf\x91\xbf\x93\u007f\x90\u007f\x92cȱ\xe4_\xe48r<9\x81\xfc\x9b\x9cH\xfeCN\"'\x93Sȩ\xe44r:9\x83\x9cI\xce\"g\x93s\xc8\u007fɹ\xe4\xb9\x80\\H.\"\x17\x93Kȥ\xe42r9\xb9\x82\\I\xae\"W\x93kȵ\xe4:r=\xb9\x81\xdcHn\"7\x93[ȭ\xe46r;\xb9\x83\xdcI\xee\"w\x93{Ƚ\xe4>r?y\x80\xf9\x80|H>\"\x1fK\u007f\x98\xcc\xfe\x90L!˓\x15Ȋd%\xb22Y\x85\xb4\x9262\x95\xb4\x93U\xc9jdu\xb2\x06\xe9 k\x92\uf4b5\xc8\xdad\x1d\xb2.Y\x8flI\xb6\"\xd3H'\xe9\"ݤ\x87\xf4\x92>\xd2O\x06\xc8 ٚlC\xb6%ۑ\xed\xc9\x0ed\x88\xecHv\"\xb7&k\xe1m\xc9Z\x97\xed\xc9Z\xfa\x8ed-cg\xb2\x16ٕ\xacuݝ\xace\xeeIֲ\xf6&k\xd1}\xc9Z\xb7\xfd\xc9Z\xf7\x03\xc9Z\xf6\xc1d-\xe7P\xb2\x96{8Y\x8b\x1dI\xd6z\x1cM\xd6\xde?\x96\xac\xe5\x1dO\xd6\xf2O$k\x05'\x93\xb5\xf8\xa9d\xad\xe7\xe9d\xadיd\xad\xf7\xd9d\xadϹd\xad\xef\xf9d\xcdz\x81o\xbcH^\"/\x93Wȫ\xe45\xf2:y\x83\xbcI\xde\"o\x93wȻ\xe4=\xf2>\xf9\x80|H>\"\x1f\x93O\x94\xd5*i0SY\xad\xbf\x19\x96\x80p\"\xac\b\xc2Ea\xc5\x10.\x0e+\x81\U00013c27\xcaj\x15\x9e\x86=\x03{\x16\xf6\x1c\xecy\xd8\v\xb0\x17a/\xc1^\x86\xbd\x02{\x15\xf6\x1a\xecu\xd8\x1b\xb07ao\xc1ކ\x95\x84\x95\x82\xe9\xb0$XiX\x19X2\xac,\xec\x1dX9\x98\x05\x96\x02+\x0f\xab\x00\xab\b\xab\x04\xab\f\xab\x02\xb3\xc2l\xb0T\x98\x1dV\x15V\rV\x1dV\x03\xe6\x80Մ\xbd\v\xab\x05\xab\r\xab\x03\xab\v\xab\a{\x0fV\x1f\xd6\x00\xd6\x10\xd6\b\xd6\x18\xd6\x04\xd6\x14\xd6\f\xd6\x1c\xd6\x02\xd6\x12\xd6\ne\x91\x06s\xa2,\\07\xc2\x1e\xa8\x17\xd7|0?,\x00\v\xc2Z\xc3\xda\xc0\xda\xc2\xda\xc1\xda\xc3:\xc0B\xb0\x8e\xb0N\xb0ΰ0\xac\v,\x1d\x96\x01\x8b\xc0\xba\xc22aY\xb0(\xac\x1b\xac;,\x1b\x96\x03˅\xc5`=`\xef\x97ժ\xe4\x95\xd5:\xe4\x97\xd5\xda\x14 =q\xa4\xa7'\xb4\x17\xb47\xb4\x0f\xb4/\xb4\x1f\xb4?t\x00t t\x10\xf4\x83\xb2Z\x99\x0fa\x83aC`Ca\xc3`\xc3a#`#a\xa3`\xa3a\x1f\xc1>\x86}\x02\xfb\x14\xf6\x19\xecs\xd8\x17\xb0/a_\xc1\xbeF\xbc\xdf \xdeo\xcbr\x14#\xbf'\u007f \u007f$\u007f\"\u007f&\u007f!\u007f%\u007f#\u007fG<\u007f\xc0\xfe\x84\x8d\x81\x8d\x85\xfd\x05\x1b\a\x1b\x0f\x9b\x00\xfb\x1b6\x11\xf6\x0fl\x12l2l\nl*l\x1al:lFY\xad\xe3L\xd8,\xd8l\u061c\xb2Z\xdd\u007fasa\xf3`\xf3a\v`\v\xcbj\xc1E\xb0Ű%\xb0\xa5e\xb5\xda\xcb`\xcba+`+a\xab`\xab\xcbj\xe5\xd7\xc0\xd6\xc2\xd6\xc1\xd6\xc36\xc06\xc26\xc16ö\xc0\xb6¶\xc1\xb6\xc3v\xc0v\xc2v\xc1v\xc3\xf6\xc0\xf6\xc2\xf6\xc1\xf6\xc3\x0e\xc0\x0e\xc2\x0e\xc1\x0eÎ\xc0\x8e\u008e\xc1\x8e\xc3N\xc0N\xc2N\xc1N\xc3\xce _ga\xe7`\xe7a\x17`\x17\x91\xb7K\xb0˰+e5\xd7U\x9c\xbb\x06\xbb\x0e\xbb\x01\xbb\t\xbb\x05\xbb\r\xbb\x03\xbb\xcbR\xbeG\xde'\x1f\x90\x0f\xc9G\xe4c\xf2\xa5w8/'_!_%_#\xf3\xc8|\xb2\x80\x8c\x93=\xc9^do\xb2\x0fٗ\xecG\xf6'\a\x90\x03\xc9A\xe4\a\xe4\x87\xe4`r\b9\x94\x1cF\x0e'G\x90#\xc9Q\xe4h\xf2#\xf2c\xf2\x13\xf2S\xf23\xf2s\xf2\v\xf2K\xf2+\xf2k\xf2\x1b\xf2[\xf2;\xf2{\xf2\a\xf2G\xf2'\xf2g\xf2\x17\xf2W\xf27\xf2w\xf2\x0f\xf2Or\f9\x96\xfc\x8b\x1cG\x8e''\x90\u007f\x93\x13\xc9\u007f\xc8I\xe4dr\n9\x95\x9cFN'g\x903\xc9Y\xe4lr\x0e\xf9/9\x97\x9cG\xce'\x17\x90\v\xc9E\xe4br\t\xb9\x94\\F.'W\x90+\xc9U\xe4jr\r\xb9\x96\\G\xae'7\x90\x1b\xc9M\xe4fr\v\xb9\x95\xdcFn'w\x90;\xc9]\xe4nr\x0f\xb9\x97\xdcG\xee'\x0f\x90\a\xc9C\xe4a\xf2\by\x94 \x1f\x92\x8f\xc8\xc7\xe4\x13\x16M\xd7,Z\xd8d\xd1lf\x8b\xd6%\xc1\xa2\x95K\xb4h\u074bX\xb4Ԣ\x16-\xbb\x98E{\xaf\xb8EK/aѢOZ\xb4\xaeOY\xb4*O[\xb4\x9cg,Z\xfe\xb3\x16\xad\xc7s\x16\xad\xf4\xf3\x16-\xf2\x82E\xb3\xbfhѲ^\xb2h5_\xb6h\xb1W,Z\xf5W-Z\xdek\x16\xad\xe3\xeb\x16-\xe3\r\x8b\xd6\xedM\x8b\x96\xf9\x96E\xab\xfb\xb6E\xcb-i\xd1\nJY\xb4\xf7u\x8bV1ɢ\x95/mѪ\x95\xb1h\xb5\x93-Z\xa5\xb2\x16-\xfe\x8eEs\x94\xb3h=-\x16\xadr\x8aE+[ޢ\xb5\xaf`ђ+Z0C\xb2h\xbd+[\xb4\xa4*\x16\xed\x1d\xabE\xabe\xb3h\xef\xa6Z\xb4\xaav\x8b֡\xaaE\xb3V\xb3h}\xab[\xb4\x1a5,\x9a\xc5a\xd1:մh\xf5\u07b5h\xbdjY\xb4Pm\x8bV\xa6\x8eE\xebSע\xa5Գhu\u07b3$\xb4Ϩ\x11\xaaoIl\x9fa\xab\x11j\xa04\xb5F\xa8\xa1\xa5\b\x8e\x11h\xa4N\xd8k\x84\x1a\xf3\x04\x02MT \x15\x81\xa6\x96\xa2\xea\x16\x84\x9a\xa9{\xaa\xd6\b5\xe7=\b\xb4\xe0=\b\xb4\x94{\x10j\xa5N\xd9\x11H\xe3)\x15r\xaaP\xaa\n\xb9,\xc5\x18\x15\x82n\x15W\xb5\x1a!\x0f\xe3B\xc0˸\x10\xf0I\\\b\xf9\x19\x17\x02\x01\x89\v\xa1\xa0ąPk#.\x04ۨ\xfb\xaa\"Ж\xf7\xa9P;ާB\xed\xe5>\x15\xec\xa0N\xdaU(ē\fvT\xc1T\x06;Y\x8aK\xf2\x10\xee\xac\xd2W\xbdF(\xcc\xf4!Ѕ\xe9C ]҇P\x06Ӈ@D҇PWI\x1fB\x99F\xfa\x10\xccb\xfa\x10\x88J\xfa\x10\xea&\xe9C\xa8\xbb\x91>\x04\xb3%}\b\xe5\x18\xe9C0\xd7H\x1f\x82\xb1\xc2\xf4!\xdcC\xc5Y\r\x81\xf7\x19\xa7\n\xe51N\x15ʗ8U\xb0\x80q\xaaP\\\xe2T\xc1\x9e\x12\xa7\n\xf62\xe2T\xe1\xde\xeaު*ԇ\xf72ؗ\xf72\xd8O\xeee\xb8\xbf:mgp\x00OKx\xa0\n\xa7Jx\x90\xa5\x84Q\x9c8\xf8@\xf9\x9f#\xf4!\xfd\xcf\x11\x1aL\xffs\x84\x86\x88\xff9BC\xe9\u007f\x8e\xd00\xf1?Gh\xb8\xf8\x9f#4\xc2\xf0?Gh$\xfd\xcf\x11\x1a%\xfe\xe7\b\x8d\x16\xffs\x84>2\xfc\xcf\x11\xfaX\xfc\xcf\x11\xfa\xc4\xf0?G\xe8S\xc3\xff\x1c\xa1\xcf\n\xfd\xcf\x11\xfa\x9c\xfe\xe7\b}!\xfe\xe7\b})\xfe\xe7\b}e\xf8\x9f#\xf4\xb5\xf8\x9f#\xf4\x8d\xe1\u007f\x8eз\x86\xff9B\xdf\x15\xfa\x9f#\xf4\xbd\xf8\x9f#\xf4\x83\xe1\u007f\x8eЏ\x86\xff9B?\x15\xfa\x9f#\xf4\xb3\xe1\u007f\x8e\xd0/\x85\xfe\xe7\b\xfdZ\xe8\u007f\x8e\xd0o\xff\xf9\x9f#\xf4;\xfd\xcf\x11\xfaC\xfc\xcf\x11\xfaS\xfc\xcf\x11\x1ac\xf8\x9f#4V\xfc\xcf\x11\xfa\xcb\xf0?Gh\x9c\xe1\u007f\x8e\xd0\xf8B\xffs\x84&\x88\xff9B\u007f\x1b\xfe\xe7\bM4\xfc\xcf\x11\xfa\xa7\xd0\xff\x1c\xa1I\x86\xff9B\x93\v\xfd\xcf\x11\x9aR\xe8\u007f\x8e\xd0\xd4\xff\xfc\xcf\x11\x9a&\xfe\xe7\bM7\xfc\xcf\x11\x9aa\xf8\x9f#4\xb3\xd0\xff\x1c\xa1Y\x86\xff9B\xb3\v\xfd\xcf\x11\x9aS\xe8\u007f\x8eп\xff\xf9\x9f#4\xd7\xf0?Gh^\xa1\xff9B\xf3\v\xfd\xcf\x11Z\xf0\x9f\xff9B\v\v\xfd\xcf\x11Z\xf4\x9f\xff9B\x8b\xff\xf3?Gh\xc9\xff\xfb\x9f#\xb4T\x95g\rGh\x19\xcb\x13\x81\xe5,O\x04VHy\"\xb4\x92\xe5\x89\xc0*)O\x84VKy\"\xb4\xc6(O\x04ײ<\x11X'\xe5\x89\xd0z)O\x846\x18\xe5\x89\xe0F)O\x846\x19\xe5\x89\xe0f\xa3<\x11\xdcRX\x9e\boey\"\xb0M\xca\x13\xa1\xedR\x9e\b\xed0\xca\x13\xc1\x9dR\x9e\b\xed2\xca\x13\xc1\xddFy\"\xb8\xa7\xb0<\x11\xde+\xe5\x89\xd0>\xa3<\x11\xdco\x94'\x82\a\n\xcb\x13\xe1\x83Fy\"x\xa8\xb0<\x11>\\X\x9e\b\x1f\xf9\xaf\xcb\xf4\xaa\xd09I\xaf\n\x9e\x97\xf4\xaa\xe0\x05#\xbd*|Qҫ\x82\x97\x8c\xf4\xaa\xf0e#\xbd*|\xa50\xbd\xeaઊ\xbb\x9a\n]c\xdc\f^g\xdc\fސ\xb8\x19\xbeɸ\x19\xbc%q3|[\xe2f\xf8\x8e\x117\x0f\xee\xaa\xfb\xab2x\x8f\xf7K\xf8>\xef\x97\xf0\x03\xb9_\x0e\x1e\xaa\vv\t?\xe2\x05\xe3\xe0\xb1:H5\x0e\xfegy\xf2\xbf\xee\xd3\x11\xea\\Qk\x15\xae\xa8ew\x81\xa6W\xd4\xdc\x19\x155o\xa4\xa2\x16\xeeZQ+Ȭ\xa85˪\xa8eE+j-\xbbUԺw\xaf\xa8\xb5ˮ\xa8\xf5ͭ\xa8\xa5\xc5*j\xf5\x9f\xa8\xcc\xefA\xa4\x894\x93\td\"Y\x84,J\x16#\x8b\x93%\xc8'ɧȧ\xc9g\xc8g\xc9\xe7\xc8\xe7\xc9\x17\xc8\x17ɗȗ\xc9W\xc8W\xc9\xd7\xc8\xd7\xc97\xc87ɷȷɒd)R'\x93\xc8\xd2d\x192\x99,K\xbeC\x96#-d\nY\x9e\xac@V$+\x91\x95\xc9*\xa4\x95\xb4\x91\xa9\xa4\x9d\xacJV#\xab\x935H\aY\x93|\x97\xacE\xd6&\xeb\x90u\xc9z\xe4{d}\xb2\x01ِlD6&\x9b\x90M\xc9fds\xb2\x05ْlE\xa6\x91N\xd2E\xbaI\x0f\xe9%}\xa4\x9f\f\x90A\xb25نlK\xb6#ۓ\x1d\xc8\x10ّ\xecDv&\xc3d\x172\x9d\xcc #dW2\x93\xcc\"\xa3d7\xb2;\x99M搹d\x8c\xecA\xbeO\xe6\x91\xf9d\x01\x19'{\x92\xbd\xc8\xded\x1f\xb2/ُ\xecO\x0e \a\x92\x83\xc8\x0f*'(/\xfaPd\xb0\xc8\x10\x91\xa1\"\xc3D\x86\x8b\x8c\x10\x19)2Jd\xb4\xc8G\"\x1f\x8b|\"\xf2\xa9\xc8g\"\x9f\x8b|!\xf2\xa5\xc8W\"_\x8b|#\xf2\xad\xc8w\"ߋ\xfc \xf2#\xd3\xfe\x93\x1c\xfc,\xf2\x8bȯ\"\xbf\x89\xfc.\xf2\x87ȟ\"cDƊ\xfc%2Nd\xbc\xc8\x04\x91\xbfE&\x8a\xfc#2Id\xb2\xc8\x14\x91\xa9\"\xd3D\xa6\x8b\xcc\x10\x99)2Kd\xb6\xc8\x1c\x91\u007fE\xe6\x8a\xcc\x13\x99/\xb2@d\xa1\xc8\"\x91\xc5\"KD\x96\x8a,\x13Y.\xb2Bd\xa5\xc8*\x91\xd5\"kD֊\xac\x13Y/\xb2Ad\xa3\xc8&\x91\xcd\"[D\xb6\x8al\x13\xd9.\xb2Cd\xa7\xc8.\x91\xdd\"{D\xf6\x8a\xec\x13\xd9/r@\xe4\xa0\xc8!\x91\xc3\"GD\x8e\x8a\x1c\x139.rB\xe4\xa4\xc8)\x91\xd3\"gDΊ\x9c\x139/rA\xe4\xa2\xc8%\x91\xcb\"WD\xae\x8a\\\x13\xb9.rC\xe4&\x9d\xee\x16y\x9b\xbcC\xde%\xef\x91\xf7\xc9\a\xe4C\xf2\x11\xf9\x98|\xa2\n#\xd2DL\"f\x91\x04\x91D\x91\"\"EE\x8a\x89\x14\x17)!\xf2\xa4\xc8S\"O\x8b<#\xf2\xac\xc8s\"ϋ\xbc \xf2\xa2\xc8K\"/\x8b\xbc\"\xf2\xaa\xc8k\"\xaf\x8b\xbc!\xf2\xa6\xc8[\"o\x8b\x94\x14)%\xa2\x8b$\x89\x94\x16)#\x92,RV\xe4\x1d\x91r\"\x16\x91\x14\x91\xf2\"\x15D*\x8aT\x12\xa9,RE\xc4*b\x13I\x15\xb1\x8bT\x15\xa9&R]\xa4\x86\x88C\xa4\xa6Ȼ\"\xb5Dj\x8b\xd4\x11\xa9+RO\xe4=\x91\xfa\"\rD\x1a\x8a4\x12i,\xd2D\xa4\xa9H3\x91\xe6\"-DZ\x8a\xb4\x12I\x13q\x8a\xb8D\xdc\"\x1e\x11\xaf\x88O\xc4/\x12\x10\t\x8a\xb4\x16i#\xd2V\xa4\x9dH{\x91\x0e\"!\x91\x8e\"\x9dD:\x8b\x84E\xba\x88\xa4\x8bd\x88DD\xba\x8ad\x8ad\x89DE\xba\x89t\x17\xc9\x16\xc9\x11\xc9\x15\x89\x89\xf4\x10y_$O$_\xa4@$.\xd2S\xa4\x97Ho\x91>\"}E\xfa\x89\xf4\x17\x19 2Pd\x90\xc8\a\"\x1f\x8a\f\x16\x19\"2Td\x98\xc8p\x91\x11\"#EF\x89\x8c\x16\xf9H\xe4c\x91OD>\x15\xf9L\xe4s\x91/D\xbe\x14\xf9J\xe4k\x91oD\xbe\x15\xf9N\xe4{\x91\x1fD~\x14\xf9I\xe4g\x91_D~\x15\xf9M\xe4w\x91?D\xfe\x14\x19#2V\xe4/\x91q\"\xe3E&\x88\xfc-2Q\xe4\x1f\x91I\"\x93E\xa6\x88L\x15\x99&2]d\x86\xc8L\x91Y\"\xb3E\xe6\x88\xfc+2Wd\x9e\xc8|\x91\x05\"\vE\x16\x89,\x16Y\"\xb2Td\x99\xc8r\x91\x15\"+EV\x89\xac\x16Y#\xb2Vd\x9d\xc8z\x91\r\"\x1bE6\x89l\x16\xd9\"\xb2Ud\x9b\xc8v\x91\x1d\";Ev\x89\xec\xae\u008dcr/\xb9\x8f\xdcO\x1e \x0f\x92\x87\xc8\xc3\xe4\x11\xf2(y\x8c\x84b~\x84b\x01\xab)?\x1c\xb4\x9a\xfa\x86[[\xd5\xff\v\xad\r\x82Ѷ8\x19o\x87P\xbc=B\x91\x0e\bEB\b\xc5:\"\x14\xebd5\x15\x84;[M\x19\xe1\xb0U\xfd\xffκ \x18M\xb7\xaa\xffqZ\x06\x19\xc1\x89xW\xdc\x15\xc9D(\x92\x85P,\x8aP\xac\x9bՔ\x1b\xee\x0eD\xb3\x81x\x0e\x10\xc9\x05b1\xab)+\xdc\xc3j\xea\x12~\xdfj\xea\x11\xce\xc3a4\x1f\x87\xd1\x02\x1cF\xe38\x8c\xf7\xc4a\xbc\x17\x0e\xe3\xbdq\x18\xe9\x83\xc3H_\x1cF\xfa\xe10\xd6\x1f\x87\xb1\x018\x8c\r\xb4\x9ar\u0083\x80\xe8\a@\xfcC 2\x18\x88\r\xb1\x9a\xfa\x84\x87*\f\x03\xe2\xc3\x15F\x00\xb1\x91\n\xa3\xac\xa6\xbc\xf0h \xfa\x11\x10\xff\x18\x88|\x02\xc4>\xb5\x9az\x85?S\xf8\x1c\x88~\x01D\xbe\x04b_Y\xb5ܯ\xad\xa6\x9e\xf1oXEߒߑߓ\xbf\xc0Y\u007f\x85\xfdƣ\xdf\xc9?P\x89\u007f\xc2Ơ\x12\xc7\xc2\xfeB%\x8e\x83\x8dG%N\x80\xfd\x8dJ\x9c\b\xfbGU\xe4$U\x91\x93UENQ\x159UU\xe44U\x91\xd3UE\xceP\x159SU\xe4,U\x91\xb3UE\xceQ\x15\xf9/+r\xae\xaa\xc8y\xaa\"竊\\\xa0*r\xa1\xaa\xc8E\xaa\"\x17\xab\x8a\\\xa2*r\xa9\xaa\xc8e\xac\xc8\xe5\xaa\"W\xb0\nW\x92\xabTE\xaeV\x15\xb9FU\xe4ZU\x91\xebTE\xaeW\x15\xb9AU\xe4FU\x91\x9bTEnV\x15\xb9EU\xe4VU\x91\xdbTEnW\x15\xb9CU\xe4NU\x91\xbbTE\xeeV\x15\xb9GU\xe4^U\x91\xfbTE\xeeW\x15y@U\xe4AU\x91\x87TE\x1eV\x15yDU\xe4QU\x91\xc7TE\x1eW\x15yB\xd5\xe1I\x85S\xaa\x0eO+\x9cQuxVᜪ\xc8\xf3\xaa\"/\xa8\x8a\xbc\xa8*\xf2\x92\xaa\xc8˪\x0e\xaf(\\U\x15yMU\xe4uU\x917P\x917UE\xdeR\x85~[\x95\xed\x1d\x1c\x86\xef\x02\xd1{@\xe4>\x10{\x84\xaa|\f{\xc2\xc6m\x1e\xd2D\x9a\xc9\x042Ѧ5(b\xd3\xdcEmZZ1\x9b֤\xb8MkT¦\xf9\x9f\xb4iΧlZ\xab\xa7mZ\xd3glZ\xcbgmZ\xb3\xe7lZ\x8b\xe7m\x9a\xe7\x05\x9b\xd6\xe6E\x9b\xa9]\xb3\x97l\xa6\x86\xcd^\xb6\x99|\xcd^\xb1i\xdeWmZ\xbb\xd7lZ\xc3\xd7m\x9a\xef\r\x9bV\xffM\x9b\xe6z˦5~\xdbfjܬ\xa4\xcdT\xbfy)\x84\x9a\xeb\b\x05\x92l&W\xa04B\xce28\xe7L\xb6\x99\xeb;\x9b\x96\xb5\x99\x1b;\x9b\xbe\x83\x13\xder6\xad\xb9Ŧ\x05Rl\xa6\xe6\x81\xf26-X\xc1fr6\xadh35uVb\xba+\x93UH+i\xb3i\x99\xa96Sf\xa6\x1dȯj\xd3r\xab\xd9L\xb9ݪ\x03Y5lZ\x86\xc3f\xcaȨi\xd3\xf2\u07b5\x99\xb23k\x019\xb5\x81.u\x80\xfc\xba@A=\xa0\xc7{6S^V}\x9b\x96\xd3\xc0\xa6uih3u\xe9\xd2\b\xc8ol\xd3\xf2\x9b\xd8L\xf9\xf9\xcdlZ\xb7\xe66S\xb7n-lZzK\x9bֽ\x95M+H\xb3i=\x9c6-\xcbe\xd3\xc2n\x9b)\x1c\xf1\xd8P\xdd^\x9b\xfa\xfbo>\x9b)\x12\xf3۴H\xc0\xa6\xfe\xfe[\x10W\"\xadmZ\xac\x8d\rU\xdb֦\xfe\xfe[;\x9b)\x16i\x8f\v\xb1\x0e6-\x1e\xb2\xa9\xbf\xff\xd6\x11\x97#\x9d\x80hg\\\x88\x87\x11K\xbc\vB\xd1t\x9b\x16\x8d S\xb9]\x81\x8cL ?\vh\x17\xb5\xa9\xbf\t\xd6\ry\xc8\xe8nS\u007f\xf7+\x1b\xc1v9\x80'\xd7f\xca\xe9\x12\x03\xf2{\x00\xed\xde\a\x9cy\xc8Uf\x81M\xfd\x9d\xbe\xb8bAO\x9c\xe8\xd6\v(\xe8\r8\xfb\xd8\xd4\xdf\xe2\xeb\x8b,g\xf6\x03r\xfb\x03\x19\x03\x80.\x03\x81n\x83P\xd9\x1f\xc2G\x06\xa3^\xf2\x87\x00\xed\x86\xdaL=\x9c\xc3l\xa6\xac\xac\xe1\xf0\x91\x116\xf9\x03r#m\xc6\x1f\x90\x1be\xe3\x1f\x8c\x1bm3\xfe\xec\xdbG6\xf9\xb3o\x1f\xdb\xf8\xf7\xde>Ae\u007fjS\u007f\xa6\xed3\xd6\xe6\xef6S\x83\xc0\x1fp\xb2\xe6\u007f\xdaL-\x9a\x8fA\xcd\a\xc6\xc23\x1a\xff\xa5\x9c\xc49\x0e\xfe\xe3\x1ao3\xbb\x9c\xce\tp\x11\xef\xdfʁ\x9c\x13m\xe6\xe6N\xe7?\x88\xc6霄\xd3i\x93mfgӦSl\t\xf5q~*\x04\x17\xa6\xc1\xebҦ\xe3\xf9\xb4\x19\xea\xf9\xa63m\t\xb8蜅F0\x1b\x8e?\a\xce\xfe/\x9c}.\x931\x8f\x9cO. \x17\x92\x8b\xc8\xc5\xe4\x12r)\xb9\x8c\\N\xae W\x92\xab\xc8\xd5\xe4\x1ar-\xb9\x8e\\On 7\x92\x9b\xc8\xcd\xe4\x16r+\xb9\x8d\xdcN\xee w\x92\xbb\xc8\xdd\xe4\x1er/\xb9\x8f\xdcO\x1e \x0f\x92\x87\xc8\xc3\xe4\x11\xf2(y\x8c \x1f\x92\x8f\xc8\xc7\xe4\x13\xa9fKf\x8a\x06榘\xc0\x8c\x143\x98\x97\x92\x00\xe6\xa4$\x82]R\x8a\x80\xf9)ESM\x96\x94b\bvK)\x0e\xa6\xa7\x94\x00\xbb\xa7<\t\x16\xa4<\x05\xf6Hy\x1a\xccJy&5\xc1\x92\x19Ny\x16\x92\x1bNy\x0e\x92\x11Ny\x1e\xa2\xfe\x14\x1b$'\x9c\xf2\"\xa4K8\xe5%H~8\xe5e<\x16Ny\x05\a\xdd\xc2)\xafB\xd2\xc3)\xafA\xba\x87S^\x87\x14\x84Sހ\xf4\b\xa7\xbc\t\xc9\n\xa7\xbc\xa5\ue327\xbc\x9d\xcaMm\xb2\x14\xa9\xe3\x82-EO\x82\xa4\xa6\xe8\xa5!\xf6\x14\xbd\f\xa4j\x8a\x9e\f\xa9\x96\xa2\x97\x85TO\xd1߁\xd4H\xd1\xcbA\x1c)\xba\x05R3EOIM\xb4ج)z\xf9\xd4\"\x96\xb6\xf1H\x8a^\x01\x81f\xf1X\x8a^1\xb5\xa8ŗ\x15\x8f\xa6\xe8\x95pOZ\xe2\x98}\xc51\xfb\x89c\xf6g\xca\a\x90\x03\xc9A\xa9\t-\x9b\xfb\xf4\x0f\xc41?\x14\xc7\x1c,\x8e9D\x1cs\xa88\xe60q\xcc\xe1\xe2\x98#\xc41G\x8ac\x8e2\x1cst\xa1c~T\xe8\x98\x1f\xff瘟\x18\x8e\xf9i\xa1c~f8\xe6\xe7\x86c~Q\xe8b_\x16:\xe6W\x85\x8e\xf9\xf5\u007f\x8e\xf9\x8d\xe1\x98\xdf\x16:\xe6w\x85O}_\xe8\x98?\xa8\x17\xe1ʏ8\xe1T^\xf8\x13N8q\xc7ό\x0eW~)|\xc1\xaf*\xa0\xd2\xf2[a,\xbf\xa7\x16\x83C\x86ի\xfe(\xf4\xcd?\v\xbdv\f.\xb6ˊ\xa8\x8bc\x11\xf4e\xf1\xbe\xbfx6\xa6\x82\xe3xg8E\x1f\xaf\xe2S\xf9\x9fP\xf8\xaa\xbf\x91\x88\xb6x\xf7Dd\xa5\x1do\xfe\xa7\xb0\xbdL\xfa\xaf\xbdL6\xda\xcb\x14\xa3\xbdL-|\xf34\xf54t:+m\x069\x93\x9cE\xce&\xe7\x90\xff\x92s\xc9y\xe4|r\x01\xb9\x90\\D.&\x97\x90KSM\xb6\xb4e\xa9\xa6Դ\xe5\xa9&{ڊTSմ\x95\xa9\xa6ji\xabRM\xd5\xd3V\xa7\x9aj\xa4\xadI59\xd2֦\x9aj\xa6\xadK5۬i\xebA[\xda\x0605m##\xd9Dn&\xb7\x90[S\xb5\xf0\xb6T-\xba=U\x8b\xef\x80\xedL\xd5b\xbbR1\xb3\xdd\rD\xf7\x00\xf1\xbd@d\x1f\x10۟\x8aU\xc3\x01 z\x10\x88\x1f\x02\"\x87\x81ؑT\xac\r\x8e\x02\xd1c@\xfc8\x109\x01\xc4N\xa6b\xea\u007f\n\x88\x9e\x06\xe2g\x80\xc8Y v.\x15S\xff\xf3@\xf4\x02\x10\xbf\bD.\x01\xb1˩\x98\xc8_\x01\xa2W\x81\xf85 r\x1d\x88\xddHŤ\xee&\x10\xbf\x05\xc4n\xa7b\xe6~\a\x88\xde\x05\xe2\xf7\x80\xc8} \xf6 \x153\xbb\x87@\xf4\x11\x10y\fĞ\xb0\x17\x0fc\xa1X\x90\x13\xc9-\xd0\xec\x89\xe1\xec\x1eYa\x93\xbdH8\xa7G$/b\xb6\x9b\xc3y\x91\x04{\x91hn.|8ў\x10\xcdM\xcf*b7\xf7\x8a\xe5\x16\xb5\x17\x89\xe4\xa7\xc73b\xc5\xec\t\xe1\xf4\xbcHq{b,\x9e\x9b\x1e)a7Dzr\x9e\xb4\x17\xe9\x1e\x8eVʋ>eOL\x0f\xe7\x85\v\x9e\xb6\x17M\x0fg\xc7\xf2\xa2\x91g\xecE2\xc3\xd9ٱ\xdcg퉙\u171c\xf0s\xf6\x84\xcchf\xf8y\x9c\x8fGs#\xe1\x17\xf0H\x1c7\xbeh/\x8a\x13\xd9\x19\x91\xbc\x97\xec\tݣٱ\x97\xedŔd\xe6\x85s^\xb1\x17W\xc1\x9cHA$\xefU9\xdd+\\P\xf0\x1a\xa2\xc2\xd5\xd7\xedŔ\xe8\x05\xb1\xdc7\xec\xc5\xd2\xf3\xe2}#Ѽ؛\xf6\xc4\xeey\xb1\xdc\xc8[\xf6\x84\xf4p~\xe4m$1\x96\x17\xcf\r\x97\xc4\xfbb\x95b=JA\xfb\xa4gGt$\x15\xa5\x11͉$ًa\x05\x98\x9d\x8d\xbc\x97\xc6Eu\xb2\f\x1e\x86&\xdb\x133b}#\xb9e\xed\t\x19\x91\xfc\xe8;\xf6\"\x19\xb1\xec\xecp^9\xbb\x19/\xb5\xd8\x13rù\xb1\x14$;7VP\xde^4+\x12\xcd\xcc*ȯ`/\x8abU\xcfW\xb4'\xaaRϯd/\xd2%\x9c\x97\x17ɮ\x8cK\xd1p>\xb2S\x05\x97\xa2\xe9\xf1l\xab=\x01\x1a\xb3ًuQ\xa5\x804\xa4\xda\x13\xbb\xa2,3\xec\xf6\x84\xae\x91HAU<\x1b\xcfϊdW\xc3\xf9\xbcpnzu\xf5\xa2\xf4\x02TY\r<\x1bɏ9\x10i\xd7\b\xea.\xb3\xa6=1+\x92W\xd0\xf7]D\x1eA-\xd5\xc2\r\xe1\xccHm{B\x97HA\xb8\x0e\xceƢ\xb9\x05u\xed\t=cXo\xd8\xcd\xe8\x14\xdeS'\xe3\xb9\x19\xf5\xed\tY\xa8\xaf\x06\x90X^nC{bN4=/\xd6Ȟ\x90\x13͎4\x86\x84ӳ\x9a(\xc9\xeb\xde\xd4^4'\x9c\x9b\x1f\x8d\xe56\xb3\x17\xe1m\xb9\xcd\xd5\xfd(\xc1\x16\xf6bT\xe4\xb6%n\x8ed\x86[\xe1f\b\x8a+\r\xf7\xa8\x8at\xda\x13\xfa\x84\xf32\\\"nH<\x9c\xeb\xb1'fGq\xd1kOȎ\xe6\x85}\xf6ļx\x8fHį\xb4Kv$`7\xe7Er\x82\xf6by1\x14kf$\xb7\xb5=AyB\x1b\xbbɚ\xd5\xd6n\xb2e\xb5\xb3\x9bR\xb3\xda\xdbM\xf6\xac\x0evSլ\x90\xddT-\xab\xa3\xddT=\xab\x93\xddT#\xab\xb3\xdd\xe4\xc8\n\xdbM5\xb3\xba\xd8\xd13d\xa5\x83\xb6\xac\f05+\x02ڳ\xba\x82U\xb32\xc1jYY`\xf5\xac(X#\xab\x1b\xe8\xc8\xea\x0e\xd6\xccʶ\x9bS\xadY9\xa0-+\x17L͊\x81\xf6\xac\x1e`լ\xf7\xed\xe6f\xee\xfayvSF8ߎEf\x81\u074cb\x88\xdbM\xb1`O\xbb\xa9Gz/;?\x13\x93}Ⱦd?{\x91f\x91h~$\xda\xdf^\xc4\xd7'\x16\xef\x15\x1e`/\xea\x0fG\xf3\x11\x1ehOL\x8bD\xbbE\a\xd9\x13\x9a\xe7\xa6W\xfe\x00\xf1\xd4\xff\xd0nʭ?X\x154J\x1e-x\x88ݔS\u007f\xa8\xddԽ\xfe0\xa0\xc1p\xbb)\xad\xc1\b\xbb\xa9i\x83\x91v3\x1a\xe3(\xb8'd4\x1em\xf2\x11\x1em\xf2\xb1\xbd\x04\x1fm\xa2\x9c\xec\x13{q\x1e\xa8\x86\xf4)\"\xca\xfc\fqd~n75\xeb\xfb\x85\xddܽY\xdf/\xed\xe6\xb4f}\xbf\xb2\x9b\x9b6\xeb\xfb\xb5\xdd\xeco\xd6\xf7\x1b\xe3y\xd6\u05f7x&\xfb;\xe49\xfb{<\x98\xfd\x83\xdd\xd45\xe7G\xbc%\xe7'\xe3.V\xf9ϸ+\xe7\x17\xbb)=\xe7Wܕ\xf3\x1b\\#\xa7c\xea\xefhd\x90?\xecf\xf0O$\x132\x86\x97\xaa\x8e\xe5%\xfb_\xea\x92}\x1c/\xd9\xc7\xe3\xa0J\xfe\x04xQ\x95\xfc\x8e\xa9\u007f\xdbM\xee\xf0D$\xd1\x1d\xfe\aIt\x87'!\x89\xee\xf0d8J8c\n\xdc&\x9cQ%\u007f\xaa\xbd(\xb5c\xea4d?\u007f:\x12\x96?\xc3\xfe$\x13\x96\x1fI\x8f\xe5f\xccD\xca\xf2g\xe1bp6.\x06\xe7\x18š\xdaǿ\xb8\x14\x9c\x8b\xf4\x06\xe7\xa1H\x83\xf3qS\xeb\x05\xb8\xa9\xf5B\xe3&個pS\xebŸ\xa9\xf5\x12\xdc\xd4z)\x92\xea\xca\xcaYfOH\x83,G\x9fY9\xa7\xf2\n\xbb\xa9\xc1\xfb+\x91\xfb\xf4U@\xc6j{B\xc3*\xdd3\xd7\xd8\xcd\rc\x95ע\xe8\x1a\xacCm\xf5Yoǀ\xb0\x01\x05\xef\xdeh7Es7\xd9\x13ZVnYy\xb3\xdd\xd42m\v\xe2/\xd8j7e\xe7l\x03r\xb7\xdb\xcd\xe8\x1ew \xd8{'^\xdfe\x97]\xfd_\x8bv\x83\xb1\xec=He\xb3\xbdh\xf6x\xef>\xbb\xd9\xedNۏ\x82\xf2\x1e\xb0\x9b\xf2\xf3\x0e\xdaM\xbe\x9e\x87\xec\xa6\xd6]\x0e\xd3\xf1\x8e\x90G\xd1z2\x8e\xa1\xf5d\x1cG\xeb\xc98\x81֓q\x12\xad'\xe3\x14ZO\xc6i\xb4\x9e\x8c3h=\x19g\xd1z2Ω֓q^\xb5\x9e\x8c\v\xaa\xf5d\\T\xad'\xe3\x92j=\x19\x97U\xebɸ\xa2ZO\xc6U\xd5z2\xae\xa9֓q]\xb5\x9e\x8c\x1b\xaa\xf5d\xdcT\xad'\xe3\x96j=\x19\xb7U\xebɸ\xa3ZO\xc6]\xb0Z\xc6=\xb0z\xc6}\xb0F\xc6\x03Б\xf1\x10\xac\x99\xf1\xc8n\xb6[3\x1e\x83\xb6\x8c'\xd2\xe4[\xae\x88I\xc4,\x92 \x92(RD\xa4\xa8H1\x91\xe2\"%D\x9e\x14yJ\xe4i\x91gD\x9e\x15yN\xe4y\x91\x17D^\x14yI\xe4e\x91WD^\x15yM\xe4u\x917D\xde\x14yK\xe4m\x91\x92\"\xa5Dt\x91$\x91\xd2\"eD\x92Eʊ\xbc#RN\xc4\"\x92\"R^\xa4\x82HE\x91J\"\x95E\xaa\x88XEl\"\xa9\"v\x91\xaa\"\xd5D\xaa\x8b\xd4\x10q\x88\xd4\x14yW\xa4\x96Hm\x91:\"uEꉼ'R_\xa4\x81HC\x91F\"\x8dE\x9a\x884\x15i&\xd2\\\xa4\x85HK\x91V\"i\"N\x11\x97\x88[\xc4#\xe2\x15\xf1\x89\xf8E\x02\"A\x91\xd6\"mDڊ\xb4\x13i/\xd2A$$\xd2Q\xa4\x93Hg\x91\xb0H\x17\x91t\x91\f\x91\x88HW\x91L\x91,\x91\xa8H7\x91\xee\"\xd9\"9\"\xb9\"1\x91\x1e\"\xef\x8b\xe4\x89\xe4\x8b\x14\x88\xc4Ez\x8a\xf4\x12\xe9-\xd2G\xa4\xafH?\x91\xfe\"\x03D\x06\x8a\f\x12\xf9@\xe4C\x91\xc1\"CD\x86\x8a\f\x13\x19.2Bd\xa4\xc8(\x91\xd1\"\x1f\x89|,\xf2\x89ȧ\"\x9f\x89|.\xf2\x85ȗ\"_\x89|-\xf2\x8dȷ\"߉|/\xf2\x83ȏ\"?\x89\xfc,\xf2\x8bȯ\"\xbf\x89\xfc.\xf2\x87ȟ\"cDƊ\xfc%2Nd\xbc\xc8\x04\x91\xbfE&\x8a\xfc#2Id\xb2\xc8\x14\x91\xa9\"\xd3D\xa6\x8b\xcc\x10\x99)2Kd\xb6Ȝ4.\x9fȹ\xe4\xb9\x80\\H.\"\x17\x93Kȥ\xe42r9\xb9\x82\\I\xae\"W\x93kȵ\xe4:r=\xb9\x81\xdcHn\"7\x93[ȭ\xe46r;\xb9\x83\xdcI\xee\"w\x93{Ƚ\xe4>r?y\x80\xf9\x80|H>\"\x1f\x93O8e\xbcr&6\xc2\xec^79\x13Z\x86c\xba\xd9i\xf6D\xf5\x04g\x11.\xd2\xf5Dg\x02\xd6\xe5z\x11\xb9\xb7\xa8Ӝ\x16Ӌ9\x8b\xb4\xe3\xc5\xe2\xce\x04_8W/Qx\xf3\x93r\xf3SNs\x8b\xa8\xfe\xb4\xd3\xdc \xae?\xe34\xb7\x8d\xeb\xcf:\x13ӰNןs&4\rG\xf5睉\r\xb3bq\xfd\x05C_T\x11b\xb2\xad\xbf\xe4L\xf0D#\xfa\xcbN\xb3;\xaa\xbf\x82س\xa2\xfa\xab\"\xaf\xa9Kq\xfdugb\x03\x95\xda7\x10oD\u007f\x13\x11\xc4p\xf4\x16\xf2\xa0\xf4m\xa7\xd9\x17\xd5K:\x8b4T\x9b\rz)gB#<\xa3ˣI\xce\"\xad\xd4\x06\x85^ZN\x97q&\xb4\xc5˓\vO\x97\xc51\xd2\xf0\x8e\xf1\x86r\xceD\x9f:mA\x9a\xe3\xb9z\x8a3\xa1\x05\x1e*\xef47\x8d\xe8\x15\xf0\xf6\xb0^\xd1Y\xc4\xc3'+\xa9\xf4\xab\xf7WfΫ\xa8[#\xbaՙ\xd8D\xa5\xc2\xe6Ll\xaa2\x97\xaa\x92\xa5\x02v\x95mhUgB\xabh\xae^͙\x88\xdc\xc7\xf4\xea\xce\x04\xa8^\xc3in\x16\xd6\x1d΄ָ\xa5&\x92\ny\x17G\x91\xa8^K\xee\xa8\xedLl\xa1RX\xc7in\x15\xd5\xeb\xe2\x9dq\xbd\x9e3\xc1\x8dw\xbe\xe747\x89\xeb\xf5\x91\xbc\xa8ހl\xe8Lp\xa2\xc8\x1b\xa9:\u009aRo,\xf9h\"\xd2T\xc5\x18ћ\xa9\xac\xc7\xf4\xe6|\xa2\x85\xd4uKu)\xaa\xb7r\x9a[\xc7\xf54u\x10֝H[\\w\xe1%aݍ\xb7GtOaֽ\xceD\xb7J\x94\x0f\xaa\xca\xc4\xefLD\xe1\xc4\xf4\x00\x1f\t\xb2\f\xa2z\xebªiS\x18h\xcbw\xb6C\x02P\x14\xed\xe5\xd5\x1d\x9c\ti\xc8LH\xd2ؑ\x1eى\x15\xde\xd9in\x13\xd5\xc3\xce\"\xdcjһ\x88?\xa5K\x85f8\x13\x9a\x85\xb3\xf5\x88D\xd2U\xf9M\\\xcft&\xfa{ų\xf5,q\xa3(k\xa8\x9b3ѩ\x9e\xef\xae\xde\x10ֳ\xe1\xfa\x91l=Gnɕ\xe7cN\xb3+[\uf064\xe0\r\xef;\x13[\xa9\xaa\xcbs&4\xc4+\xf2\x9dfo\\/\x80\xbb\xc7#z\\\xfc\xa6'\x8b\xbe\x97\x8a#\xac\xf7v\x9a\x9da\xbd\x8fJ^\xae\xde\x17\xe7b\xd9z?\xa7\xb9q\xb6\xde\x1f\x95\xaf\xd23@\xde2Pn\x19\x84x\xf1\xea\x0f\x94\xafE\xf5\x0f\xe5\xe4`\xe3\xa5C\xc4O\x86\xb2\xa4\x86!\xc9x\xe9p\\S\xc5;\x02-2\xac\x8fd+\x1b\xa5\xeaB\x95\xe9h\xc9\xc8G\"\x1f\xe3\xbdy\xfa'\xea\rq\xfdS\xde\xf9\x19\xf99\xf9\x05\x92\x0f\xff\xfe\x92\xf5\xf4\x15\xcb\xfak\xd6\xf97\x86\x9b}\xcbF\xf5\x1d\"CҾG\xd2\"\xb9\xfa\x0f\"?\xb2\x1d\xfc\xa4\x9a}D\xff\x99\a\xbf0\x86_\x19\xfe\x8d\x85\xfd\xbb3\xc1\x8fv\xf6\x873\xb1\xb5*\xf3?\x9d\x89-\x95\x8eq\x9a\xfda}\xacz\t\xf2\U00057abb\xa8>\x8ey\x1co4\x90\t\xce\xc4f*?\u007f#\x93q}\xa2\x94\xd8?\x85\x95?\xc9H\xdfd\u0530\xd2)\x85\ryj\xe1\x1dӌ;\xa6\xd3wf\xa8n W\x9f\x89~)\xa6ϒ\xc6?[\x92>G\x9aٿ,\xd4\\}.\x0e\xd1 \xe7I\xab\x9b\xefL\xf0\"\xaf\vD\x162O\x8b\xe0\xfca}\xb1\x91\xa5%L\xf5R)\xeeer\xdfr6\x8f\x15F\xe7\xba\xd2in\x17\xd1W)w\xcb\xd5W\xa3{\x8b\xebk\x8c\xceo-\xd2\x13\xd6\xd7\x15\xf6\xa9\xeb\xe5\x9e\r҇l4:\xa3M\xceD\xaf*\x89\xcdF\x87\xb7\x05\xce\x16ӷJ\xff\xbb\r\xfe\x81\x1a\u070e\xa3>a}\anQ\x11\xed\xc4+\xa3\xfa.I\xd4n\x16\xf6\x9e\u0097쥯\xees&\xaar\xd2\xf7\x1bz\x00\x15\x85l\x1fD\xa9\xabw\x1cB\xb9*=,\x19:\xc2\x06\x9d\xab\x1f\x15\xe7<\xc6<\x1fw\x9a\x1bE\xf5\x13\xaa\xf8\xa2\xfaIU|x\xe0\x14/\x9df%\xc6\xf43\xaa\xa5\xab\x97\x9e\x85\x1f\x85\xf5sR\x8b\xe7y\xcf\x05V\xecE\xb41\xbc\xe0\x92\xbc粴\xc9+\xc6\xe3W\x9d\x89m\xd5\xd3\u05cc\xd7_/\xecpnp\x90\xb8)\xfd\xe4-z\xecm:S\xae~G\n\xef\xae\xf4j\xf7\x8c~\xf8\xbeQ0\x0f\xe4=\x0f\x8d\xca{\xe4L\xc0e\xfd1s\xf2?ܫN>\xe12ޢ\xb9\x94;\x9b\\\tnx\x82٥\x1aL\x82\vu\x8ax\x13]\xf0\x92\\\xbd\x88\v^\x92\xab\x17u\xa9\f\x15s\xc1\x9fr\xf5\xe2.\xf1\xf1\x12<\xf9\xa4K\xb9\xccS.s}\x8c\x8a.\x95\xd0gx\xe6Y\x97\xaa\x85\xe7\\\xaa7}^=\x88\x11\xd1\xc5\xda\u007fѕ\xd0\x00\xef{\xc9E\xb7|\x99\xf7\xbd\xe2J\xf4\xab8_-L\xdak.\xba\xf0뮄f\xa8\xb57\x94D\xf57\x99ķ\\\t\rq\xf0\xb6\xcb\x18qJ\xba\xe8(\xa5\\th\xbd\xf0t\x12nC\xef_ڥ\x1ak\x19\x97xA\xb2\xcb\x18\xcd˺\x8c\x8a{\xc7E\xff*\xe7\x92\xf2\xb3 \xa9j@\x94\xac\x96\x978+\xb884Ut\xc9z\x0eq\xa9\x17Tv\x99\x9dq\xbd\x8aK\xb58\xab\xcb\xdc4\xae\xdbp&\xaa\xa7\x92v\x17\x1b_Ud\x16\xf7Vs\x99\xdb\xc4\xf5\xea.\xf1\xa1\x1a.\f\x8aȝÕ\xe8C\x9f\xa6\xd7D=\xc4\xf5w]\xaa+\xaa\xe5Jl\xa0*\xb46\x8f\xea\xb8\xe8/u]\xca\x1d\xea\xb1\xc0\xdf\xe3\x85\xfa,\xf0\x06.\xa3Sl\xc8j\xcb\xd5\x1b\xa1\xa0 \x8d]\xec؛H\x117\x95\xe47s\xa99Ks\xbe\xab\x05\xc2Q\xbd\xa5z?*\xbb\x95\xcb\xdc0\xaa\xa7If\x9d.\xe5+.y\xc6\rA\xf9{\xa46\xbc.qW\x9f\xcb\xec\x8f\xea~\x95\vD\x1fp\x99\x9bE\xf4 cl\xedb+k\x83*Aٷe}\xb5c\xb2ۻT\xa3\xee \xf1\x85\\\t-\x11_GW\xa2S\x95G'\x974\xc6\xce,\xb9\xb0Q\xbe]\x98\xd2t\xbcS]ˀo\x85\xf5\x88K\x1aJW\xa9\x8aL\x97\xb9eXϒ\x83\xa8\xf8q7Wb35\x1awg\xb2\xb2Q\xbd\xc8a\x8e\xcb\xe85s]j\x8a\x1332\xd2C\xd5\x0e\x06@zi\x9e+Q}M\xd2\xf3\xa5\xcc\v\\jd\x89\xbb\xa4\x8f\xeaI\xa7\xee\x85\xe2E}\xf5\x16\xaf\xec\xe3\xe2\x80\xd6\x17iR\xe9\xed\x87\\\xa8\xa6\xd3\xdf%\xbd\xfe\x00\x96\xea@\xe6~\x90\x8b=\xd5\a\xca\r\x11ۇF\xc5\x0f\x96\xe2\x1a\x82\xe2@\x04C]\x9c\x94\fsq\xdc\x1f\xaeR\x1e\xd5G\xf0\xf9\x91.\x0e\xa1\xa3pc<\xac\x8fv\x15Q\x93\xaeL\xfd#\x97\x9a$}\xec\xe2\xfc\xe2\x13yǧ.sZT\xffL\xea\xefs\xfa\xdd\x17R}_\xc2gr\xf5\xaf\xf0L\\\xff\x1a\x1e\x82S\xdf0\x93\xdfJ\x1b\xf8Ψ\x86\xef\xd5\xcb\xc2\xfa\x0f\xf2\xce\x1f\xa5E\xfc\xe4Jl\xaa.\xfel\xf8\xee/L\u05ef.5W\xf8\xcd%]\xd3\xef.N\x91\xff\x90\x02\xfc\x13I@\x06\xc6\xf0}cQS\x18\b\x8d\xa2\x19\xa7\"A\xfeǣ\x88\"\xfa\x04\x97̦\xfeFǢjn\xa2K\xf5\x8eQ\xfd\x1f\xc3=&Aի'K\xff1\x85\xbd\xccT\xd4\x01\x921\xcdU\xa4ET]\x9c\xee2\xe6\\3\x8c\xb8g\x8a\xab\xcdr\xc9H4\xdb\xc5\x01r\x0eS\xf3\xafKM+\xe7\xba8\x89\x9a\xe7\x92!v\xbeQ)\v\xa4R\x16\xb2a/ra\x86\x99\xab/V\xed*\xa6/Qހå\xea\x16\x8c\x86\xf4\xef\xe5.5m[\x81\xac\xc4\xf5\x95.Sc}\x95\x8bS\xf2\xd5,\xfb5\xae\"-YWkQ\x02q}\x1d\x13\xbf\x1e9A\x99n`\x19nd\x0f\xb8\t}\x83\xbam\xb3\xcb\xdc.\xaeo1\x8ad++x\x9b\xd1\x06\xb6+\xbf\xcb\xd5w\xb8d\xe4\xdfitj\xbb\xa4\x03\xdc\r\xffG\xa9\xedA\xff\x10\xd7\xf72\xd2}L\xda~Fr\x80g\x0e\xbad&{Hy~L?\xcc\x1c\x1ca\x97rTj\xef\x98t}ǥ\xcf>\xc1\x1bN\xb2\xc1\x9f\x92\x1e\xf5\xb4Q\xa2g\xa4ٝuq\x86x\x0e>\x84\xa7\xcf\xf3]\x17\xc4w/\xca\x1d\x97\f\xf7\xb8,Q^aB\xae\xb2y^cE\\gFb\xfa\r\xc9\xc1M\xe3\xfe[,\xed\x98~\xdb\xc5\xd9\xdd\x1d\x17Ʋ\xa8~\xd7%#\xde=v\xec\xf7\xe9\xd3\x0fdHy\xc8\xc4>\x92\fY\x13\xba\xd9Ƿu\xabѵ\x9d\x9b}S{7;\xda\x0en\xe9\x80B\xe2C\x1d\x99\xfbN\xee\"\xcd\xd8ot\x16\xd7\f\xbbe\xb4\xed\xe2\x96֗.\t\xc9p\xb3k\x8f\x18\xb9\xef\xeaV}N\xa6\x9b\v\xda,\xb7\x1a\x80\xa2n\xd5ܺ\xb9Uk\xe8.E\x9amTw\x8e[v.r\xddjp\x8b\xd1\xc5zH\xf2\xdfw\xb3\xd7\xcfs\xab\x9e!\x1fթ\n\xb3\xc0\xad\xba\xeb\xb8[:؞n\xb3\vkB7\x17@\xbdE\xfaH\x16\xfa\xa2\xde\xc3z?\xb7\xeak\xfbKI\x0f\x90r\x1c\xe8\xe6\x84tP\xa1\u007f~\xe0\x96E!*\x19\x8f\x0f\x96\xf4\rq\xab.l\xa8\x9b\xfd\xd10\xa6\u007f8\xea[%a\x84;ѫ\x92>ҭ\xfa\x99Qn5\xfc\x8cvs\x91\xf7\x11\xa2B\x1c\x1f\xbbբ\xf1\x13ԥ\x8a\xffS\xb7L\x8c>3*\xeas7;\xf2/$Q_\xbaU\xff\xf8\x95[M\x83r\xf5\xaf\r\xbf\xffFZ\xe6\xb7F\x83\xff\xce\xcd\xee\xe7{7{\xd9\x1f$\x87?\xba\xa5\xcb\xfa\xc9-]\xf3\xcfRK\xbfH\xdb\xfeU\xb5\u0098\xfe\x9b[\xa6\x19\xbf\xbb\xd5\xfc\xe2\x0fý\xfe\x14\x0f\x1c\xe3\xe6\xbab\xac\x9b\xa3\xe4_F\xfaƉ{\x8f\xc7[ЇM\x90W\xff\x8dւ\xe8&\xaa֒\xab\xff\x83\xa6\x87\x14L\x92\xbej\xb2\x91\x80)*\x9e\x98>ը\xdaiF72\xddh\xa03\xdc\xe6\xfa1}\xa6ۘ\x1cϢ\xc3\xcf6\xda\xdb\x1cD\x85\x92\xfb\xd7mL\x8e纍\xbd\x9fyF\xe72_:\xcd\x05\xd2\x14\x17\xba9\xc9\\\xe46\x16\\\x8b\x8d\xa6\xb9D\\r)]|\x19\x8bv9]`\x85\x94\xd9J\xb6\xa1\x98\xbe\xca-+E\xb7,H\xd7\xf0\x9e\xb5\xe8#q\xb0Ψ\x83\xf5n\x19\xdb6H\x81n4\x9a\xfb&\xa3c\xd9\xec66ն\xb8\x8d\x89\xddV\xa3\x12\xb7\xb9e\xe4\xdc\xee6&\x06;\xdc\x1c\xabw\xaa&\x87\xb1ҭ\x96\xa7\xbb\x99\xb8=n\xce\r\xf7\x16\xfa\xe2>\xb71\xad\xda\xef\x96\xfd\x87\x03\xecn\x0f❪(\x0f\xb9e\xe1~X<\xf9\b\x1b\xcdQ\xf8\xb3Z0\xba\xb9@9^X\xbe'\x8c\xf6x\xd2\xcd\x19\xf8)\xb7\xcc{N\xbb\xb9\xe9s\x06iU1\x9e\x95\xc6p\xce\xc8\xeby\xf1\xbb\v\xaa\x17\xb9h\xb3\xbbG\xedhe{d5\x94\xa3\xbc%\xae\xe7zT\u007f\x12\x13\xaf\xed!\xbe\xf8\xbeqK\x9eG\xa6\x84\xf9\x1e\x99)\x16xd\xa2\x17\xf7\x18+K\x8f\xeadzyԴ\xa8\xb7GF\xab>\x1e5o\xea\xeb)\xe2\xee\xd1\v5\xd4\xcf#\xe3H\u007f\xa3\t\rP\x99C\xae\x06zT\x174\x88\xcd\xef\x03\x86?4\xbcf\xb0\xc7\xe8\xfd\x86Hq\f\xf5$\xc0\x8b\xf4a\x92\xb5\xe1FU\x8f\x90\x8b#\x912\x15\xfd(\xb9i\xb4G\x8d\xf2\x1f\x15\xc6\xf01\x0f?\x91\x86\xf9\xa9\a\xc3uX\xff\xac\xf0\xe2\xe7\x12\xc3\x17\x1e\xe93\xbe\xf4pU\xf9\x95\x87#\xf1\xd7\x1e5\x19\xf9\xc6\xf0\x83o=j]\xfa\x9dG\xc6\xef\xef=\\\xb3\xfe\xe0\xe1l\xe3G\x0f'G?I\xff\xf2\xb3ѿ\xfc\u008c\xfd\xca\xe7~\x93\xba\xff\xdd(\xc4?\x8c6\xfd\xa7ǘލa\xf9\x8d52\xf6\x97G\x96\xf2\xe3<\xdcg\x18\xafj$G\x9f\xe0Q3\x89\xbf\xe5\x95\x13\x19\xed?F%L\xf2ȸ2Y\xba\x97)F\x89L\xf5H\x9f8\u0378m\xbaG\xed#\xcf(l\x183=\xec\xb0g\xf1\xec\xec\xc2Nb\x8eGu\xb5\xffzdF<׃5M\xae>\x8f\xef\x9e\xefag\xbc\xc0x\xdbB\xa3\x1d-\x92>g\xb1\x87\x13\x9e%F-/5\x9cu\x99\xb4\xfc\xe5x\x16u\xb7\x82\xadd%\x9df\x95G\xf5\xff\xabU\xe1\xa3\xd8\xd6xd\xa8_k<\xb7\xae\xd0q\xd7{d|\xde\xe0\x91\xbd\xbd\x8d\x1eن\xdad$c\xb3\x87+\x89-\xc6{\xb7J\xd5l\xf3pR\xb0\x9d\x95\xb0\x83\rf\xa7GfD\xbb\x8c\xe2ݭJ?\x8eV\xb6\xc7ù\xc8^\xb6\xdd}\x1en0\xef\x97^\xf8\x80\x87\x03\xd6A\x91CF\xef{\x98\r\xe6\b\xfbѣ\xf4\xdcc\x1e\x0e\xa4\xc7EN\x88\x9c4\xca\xff\x14k\xf7\xb4\xf4\x8cgT\a\xd7=\xa6\x9f5.\x9eSN\xd7]?\xefQ\xb3\xdd\v,\xe7\x8bƥK\x1eY\xed^6\x8e\xaf(?\x8b\xeaW\v\x1b\xfe59\xbe.\x05p\xc3#\x03\xfdM\xe3\xa9[\x1e\xb5\x01u\xbb\xb0\u05fbc\fdwY\x03\xf7\xef\x95\xfe\xe6\x05/F\x8e\xb0\xfe\xa2\x97\x8bڗ\xbc\xb2\xfa\xf4\xaaz}ŋ\x81 \xa2\xbf\xea\xa5\x1b\xbe\xe6e\x9f\xf6\xbaW\xed\xea\xbfᕖ\xf0\xe6\u007fI{˫J\xf8myaI\xaf*\xa2R^U=\xbaWf`I^:yi\xafxY\x19\xaf\xd1D\x93\xbdl\te\xbd\x18\xd4#\xfa;^\xce\xc0ˉX\xbc\x1cXS\x98\x9c\xf2FD\x15\xe4Ί^\xe9\xd3*yů+{\xd9\aU\xf1\xf2+\x8dU\n\xd2\xf6_\xfaRQ@\xf0E\xbbWm\xb7T\xf5\xd2\x17\xaby\xd58S\xdd\xcb\x11\xba\x86\x8a\x1e\x118\xbc\xd2\xc1\xd7\xf4r\x15\xf6\xaeW\xf9O-\xaf\x1a\xc2jK\xaa\xeax\xb98\xa9\xcb+\xf5\xe4\xe0=/]\xa6\xbeD܀%\xddЫV\x8a\x8d\x8c\xf45F\xf9\xa1\xe14\x91\xf45e16\xf3J\xc7\xd4\\UWLo\xe1\xe5ǵ\x96|Y+\xaf\xfaܒfT\xa8SR\xe3\xfa/?nɮ\xc7+\xad\xd3+1\xf8XR~\xbe8\xe0\xe5\xc6H\x10\xe5\x8b\xf7\xb66\x8a\xab\x8dWu\x11m\xff\x8b\xa7\x9dW\x8d)\xed\x8d\xfa\xec n\x10\xf2\xaaydG梓d\xa9\xb3Wz\x8b\xb0\x97co\x17\xaf\xac\xfbӍ\xf7g\xf0\xde\b\x9f\xeb*\xb7d\xb2\xfa\xb3\xbc\xdc\x14\x8dz\x8d\x05o7\xe3\x81\xee\x12m\xb6W\u05909^5\xf5ȕL\xc5\xe8K=\n\x1fy\xdfx$ϫ\xb6V\xf2\xbd\\\xc5\x16H\x81\xc7\xff\xcbHO\xaflZ\xf422\xdaۛ\xe8W\xc7}\xbcj\xae֗\x05\xd3\x0fE\x82\xb2\xe8ox\xd2\x00\xaf\xac<\x06z9\x1e\f\xf2ʤ\xf3\x03IŇ^\x99\a\x0f\x9661D\x1a\xcaPy\xfd0#\xffý\xb2Z\x19\xc1̎\xf4\xaa)\xf3(i)\xa3\xc53>2b\xfdX\x0e?12\U000e985fy\x8d\x15\xa8\x94\xd3\x17,\xc7/\x8d\x8b_\xb1\x85\u007fm\x1c}#\x8e\xfa\xad\x91\xc1\xef\xa0*\x83\xdf{\xd5\x0f\x14~`\x06\u007f\xf4r\u007f\xf7'\x1e\xfc\xec\xe5\xc7Iß\u007f\xf5\xf2\x93\xc3o^\x99\u007f\xfc\xee\xe5d\xff\x0f\x96\xfb\x9f^~\x18\x18#-}\xac\x97]\xf4_\xdeĖ\xaa\xf8\xc7y92\x8e\x97\xb7O\xf0\xca\xe8\xf0\xb7\xbci\xa2W}\xed\xf8G\x1a\xc6$6\xfb\xc9^\xf9|0\x85\x89\x9f*\xf7M\xf3\xaa\xe1>\xa6Og\xcaf\x18mb\xa6\x97K\xadY\"\xb3\x8d\x1a\x9c#\xd5\xf1\xafW\xa67s\xbdj\x8b\u007f\x9eWV\xd6\xf3\xbd\x9cK,\xf0\x1a\xc3\xeaBy\xf5\"I\xfab\xaf\xda>\\\xe2\xe5\xac`)\xdb\xd12\xe3]˽\x9ct\xaf\xa0\x8b\xae\x94\xceh\x95\x97\v\xd8\xd5^\xd9d]\xe3\x95E\xf3ZC\xd7ye\x8e\xb2\x9e\x11m\xf0r\xefc\xa3\x91\xcaMF\x83\xd8L\xff\xda\"\xadl\xabW& ۼ\xdc\x1c\xdc\xeeU\x1fDw\x88\xab\xee4b\xdbe\x94\xe0n\xaf\x9a\a\xed!\xf7J\xf7\xbd\x8f\xb5\xb1\x9f\x05w@^wЛؚ\xcb\xcc\xc2\xd6pX\n\xeb\x88W}F<ʗ\x1f3\xfc\xf8\xb8\xe4\xfb\x04}褗;ۧ\xbcƢ\xfd\xb4\xe1\x87g\f\u007f8k\x14\xef9\xbe\xed\xbc8\xf9\x05/\a؋F\x1e/yeyy\x99\xde}EF\x8e\xab\xe2\x0f\xd7\xc4g\xaeK2o\x88\xdcdzn\xb1\x92o{\xb9\xd5u\xc7++\x95\xbb\xc6{\xefI\x9f~\xdf\xf0\x92\a^\xae6\x1ez\xb9\x04~$\x17\x1f{9S\xfc\x1f\x8b\xe3\t\x1f\xef\xd0|\x89ij\xd6k\xf2%8\xc3}t\xb3\x8f\x9fC\x12D\x12}|\xae\x88O>\x15\x17\xf5ɯL}\xc6\x18]\xdc'\xdb\x1e%|*\x1fO\xfa0\x9b\x8b\xe9O\xf9Tw\xf2\xb4O\xad\xfc\x9f\xf1\xa9\xb5\xe4\xb3>\fE1\xfd9\x9f\xea~\x9e\xf7ICy\xc1gn\x84\xf1\xd1\xc7\xf4\xbd\xe43\x96\x87/\xfb\xd4\xc6\xd9+>\xf6\t\xaf\xfa\xb8\x91\xf1\x9aJYL\u007f\xddG\xa7z\xc3'{\xaboʓo\xf9T1\xbf\xedS;`%}\xc6*\xbc\x94O\xbe\x99\xea\x85'\x92|\x86[\x97\xf6\xa9\xc9c\x19\x1f\xa7~\xc9>ծ\xca\xfa\xe4+\xc3;>v$\xe5|\x9c\x04X|\x89\xed\xd4\x03)\x85O\x96\xf7\xb1ۯ\xc0\b*\xfa\xb8\xffVI\x1e\xa9,R\xc5\xc7Ţ\xd5\xc7\xea\xb4\xc9\xc9T\x9ftdvC\xab\xfa\x12}\xaa\xba\xaa\xf9\xd4d\xac\xbad\xa2\x06\x13\xe2\xf0\xa9YZM\x9f\xea\xeb\xde\xe5\x99Z\xcc\\m\x95\xa2\xb0^\xc7\xc7\uefee\x91\xdcz>\xb5\x90~\x8f\xf7\xd5\xf7\xd1A\x1b\xf8\xb8\xb2k蓱\xad\x91\x0f\xb3?\xb5)\xebS\x1f˛\xf8ԗ\x8e\xa6>\xc3ݛ\xf9\x94\x0f4\x97$\xb7\xf0\xb1\x0fk)Ѵb:\xd2|\x1c\xb2\x9cR\x15.\x1f\x9d\xd3-'=R1^F\xe9\xf3I\x97\xed\x97[\x02\x92\xfd\xa0\x8fn\xdc\xda\xc7\x0f:m\xe4d[\x9fjA\xed|\xaa\xabh\xafR\xa2&x\x1d|\xeaCi\xa8\xb0\x9c;\x1a\xf9\xeb\xc4ӝ}\xb2\x0f\x1b\x962\xe8\xe2S\xed \xdd\xc7\xe5~\x06\x0f\"LmW\x1fǖL\x9f\xda\x18Β\x94D}\xecº\x89t\xf7qݟ\xedK\xa8\xafV\x92\x85\x0e\x97\xebc\xf7\x1b\xf3qe\xd5\xc3\xc7\xc9\xf8\xfbro\x9eO\xdaR>\x93]\x80$\xa8\x9f\xe8\xf8\xe4\x974=\xa5\xb0z\xf9\xa4I\xf7\x96w\xf6\xf1\xa9n\xa7\xaf\x8f=}?y\xa2?\x8bz\x00#\x19\xe835\xd6\a\xf9\xa4\x8b\xfb\x80I\xffP\x1a\xd5`\x1f\xe7\xafC\x8c\f\x0fU7\x0e\xf3\xc9V\xf5p\x9fj\xf8#|\xfc\x81\xd5H\xb9q\x14ύ\xf6\xa9\xb1\xe8#\xf2cq\x90O|\xfc\x1a\xfc\xa9d\xec3\x1f\xc7\xd5\xcf}\xf2S\xaa/\xe4e_\xfaT\u007f\xfe\x15\x1f\xfbZn\xfcƧ\x16\x9e\xdf\x1a\xf7}\xe7SS\x9d\xef\xf9\x8e\x1f$\xc2\x1f}\xfc4\U00093466\x9f\xf1\x18\\\xe7\x17\x1f'H\xbfJ\xeb\xf8\xcd'\x13\xaf\xdf\x19\xd9\x1f\xc6џ>c\xd7p\x8c\xa4p,\xa3\xfd\xcbǮq\x9c\x8f[Q\xe3}\xb2\x16\x9f\xe0\x93=Xi\x18\x13}\xecl\xff\xf1\xa9\xd9\xff$iQ\x93%\x92)>\xb5\xfa\x99ʨ\xa6\xf9\x8c\xed\x8e\xe9>n\x06\xcc\xe0ٙr\xe3,\x91\xd9Fb\xe6\xf0\xb9\u007f\x8d^h\xae\x8f\v\xa5y>~C\x9c\xcfִ\x80\\\xe83\a\xba\xeb\x8b|\xdc\xcfY\xccֺ\xc4'\vȥ>c;t\x99\x8f\x9b\xd4\xcb}\xdc)^!\xb2Rd\x95\xc8j\x9f\xda\xd7[\xe3\xe3\xee\xf3Zq\xdcu>N:\xd7\xd3#6ȹ\x8d\xbe\xa2>\x99pm\xf2q\xac\xd8\xec\x93]\x87->\xf9\xb9\xdbV\x9f\xda\t\xd9\xe6S{\xdb\xdb}\xfcd\xb2C\\p\xa7\x8f+\xd6]F;\xdc\xcd\xe4\xef1\x1e\xdfk4\xa7}t\xcc\xfd>\xceN\x0e\xf8\xb868\xe8\xe3g\x80C\xaa\xaf\x8f\xe9\x87\xc5\xe9\x8f\xc8ɣH1\xfc\xf9\x98zST?n\f\x00'|\x9cB\x9c\xf4\xa9\x95\xf0)\xf6\xfa\xa7}\xb2\xf08#-\xf0\xac\f\x02\xe7\xe4\xc6\xf3\xe2[\x17|2F^\xf4\xc9\xd6\xd6%\xe6\xe5\xb2O~\xd0tŧ\xa6\x01W}\xdc\v\xb8&\xbd\xfcu\xc9\xda\r)\x9c\x9bR=\xb7\xd05\xc6\xf4\xdb\xd2\xd2\xee\xf88\xef\xb8\xcb\xea\xbc\xe7\xe3\xbe\xf5}y\xf8\x81OM\x0f\x1e\xfad\xed\xf7\x88\xc5\xfcX\x1c\xff\u007f>\xf9A\xd2\x13~\xfe,P\xf3\xabR1\xf9\x13\xd3Tɛ\xfd|<\xc1\xaf\x06\xb0D\xbfl\xa8\x16\xf1\xf3SCQ\xbf\x9a\xcf\x17\xf3\xabvS\xdcϥ\xa1_\xe2\u007f\xd2\xcf5\xe1S~\x19ӟ\xf6\xcbj\xfd\x19?\x9b˳~\xe5\x8a\xcf\xf9\xd5\"\xf6y\xbf\xd1\x0e^\xf0\xb3Q\xbe\xe8\xe70\xf4\x92_\xed`\xbc\xecW\xbd\xd9+~S}\xfdU\xbf\xfa\xa6\xf2\x9a_}\xf0y\xddϵ\xcb\x1b~~\x90\xf4s\xc7\xe6-\xbflǾ\xed\xa7\xf3\x96\xf4'\xf8q\xb6\x94\x9f}\x97\xeegKL\U000ab074\xb4\x9f\xed\xa7\x8cH\xb2\x9f=aY\xbf\xeaY\xdf\xf1s\xce^ί\xaa\xc2\xe2\xe7\xef\x8fR\xfc\\\a\x97\xf7\xa3\xef\xa9\xe0g7S\xd1/ͽ\x92?\xb1\x99Jze\xbfZ\xce\xe5\xeaU\xfcj\xbdc\xf5Kq\xda\xfc\xac\xe7T\xbfl\x03\xda\xfd\xaaǫJV\xf3ˏ\xaf\xaa\xfb\xcdΈ^C\xb2\xe3\xf0s\xa8\xf3\xcb\xc7\xd8w\xfd\xdc5\xac\xe5\xe7r\xba\xb6\x14\\\x1df\xbf.\xe3\xa8秇\xbe\xe7W\xfdU})\x84\x06,\xaf\x86*\xa9\x8dXa\x8d\xfdܱi\xe2W\r\xae)\x93\xd7\xcco|\xael\xcehZH4-\xfdj\x1e\xd2\xca/>\x99fd\xc1)\x95\xef\xf2\x1b\x03\xa4\x9b\xe9\xf5H\xd9z\xf9\x88\x8f\x95\xe0\xf7\xb3-\a\x98\xbe \xab\xb7\xb5\x9f\xfb\x9dm\xfc\xd2Ŵ\xf5\xcb\xd6E;\xbf\xfc{\n\xbf1U\xec\xc0T\x84T\x92;\xfa\xe9ߝ\xfc2y\xeb\xecO\xc0DJ\x0fK\tt\xf1\xab\xa1=\xddoJ\xd33\xfcҺ\"~n\x16v\xf5\xb3=d\xfa\xd57\x9f,\xbfl\x03F%\xaen\xc6aw$\x19\xb5\x9c\xcd\"\xcea\x92sY\x1a1\xbfl\xb4\xf4\xa0+\xbf\xefWm8\x8f\xe1|?\x9b~\x01[C\x9c>\xde\xd3\xcfIW/q\xd1\xde~\xfeا\x0fc\xeb\xeb\x97%C?\xa3\xb6\xfb\xfbՔh\x80\xe1\xf7\x03\xfdj^7ȯ:\xa1\x0f\xfc\xaa\x87\xf8\x10\xc5\x1e\xc9\xd1\a\xfb\xd5\x1cm\x88\x9f?\xf7\x18\xea\xe72u\x98\x9f\xd3\xfb\xe1~N\x10G\xd0gG\xb2\xacG\xf9e\x1a=\x9a\xa9\xf9H\x1a\xcc\xc7<\xf8\xc4\xcf\xdfw~\xea\xe7\x10\xf1\x99_\x16\x15\x9f\xf3\xb1/\xfc\x1cr\xbf4\xca\xe2+\xbfZ\xe3|\xedW\xbf[\xfa\x86\x19\xfe\x96\xe1\xef\xfc\xfc\"\xfc=[\xc2\x0f~\xf9\x14\xf0\xa3_\x16\x10?\xf9e\xd7\xecg$\x15/\xfdůz\x98_\xfd\xd2\xf7\xff\xe6\xe7z\xf2w\x96\xe8\x1f~\xb5\xd1\xf1\xa7\xaa\xd01~cs~\xac$\xf5/\x16\xff8\x16\xd8x?\x17Q\x13X\xd6\u007fK\xcf3\xd1\xcf\rQi7\x93\xa4\x8f\x99\xcc~e\x8a\x91\xf6\xa9\xf4\xeai~c\x9a4\xdd\xcf\xef\x063\x8c\u009f\xe9W\x9d\xe6,?\xe7o\xb3\xfd\xfc\xa1\xd2\x1c\xbf\xfc\x16\xeb_?\xfb\xe0\xb9F\x92\xe7\xa9\xf4\xcd7\xba\xb3\x05~\xfe*d!j\x1cw.\xf2sl\\\xec\xe7\xdag\tS\xbb\x94\rh\x19\xaa\x04]\xc6r\xa3\x8eW\x18\xbd\xdbJ\xa3\xb5\xac\xf2\x1bK\x80\xd5\xd2lְ\xa0\xd7\xfa\xd5\xef\xcd\xd7\xf9\xb9y\xbdޯ6\xf07\xd0U7\xfa\xf9S\x93M\x85\x1d\xe0f\xbec\x8b\x9f\xbf\xef\xda\xea7\xa6~\xdbؾ\xb7Ӂv\x18\xf9\xdcI\x9f\xd8\xe5\xe7\x96\xd9niP{\xfc\x9c\x93\xeeEф\xf3QR\xfb\x8c\xf6\xb6_\x9c\xea\x80\xdcu\x90\xa5y\xc8/\xbbۇ\xfd\\\xdf\x1e\xc1+b\xfaQ?ǎcF{=n8\xc0\t\xf1Ɠ\xc6pp\x8a\x15pZ\x95\xde\x19\xc9\xe5Y\xbfL\bϱ՟\xf7\xab\x1ff_\xf0\xabI\xf0Ez\xe6%\xbf\xda\x05\xbbl$\xfd\x8aQ\xfeW\xa5t\xaf\xf9մ\xf7\xba\x9f\x93\xb8\x1b\xe2%7\xfdƦ\xf1-v÷\xc57\xee\xf8\xd5\xf8u\xd7\xcfߖܓ~\xfa>]\xf5\x81t\xca\x0f\xfd\xf2c\xe6G\xfeĦJ\x1f\xb3S\x8e\xe9\xffc\x8a\x9f\b\xb0\x93\xd0\x02\xac[S@\xb9\xbd\x99L\b\xb0\x9fJ\f0\x96\"\x01~6/\x1a\x90\x9fj\x14\vp\xdb3\xa0j\xa6D\x80\xf3\x84'\x03j\xd8z* \xf9x:\xa0\xdc+W\u007f&\xa0r\xffl@9\xf8s\x01\xd9fy>\xc0\x06\xf5B\x80\x03\xf5\x8b\x01.Q_\n\xa8\xfe\xee\xe5\x00wd_\t\xa8\xca}5\xc0\xae\xfe\xb5\x80j>\xaf\a\x94o\xbc\x11\xe0\xe0\xf4f@\rzo\x050\x0f\xcf\xd6\xdf\x0e\x88\x9b\x95\x94\xdbK\x05\x12ZF\xf2u=\xc0\x82L\n\x14\xfe\xe3\v\xc9@\x19yA\xb2\x91\xbc\xb2\x01\xd9ux'\xc0\x89e\xb9\x80r\x03\x8b\xa4/%\xa0\xd6\a\xe5\x03\xb2?]!\xc0\x11\xb3b@\xf5_\x95\x98\xa4\xca\x01\x996U\t\xb0G\xb4\x06d\xf3\xd2\x160&\x98\xa9\x01\xd5\xe2\xed,\x81\xaa\x01\xa3_\xaf\x16P\xab\xdc\xea\x01\x8e\xae5\x02\xca\xc3\x1c\x01Y{\xd5\f\xa8F\xf0n@>J\xd5\np\xfb\xa2\xb6\xa4\xac\x0eK\xb0n\x80\x1db=&\xe2=\x96~\xfd\x80\xf4\x00\r\x02\tNHC\x95{,\xe7\x022\xbfklh\x93\x80\xf1\xfb\x81\xa6,\xc6f\x01\xf1\x89\xe6\x01N\x81[\x040\x91h\x19\x10Ol\x150\xda]Z\x80Ù\x93\x15\xe3\np\xb4q\a\xf85\xc1#\x89\xf22\x1d>\x16\x86\x9f\f\x04؛\x04\x03\\µ\x16i\x13Ps\x96\xb6\x01գ\xb5\v\xa8&\xda^\xa2\xe9@o\x8a顀j\x10\x1dY\xe9\x9d\x18O\xe7\x00=?\x1c\xe0Pҥ\xb0\x12\xd3\r\xef\xca\b\xa8uoDR\xd15\xa06i3\x03j37+ \xab\xa5h\x80\x13\xccn\xb8/\xacw\x97Ȳ\x03f_D\xcf\t\x18\xf3\xf1\\\x14\x18\xaa \xa6\xee\xc4b.\xc0\xef\xb5\xefK;\xc8c)\xe5\ad\xc0-\bp\xc2\x18\x0f\xa86\xd93 \x13\xd1^\x86\xe7\xf46\x8a\xadO@\xfe٠\xe1\xe3\xfd\x02\xec\x04\xfa\a\xd4\xd2h\x00\xeb|`\x80c\xd0 )\x94\x0f\x02\xaaO\xfe0 \x03\xd2`q\xa1!\x01\xae\x80\x87\x06dot\x18\x1db\xb8$\u007f\x04\xb39R\x1cgT\x80\xf3\x9e\xd1\xd28?\np\xfe\xfd\xb1\xe1\n\x9f\x04\xe4\xcb\xfb\xa7\x01\xf6۟\x05\xccވ\xfe9\xe3\xfaB\xbc\xf8\xcb\x00LJ\xaf\x02\x1cJ\xbe\xa6\xa3\u007f\x130>\x06\xb2R\xbf\vp\xc3\xe7\xfb\x80\xf1\xab\x89\x1f\x02\x1c<~4\xda\xcdO\x86\xfeL\xb7\xfc\x85\xf5\xf7+\x1b\xcfo\x01\xee\xf1\xff\x1e0>-\xfc!9\xfbS\xeajL@\xcd\x1a\xc6\x06\x8c\x8f<\u007f\x05\xd4Ri\x9c\xd4\xc4\xf8\x80\x8c:\x13\nk\xfbo&yb@\xfe=\xd4?R\xa2\x93\x02\xb2#O\x12?\x9f\xb9Z\xc0\xd7/\x94\x02_\x14\xe0\x97\x83\xc5\x01\xd5i/Af\x10\xc9\xd2\x00\x87\xf7e\xac\xe3\xe5R\x96+X\x14+U\x83Z\x15\xe0蹺\xb0寡\xff\xad5j|]@\xfd&d\xbd\xd1wm\bp\x9d\xb2\x91\xdd\xea&\xe9\xaa6\xb3\x93\xdb\x12\xe0\xfaok\x80\xf3\x98m\xf4\x9a\xed\x01\xf5\xaf*v\x18\x05\xb3\xd3\xe80v\x05ԧ\xf2\xdd\x01.\xc9\xf6\x04\xd4\x18\xbb\x97U\xb2/ \xeb\xf4\xfdl\xc9\a\x8c6t0\xc0\x99ࡀ\x1a\x06\x0e\xb3q\x1e\x91\xbc\x1e\x95\xb6p, \xbb\x9e\xc7\x19Չ\x80\x9aٜ\x94J8\x15\xe0\x98xZ\x12zF\x06\x80\xb3\x92\xc4sr\xf2\xbc\xf4\xd4\x17\xa4n/\xb2X.\xe1\xe9h\xbe~9\xc0\x1f\xae\\\t\x98\x9d\x99\xfaU\xbe\xffZ@\r\x8fו\x1f\x87\xf5\x1b\xec\xd8nJ\x83\xba\x15\x90\xc9\xc4mv\x1cw\x02jq\u007f\x97\xbc\x17\x90]\xb2\xfbҙ<\x90f\xf20\xa0v\xe2\x1fI)<\x16\xf9\x9f4\xe0'\x82\xaaUkA\xfeˊ\xa0\xcc\xe8\xcdA\xe3\x83q\x82q\"1ȍ\xe2\"A>R4\xc8E{\xb1 \xfc*\xa6\x17\x0f2\xa3%\x82*}O\x06\xa5\xe1?\x15\x94\x8dߧ\x83\xf4\x90g\x82\xe6\xa6a\xfd٠\x1aB\x9f3ny>\xa8\x9a\xd4\vA6\xa1\x17\x83\xecp^\n\xaa%\xc1\xcbA\xa9\x8cW\x82\xaa\xf7z5h\x8c\x1c\xaf\x05U3y=\xa8\x96\xb0o \xd5\x11\xfd\xcd G<\xf2mFW2\xc8݆RAՄt\xbe5)(\x9dO\xe9\xa0\xeap\xcb\x04\xe5\x9f+$\a\xc5\xd7\xcb\x06\xe1/\xc8\xda;FN\xcb\x05լ\xd2b\xa41%\xc8yR\xf9 \xb7\xe2*\x04\xb9\x84\xa8\x18\xe4HX)([\xb7\x95\x83\x1cS\xab\x18\x8fX\x83ʻlA~\\H\r\xca\xd2\xcd\x1ed\x13\xaa\x1a\xe4\x8eO5\xe3\xd6\xeaA\xe5=5\x82t\x1bGP;k\x06\xe5\x97\\\xef\x06\xa5A\xd7\n\xaaFW;\xc8FSG\x8a\xbf\xaeH\xbd\xa0\xaa\xd3\xf7\x82\xdcޮ\x1f\xc4\x1c\xabA\x90?\x19k\x18\xe4\xf6G\xa3 \xbd\xb2\xb1\x14p\x93 ;\xb7\xa6\xc1¯R͂\xea\xf7\xdf̓\xec)Z\x04\xf9{\xd2 ǶVAc͗f\x94\x89\x93%\xe7bR\xdct\x14\x8f\xa4\xd9\x1b\x14g\xf3\xb1f\xfcA\xf9\x1c\x1d\x907\x06\x83\xecU[\a\xd56v\x9b \xb7\xaa\xda\x06\xe9\xff\xed$\xd1\xed\x83l\x06\x1d\x82j\x1d\x1abat\f\xb2\x03\xebd\xd4N砚Ɇ%\x91]\x98\xc8\xf4\xa0\xf4\xe7\x19A\xee\x10F\x822;\xed\x1a\xe4\xfc6Sޝ\xc5TFU\xa1t\vrn\xd3=\xa8:\x91\xec \x9bw\x8e\x91\xb1ܠL_c\xf4\xdd\x1eA\xe9\x84ޗ\xb4\xe6\x05eN\x9c\xcf\xd7\x16\x04\xd9'ƃ\xfcz\xdb3\xa8\xdag\xaf w\x8b{\a\xb9\xf4\xef\x13\x94\xaf\xe9}\xf1\x0e\xb5u\x19d{\xee\x1fT\x83\xf2\x00r\xa0\x94\xc8 \xf1\x82\x0f\x82\xb2H\xfa\x90\rpp\x90\x93\x8e!AՉ\r\r\xb2\x0f\x1d\x16T}\xd7p)\xeb\x11A\xae#G\x06\xd5|`T\x90\xf3\xf4\xd1\xffG\xd4U\aJQ~m\x1a\x04\x15\x11\x10\x01\x91k\x80\x88\"\x16\x82\x8a\"(-!q\xb7c\xb6gwg/)e \n\x18\x94 )HwH\v(!)ݠ4\b\xd2)\x8d\xc0\xb7\xe7y\xce\xfe\xbe\u007ff\xee\xee\x9dy\xe3\xc4s\x9es\xdewf\xa1\x92\xef\xb2Y\xcc\xe8\x03\xcb\xebK\xb9\xf4\xe3]\xfd\xb3\x05\x86\ad\\\xe8\xfbl\x16/y\x1a\x946\x81tn\x87K\x06C\tC(\xbe\xa1\xd9\x12ԇe\x83\x05\x0f\xa7\x96Fp\xf0?\xc2\xcdF\xc2\xf6Fek\xc4\xfa)\x1b(3:[\"̘l,2\x8e\xcd\xce\xdf@\x1ck\\67r\x8dW\xa3\x9f\x80['Rϓp\xc7d\xf4=%\x9b9\xc4T8\xc24\xea|z6w\xa0\xcd\xc8F25\x13r\x9c\x95\xadd\xecg\xcai6\x9a\x9c\x03ḁd\xe6eKH\x9a\xcf^\x16ds]r\xa1Z\xd5/\xd9\\\xf8Y\x94͊\xe9\xe2l!\x11K \xfa_u\x98\xbf\xa9\x89,\xcd&\x1b[\x96\xcdؾ\x9c]\xae\xc8\xce\x140)\x9d\x95z\xdb*\x88t5\xa6\xb5F\xbb]\x9bM\xe6\xb1\x0e\xdf\xfe\x01C\\\x9f\x8dh\xb2A\x85\xb3\x91\u07b9)[\xc2\xdffLdK\xfa\uf72c\xadٺ\xc7%[R\xff\xedD\x95\x1d\xb4\x95\x9d\xecz\x97\xb6\xb1\x9bJ\xdd\xc3\xd3\xdel\x84\xf6?\xa9п\xa8\xbc}\xd9H.\xf6\xc3\xd5\x0e\xc8\xed\xa9\xac\x83\xda\xc3!^y\x986}\x847\x1c\xcd\x06\xf5:\x96\xcd\x15\xeb\xbf\xf5|\x9c\x16\u007f\"\x9b\x8bD\xff\xa8\x1b\x9d\xcc\xd6]V\xa7\xb2\xb9\xf9\xfct6\xf8ҙl\xd6>\xcf\xc2vα\xa3\xf3z\xd3\x05Z\xd9E\x06\x80K\x02\x94\xa9\xac\xcbz\xc7\x15\xce\xe2\xdflɢ\xae\xf2õl\xec\xef\xbf\xce\x1bn\xa4\xa7\x94\xfet3\xed*\xe9L/\x9b\xb5\x9e\xdb\b4wx\xc5\u007f\xd9(\x8b\xde\xe5\xa7{h\xea>;\xcde\xc3w\xb9m\x98l\x1e\x1b\x9e\x05\xc8k\x13\xc0\xcag\x13\xaf\xcfoc\x9eg\xe3X\v\xda\xd0u!\x1b)\xf0\x03\xb8\xa6\xb0\x8d\xec\xba\b\x1b{Ц\xe4\xf0!\x1b\x86\xfb\xb0\xde[Toz\xc4&\x96Y\xcc&F\xfe\xa8\xfe\xaf\xb8\x9eK\xd8ҀU\xd2&#|\f\xc7R8>n\xa3\xd8KK\xf7\xe1\xac26>\\h\x13CzBo-g\xe3\xa2ʓz.\x8f;\xb3l\x82\x16O\xe1\xef\xa7m\xacC>c\xd3\xfaܳ\x98h\x05\x9b8_E[\xbe\x86\xed̬\xe7p}%\x1b\xca\x0f\xcfs\x02\x95mx\xc8\x1es}\xd1&d\xa9\x8a\x8d\x8bv6qƪ\x18\xc6\xcb6\x01\xb1Wl\\7{Uϯٸ\xf3\xfeu\x1b\x13\xaej6B\xe4\x1b6\xacEU\xb7\x15\xac\x9d\xe8ء}Ǭ\x1a\xe9Y\x18\x96\x91\xf5\xa6M\v\x19o\xd9\x14\xb1\u07b6\t\x92״\xc1\xd4߱\x89\xa3\xbfk\x13\x9c\xabe\x03\xedxφ\xfaOm\x9e\xeaذ\f\xf7\xbe\r\xf1\xe5\x03\x1b\xd6\x18\xeb\xda\x10t\xea\xd9P\x9a\xadoC\U0007304d{\b\x1a\xda\x10\xf3\x1aٸ&ژ\x17}h\xc3\x13 Ml\x88\xb4MmX\x03o\x86\xf96\xb7\xf1\tC\xbd\xbf\x85M\x18XK\x1b\xf7,\xb5\x82\x18Z۸\xb0\x9f\x8d\x81\xdal\x8c#v\xfcϡ\xffs\xda\xe8v.v\xe1\xb6\t6xlR\xc3\xf0B\xc8>\x8e\xc4\x0fљY\x86MhO\xc0\x86e\x87\xa0\x8d\x94&d\x83\xb7\x84mR*\x8c\xb0\xa5(%\x11\xb3!)6m\xd8)\x16\xb7\xb1p\x99\xb0\x11\xf1\x926d\x82\x96Mʤ)\x1b\x1c$\a\xd3kcS\x1fnkc\xf9\xb0\x9dM\x9e\x83lo\x13r\xd9\xc1\x06h\xefh\x03\x8c|l\x93\x85\xfeN6!ߝmX\x17\xefbCq\xba+\xe6\xd1\xcd\xc6lφRƧ6\xfa\xe6g6`\xd8\xe7p\x80\xee6A\x84/8\xe4\x1e6\x16\xb3\xbf\xb4\t\x1c\u007fec8\xe8\xa9\xe7^\xb0\xd3\xde\x18\xc7\xd72\xe2.Y\xdf\xd8P\xf4\xfc\xd6ƗĨ\x0e\xfa\xd8X\x84\xec\v\xe9\xf7\xc3\xf0\xfa\xe3\xe6\x016\xd9\x17\xf5=\x875\xd0\x06\xa68Ȇe\xfa\x1f\b\x01\x83m\xc8 \x87ذ\xdbw\xa8\x8d\x8b-\xc30\xd6\xe16\xadO\x8e\xb0!\xa9\xf9цH3\x92\xa7Q\xf4\x89\x9flHLF\xab\xf0ưٱ6Ys\x1aG\x19\x8dW\x8f\x9d`C~:ц$g\x92\r%\x8a\xc9\x19\xf1O\xb1\xb1\x8c2U a\x9aM\xaa\x82\xd3Uy3lL\x8ff\xc2Nf\xd9P\xb9\xfc\x99B\x98mC\xa9a\x8e:\xda\\\x1bx\xfb<[\xfe\xfaF\"\x9c5߆\x92\xf4\x02\x9bd\xde\vU^\xbf\xd8ȶ\x17\xd9\x18\xe1\x16\xdb\xf8$\xca\x12\x1b\xca\f\xbfB\x97\xbf\xc1˗r:ˈ\x8a\xcbm\xc2\xfaV\xa8\xd6~O\xcf\\\x9e\xb4\xb7\xb1\xa0\xb6\xca&\x94j\xb5\x8d\x1cy\x8d\xba\xd7Z\x1b\x8a\xcb\xebl\xba\x9d\x85~\xba\x9e\xae\xbc\x01\x0e\xb2\xd1\x06\xae\xbd\xc9\xc6J\xc2f\x1b\xf3\xdf-6%\xa5[m\x88\xe9\xdbl\xa09\xdbm`\x1e;\xa8̝6\x94\x89w٤ҷ;sǞ\xf4\x90r,3k/\xbe\xfe\xd3\x06\n\xf8\x17\xbb\xdeG\xd0ޟ\x11\xfb\x01\x9bP݃6֞\x0eaD\x87\x01}Gp\xf7Q\x00\xe41\x1b\x97\xac\xff\xb6\xa1\xc0x\x1c\x9es\"\xd3\xdf?6,\xf1\x9f\xb4I\xe9\xfe\x14C\xc6i\x9b>\xf8|\xc6\xc6'\xd9\xce\xda4\xc5:\xa7\xe6zކE\x91\v\xf4\xe2\x8bP\xd1%\x1b\x1e7\xbbl\xc3\xf2\xe4\x15\x1bS\x97\u007f\tGW\xe95\xd7h\u007fס\xa0\x1b\xfc\xee&>ܢ\x02o\xc3\x0f\xeeظ\xe9\xf6?\xe0\xe9]\x1br\xf5{\x18\xe2}\xa0@.\xbbĥ\xdcvq\x97\x0f۹\xbb\xb8\xa8\x9d\x0fJ\xd8E\x8c\xc5\xec\f\x13\x8f\xf2\xcb\xe2v\x99s\t~(iǴ\x1e\xb3\xc3bK\xa1\x91\xc7\xed*\xe1\xd2v\xd8a\x19;\x02vY\xbb@\xf0\x13v\xe6\"\xe5\xec\xd0\xe8\x93vxWy\xbb\xf2\xb4,;<\xfb);\xb6\xe3<\xcd\xd33\x18\xf4\xb3v\xd1r\x05;\xf6/T\xb4s\x9b\xa7=\u007f\v\xb9\xad\x92L\xe8y;m\xba\xb2\x1d\x82\u007f\xc1\xceG\x96^\xc4\xddU\xecJ\x82_\x92K\xabځ\r/\xdba\xa4\xaf\xb0\x9bW\xed\xa2\x81\xd7\xecX]{ݮ\x91\xad\x9a\x1d\xfbW\xec\xc0\xe9\xea\x9cQ\r;\xeb\xaaoڵP\xf4\x16\x87\U000f6741\xb4\xa6\xfe\xff\x1d\x95\xfa\xbbv%\x1b\xb5T9\xef\xd9\v4\xef\"WԶ\x17l\x95\xd3\xce\f\x85\xb3\xeaؕ-\xbf\x9f\x19\xe9\a\xaa\x8c\xbavnq\xacG\x91\u05f7\xd3\xd5\x1a`\xb8\rqldg\xf0jl'u\xfe\xd0.$\xb9IF\x11M\xedt\xe4fv\xe0Ls\xbb\xf0\u05cfpla\x87\x9f\xb5\xb4K>\xdd\n\xb7\xb5\xb6\xb3v\x92\x9d\xb9\xdd\x06\xb5\xda\xd5\xda\x1cv\x80\x8333w\x97]b\xa2[\xad\xc9c',y\xedȫ|v\xee.\xf2\xd3\x1a\r;\xcb=\x01\xbb`b0\xd3A\xc8\x0e/\x0fۅ\xc2DT\x88Q\xbb@S\xcc\xce\xfd\v&U\x15\xa7n\x13\xdaN\xd2N\x90\xb6\xec\xa8\xf0\xa6\xec\x00\xe9\x1c;\xe2_\x1b{\xbe\xdai\xb2\xd2\xd6N\x88j\x97\xd1h{\x1dj\a;\x1d\xb5\xa3\x9d\xc9\xcb\xc7vI\xb5;\xd9%\x06u\xb6K\x85\xa3\x8bZ~W\xdaD7\xbb\xf0\x9aO2\xb6\xfa\xa9=\xb3\x93\xe13;<\xffs;\xaa(\xdd\xed\x88\\_\xd8\xf1\xe4Y\x0f;\x1f\xe7\xf9\xd2NF\xf1\x95]<\xbb'\xef\xe8\x05\x9b\xeem\xd7'\u07be\xe6\xec\xbeQu}kg$\xf9N\a\xd8\xc7\x0e\xd8\xe8k\x97ģ\x1f\x1d\xa8\xbf]+t\x03\xe8\xe8ߣŁv-\x99\x0f\xb2K\xf0\xff\xc1\x8e*\xd3`\xcap\b\xb4<4-f\x99\xfd0\x8cgx\xa6\x99\x11v-`\xfeHk\x1bi'\xb1\x19eg\xa5\xe3'\xbdm\xb4]\xa2\xca\x18;\x8b\xbdc\xf5<\xce.Qh\xbc]\x18\xeb\x04\xb5ɉv\"\xe7$;\xea\x1d\x93i>S ˩vd(\xd3x\x9a\xce\xd3\fLa\xa6]\xf9\xef,B\xcc\xcf<Ͷ\x03x\xe7\xd8YĘ\vC\x9ag\a\t\x9b\x9f\xf1\x9c\x05\xbch\xa1]\xca\x12\xbf\xd8%V,\x12\x81\xe7d-\xb6\xe7o\"\fv\t@\xe1\xd7̬\u007f\x83\x17-\xd59.\xb3#\xc8/\xb7c\x95\xd0.Q\xe3w\x80\xd7JU\xc8*\xbbn\xb7[\x8d\xb6ר\xbe\xd6\xda%\x1f_\xc7\xce\xff\xb0#\xfb[o\x17\xec\xde`\xe7\x82\xe5F;\x03\xfe&;S\xdb\xcdj`[\xe8\x00[y\xcf6(x\xbb]ʎ;\xe0\x05;\x01Ļ\xec\xa8j춣f\xb0G\xb1o\xafN\xe9O;\x82\xeb_v\xe5\xe6\xfb`\xc8\xfb\xa9\xf3\x03\x1c\xd4A;r\x85C:\x9c\xc3v\x92\xb6#z>j\xe7&\x18x\xf4\xdfvl\xba8\xaeC=\x91n!-\x94\u007f8ғl\xf6\x14\xfb<\r\xb5\x9f\xa1Ŝ%\xa2\x9c\xb3\xe3ك\xf3\xfc\U00082f60\x93\xber\xf1\u007f\u007f]\xcah\xf82\x04}\xc5\x0eF\xf1/B\xdbU;\xe8\xe7\xb5\xff]|\xdd.\x14\xf4\x06\x8e7\x19\xefn\xe1\xb6\xdb\xf8\xea\x0e\xcc\xf1?\xfe㮝\xa4잝\xeb\x9b\xf7\xed(\xb8\xe5r\xf0\xeb\xdc\x0e\xb9/\x8f\x03Δׁ\xf5B\a6c\xe5w\xa8^\v8\b\xe0\x05\x1d\xd8\xffW(\xfd\xef4\x91}\xc0\xc1-F\x85\x1d\xe0\x13E\x1c\xc8i\x1ft\x901?\xe4\x10\xfa\xf9\xb0\x03yGQ\a\xca@\x8f8t\xf5\xb8\x98\x83/\x1cu N\x16\xe7\u007fK8Pn,\xe9\x10u?\xe6\xc0\x82G)\a\x11\xedq\a\xe2\\i\x87\x80t\x19\al\xbf,OO8\xc4\xe8\xca9\x98\xcf>\xe9\xc8\u05faK\xfb\xac\xf2\x0e\xb0\xd6,\x9e\x9er\x88S?\xed\xe0f\xe2g\xf4\xfc\xac\x03ѫ\x82\x03\xb5\x91\x8a\x0e\xa2\xcas\x0e\xf1\x92J\x0e)\xe2>\xef \xaeVv \xd7x\x01Sz\xd1\xc1\x8dYU\x1c\b\x9a/9H\x9a\xab:\xb8Q\xe6e\a6\x9b\xbc\xe2\x80\xda_u %{\x8d\r\xbc\xee@jW\xcd\x01ky\xc3!NT\x1d\xe2\xaf\xe1\x00\x1f~\xd3\x01N\xf9\x16g\xf6v\xfa\xcbN\x1d\xb3j:Ԅ\xdfq\xa00\xf1./\xaa\xe5\x90\x05\xb1\xf7\x1cy릲j;H#\xea8Ȇ\xdfw\bf|\xc0~\xea:\x90p\xd4s0\x95\xaf\x0fa4p\xe0\xd5\x00\rU\xb3\x8d\x1c\b\x15\x8d\x1dL$\x1dt\xec&TSS*\xa6YZ\x97\xe9\xd17\xd7>>B;-\x1c\x88\xa1-\x1dX\x9ai\xc5\xe9\xb6Ɯ\xb23\xe6cs\x80\xde\xdb\x1d\f~\x0e^䄞]\x0e\x8d\xf3n\a\xf6/y\x1c\xdc]\xe5u\xc0\xb1|\x0e&\xac~\x1d\x91\xe1\x90L*\xe0\xc0\xceĠ\x83\x919\xe4@~\x19\xc6\xff\"\x0e\x81\xc0(\xfe\x8eeF`\xaa|\xe2\x14l\x82\x82Lf\xfemQY)\xce>\x87\x06\xd1\x06\xeai\xabw\xb6\xe3\x9d\xed\x1d\xb2?\xbf\x03\x8e\x1d\x1d\\>\xfc\x98\xa2\xec\xe4 xuv\x90\xd4tqH\\\xe9\xea\xc0Ja7\aJR\x9f8\x90h}\xea\x00d}\xe6`\x0e\xf3\xb9\x03\xa1\xa0\xbb\x03\xcf\x16:\xb8\xa6\xdf\xc3!e\xd4/)\x95\xaf\x1c|h\xb2\xa7\x83IT/G\x06\bz\xc3&\xbfv\xc8b\xc27\x0eT\x1f\xbfu\x00j\xbe\xc3\u007f\xfa8\x18g\xfb\xd2\x16\xfaQ\xcf\xfd\x1d \b\x03\x1cJZ\xbe\x87-\rt\b\xb7\x1b\x84a\xfc\xa0\x82\x1f\x9c\xb9d\b\xcd}\xa8\x03qu\x18%8\xdc\xc15\xb8\x11\x0e\x90\xf6\x1f)Ǒ\x0eT\xb5G\xfdo\x90?\xa5g\x9f\x16\xc2h\x8ea\x8cC\x16\x88\xc6:P\xb2\x1bG\x99\x8f\x87\xb0'8$\fMt\x80\\NRӜ\xac\x100\xc5\xc1ll*\x14<\r\x97Nw \u007f\x9d\x81\x81\xcft\b\xec\xcer\b\xed\xff\xd9\xc1z\xfbl\ak8s\x1cL\x8a\xe7»\xe7Qc\xf3\xb5\xe5\x05t˅\x0e2\xc9_\b\x85\x8b\x1cif\xbc\xd8\x01\xaa\xb3\xc4!\xe1\xf9WX\xfco\x18\xeaR\a7\xc8,s`\xdb\xf8r\xb9x\x85CB\xd4\xef\x0e\xc1ޕ\x0eI\x9eW94\x81Z\xed\xe0\x03\x12\x0eTz\xd6:\x84\x86\xac\x83\x82\xfeP\xbc\\\xef\x90e\xbb\r8n\xa4\x1c79\xb0r\xb3\xd9A6\xb1E\x91f\xab\x03Lj\x9b\x03${;\x10{\x87C\x8a\xa3;\x1dL\x8ew9\x10\xd7v\xa3\xb1=\x0e\t\xb9{\xb5\x91?\x1d(I\xffE\x1b\xd9G\xa5\xeewpa\xee@\xc6!\x0e:\xc0\xc7\x0e\xc1\x14\x0e;Pl8\xe2P\x12uT\xe0 '\xeb\x98C\xd3I\ak\xcelj\xb5'0\xb7\u007f\xd4\tN\xea\xf9\x14\x10ᴃ\x19\xea\x19J\xf8,\x9a?\x87\xe3y\xf5\x85\v\x0eng\xb8\xe8к\xc5%\a*_\x97\t\x90WhB\xff:\xb8\xe9\xec\xaa\x03\xa9\xf35\x9a\xdfu\xfe\xf3\x86\x03eқ\xfct\x8b.w[\xf5u\x87\xa0\xff\x9f\x03\xa4\xf4.\xed\xe7\x9eB\xe3}\b,\x97\x93Ox\xe6vB\x8ay\x9c\xf4\xb9\xbcNY;\xcd\xe7\x04\xa9\xcd\xef\x14\x06R\xc0\x99VPNVA'%P\xc8\xc9*\xe6\x03N֭\n;9\xa7\"Nn\x1fz\xd0)D\xe2!\xa7\xd2\xfc\x87\x9d\xac\xbe\x17u2.?\xe2\xc4s\x12N\xac\xb9>\xea\x14+/\xce!\x94p\n\t(\xe9\xc4.\x92ǜ\xac\xed\x97r\x12~\x1ew\x16h\n\xa5\x95vR\xfbe\x9c\xc0\x9a\xb2N\xbc\xc6͉\xc7\xd5\xcb9!\xa1'\x9dx2\xb9\xbc\x93\xaa\xcbrR(O9\xa1\x8f\xa7\x9d\xa8\x90<\xe3\x94\xe0\xf1\xac3\xe3\xbd\x15\x9ciۮ\xe8\x84\xdd<\xe7\xc4k\x1e*\xf1\xca\xe7\x9d\xdcgP\x19\x83}\xc1\t\x81\xbf\x88\x0fU\x9c\x82\xee/\xf1\xab\xaa\x18\xca\xcbN\xb1\xd1W\xf0\xf7\xab\x10\xe6kN\xbe\x89[\xe5T\xcd)\x96\xfa\x86S\xea\x82\xd5eh\xe9\x9ej8\xc5\xe7\xde\xd4+\xder\x92~\xbf\xed$\x89\xae\xe9\xd4$\xe6\x1d\xa7\xb0\xbbw\x9d\xc2\x0ek\xc9xߓCm*\xab\x8e\x0e\xf3}'\xeb\xf3\x1f8\xb1߫.\xba\xab\xe7\x94\x12e}'j\x16\rT\xa6\r\x9d(\x864r2Yi\xac\x9a\xfa\xd0\t\xb7h\x8215u\xa2\xa0\xdd\fZk.\xdd}\xe4\x94\xcd\x17-\x9c\xe0\x96-\x9d@\xeeV\xd4`k'\x9c5\xdbIBas\nHٝ4;\x87S\xab\xabNT\x06\\\xfa\xd1\xedd@\xf18\x19\xfb\xbc\xaa_\x9fS(\x9e\x1f\x124h\x13\x01\xa7`R\xd0\t\xdb\x0e9\xb9\xb2\x16VC\x89`\x84Qm5\xe6DiǤf\xe2NP\xec\x84\x13L1\xc9\x06,\x18w\n\xe2ɁͶ\xd1!\xb4U=\xb4K\x8f\xb4\x93\x91\xd5މ\x85\xe6\x0eN\xa6\xd9\x1d\x9d\\:\xfaXͪ\x93\x93\xb5\xfb\xceN\xd2\xfc.N\xa0MW\x18H7Z\xe9'Nli\xff\xd4\tN\xf3\x99\x13H\xfc\xb9S \xb4;,\xe5\v\n\xb0\x87S\xd6\xfb\xbeL_\x9d\xfe\xffWN\xa0rO'\xe2]/'\x99\u007fo'*\xe6_S/ߠ\x97oU\x86\xdf9\xb9٭\x8f\x13<\xb2/DҏJ\xe9\xefdAa\x00L\xf6{\xa7\x06\xbc\x81\xecd\x90S\xb3\xa8\x1fԊ\x06;\xa5\xe03D\xe7<\xd4ɠ1\x8cm\r\xe7hG\xf0\xde\x1f\x9d\f0#\x9d\xcc\xe1G9Q\x82\xfe\ts\x19\xedD(\x1e\xe3D\xb4\x1b\xeb$\xcf\x18\xc7\x01\x8ew\x02\xfa'\xc0e&:\xb905\x89\x8dO\x86\xe6\xa78A,\xa6\xc2\x18\xa69\x85\xeeL\xa7;\xcdp櫓\x16\xd2L\bq\x16%\xfa\xb3\x13@7\xdb\t\n7\x87\x8a\x98\xeb\x94\\n\x1e\xa6=_\x87\xba@\xe7\xb5\xd0\xc97\b\xfc\xe2\x14\x1a\xb2\xc8\t\xb8\\\xecD%a\tg\xf7\xab\x13\xb4\xeb7\xf4\xbd\xd4)\fd\x99\x131`\xb9\x13\x84q\x05\x15\xf2;u\xbb҉\xea\xdd*\x95\xc5j\xedp\x8d\x13\xf8\xbc\xd6ɤq\x9d\x93\xc8\xfe\x87\x8ec\xbdS\xa2\xc3\x06\xa7\xf0\xfe\x8dN\t\x1f9Y\x9b\xd4\n7;\xc1+\xb78\xb9'f\xab\x93\x95\x8dmNd\x15\u06dd(c\xed\x00&\xec\xc4Tw\x11\xc4v;%\x18\xedqr\xb3\xc7^\x98ٟN\xa6\x8b\xaa\xce}N\xc4\xd8\xfd\xb8\xf2\x80S\v\x12\a\x9d\\:<\xa4\x83=\f7>\x02\x179\n\xd4S5|\xae\x03\xea\xaev\xf1\x05\x05\xdc\xc3E\xb0\xfe҅}8tמ.\xbev\xac\x97K\x1fy\xec\r\v\xfd\xdaŲ\xcf7.\tcߺ\xa4\x96\xfd\x9d\x8b\x8c\xb7\x8f\v\xb4\xa7\xaf\vQ\xb6\x9f\vQ\xa2\xbf\x8b\xfcw\x00\xfc\xe4{\x17i\xc4@\x17Wf\x06A\xfb?p\x1c\x83]\f\xf4C\xf4\x96\xa1.\xae9\fs\xf1}2\xc3]\xcc\xcaF\xb8H\xb3~d\u007f#\xd1\xf2(\xea\xe8'\xba\xf6h\x9eƸt\xc1i\xac\xda\xfc8\x97P\xe1\xf1.\xc2\xf4\x04^5\xd1\x05\xc6?I\x1d}2ڛ\xa2\xde:\x95\xd7LS\x8b\x9e\xeeb\x05}\x86Ki\xf8L\x17\xeaV\xb3\xf4\xfb\x9f՜f\xbb\xb0\x9d`\x8e\v\x85\x8d\xb9\x1c\xdc<\xda\xc5|\x9a\xf7\x02\xbdt\xa1\v\x01\xee\x17\x1d\xe2\"\x17+I\x8bU+K\\\xca ~\x85\xac~s\xe1\x11\xb9\xa5.\xa9\a\xa7\x13H\x17\u07be\xb5\xdc%\x11n\x85\v4\xf4w\x176֬\x84\xb9\xaeRW^\x8dOk\\\xa0hk\xd1\xd4:6\xf5\x87\x8b\xb5\xaf\xf5.Ԓ7\xb8\x18a7\xba\x98{m\x82\xd9mvq\xe3\xd6\x16\x17_\xb2\xb3U\x85\xb5\x8d\x10\xbc݅X\xbdÅ\x90\xb4\x13\xa6\xb2\x8b\x92ۭw\xee\xe1%{\xf5\xe3\x9f\f\x03\u007f\xe1\xd2}\x94\xcf~\x9d\xd4\x01\xbd\xe6 o9\xe4\x92\xe8r\u0605H\u007f\xc4%\xbc\xf2\xa8\vk\xf9\xc7x\xc1\xdf.\x89p\xc7]\x12\xb8N\xb8\x18\x84\xfea\xef'iZ\xa7ԚO\xab\x90\xcf0,\x9cu\x81D\x9d\xd3~\xcf3\xf4\\\xd0&.\xba\x90;^ҩ^\x86]\\\xc1\xf1_\x0e\xf8*\xfb\xbf\x961\xb3\xeb\xfc\xfa\x86\x8b\xb5\x87\x9b\x04\x9f[.,\xd7\xdc\x06\xa4\xdcq\xa12\xf5\x9f\v\xd5\xe7\xbb.I<\xee\xf1\x82\xfb\xea[\xb9ܒS\xe4v\xb3\x917\x8aW\xf9\xddb\xc8\x05ܚq\x17\xcc\xfcQ(s\xf5\x03n̰\xb0\x1b\xd5\xda\"n\xf4\xfa\xa0\x1b\x18\xf3\x90\x1b\xfd=\xec\xc6\x18\x8a\xbai\x87\x8f\xb8\xc5B\x8a\xb9\x99m>\xea\x96G\x16\x8a\xe3X\x02ǒn\xd9Y\xfa\x98\x9bfQ\x8aM=\ue9b5\x96vkz_&\xf3GY\xb7h\xed\t\xdcTέ\xcc\xe5I7E[\xdeMw\xca\xd2\xf3Sn\xfe\xa4\x93\x9b\x85\xaag\xf4\xfc\xac\x1b\x9eYA直\xe7\xe7܀\x96Jnf\xbe\xcf\xebՕ\xf5\xfc\x82\x9e_D\xdfUx\xf1KnT\u0aba\xc1-_v+M\u007f\xc5\r\x1e\xfc\xaa\x1b[F^s\v\xb6\xbf\xce\x0f\xd5\xdcHV\xdf\xe0\xa7\xea<\xd5pì\xdetÏ\xder\x83ؾ\xed\xc6\x0f\x97\xd4t\vL\xbf\xe3FX\u007f\x97\xf2\xaf\xe5f`~\xcf-f[\xdb-<\xb2NZfx\x1f\xb8[\xb8\xcd\ana!u\xf1\xffznD\xb7\xfan\xe6y\rܒ\xcb6t\v\xdfo\xc4\u007f5v\x8b\x91\u007f\xe8f\x99\xb3\tGӔ\xa7fn\x16\x0e\x9aC\x91\x1f\xb9a\xdb-TA-\xd1Y+\f\xb1\xb5\x9bl.\x1b\xdf\xd9({\xbb\x1b\x9c\xd5\xe1N\xa7\xa2N72_\x976\xe8\xc6\b\x00#9\xa8\xcd\x1c\xe2Շ)\xbf#n>\xe3qԍծc\xb8\xe1o\xda\xdeq7i\xf8\t\xb7\u058b\xffq\x93\xe8\x9d\xd4\u007f\x9c\x929\x9d\x96\xc3\x19\x18\xf7Y\xb7,z\x9ds\xcb\xee\x9e\xf3n\xae\xd3^p\x83\xc8\\\u0530sIϗ\xddH\x87\xaf\xa0\xbf\u007f9\x96\xabjx\xd7\xdc$j\xd7is7ԁn\xba\x95\x1b\xdd\x028\xdcvs'\xc9\x1d7\xc2\xe9\u007fn\xac^\xdc\xe5<\xee\x11H\xefcX\xb9_\xf6\xf0w\f=\xb2*\xf9\xaaG\xf9\xe4k\x1e\x91\xf9\xeb췚\x87\xd8\xf0\x86\x87j\xaf\xeeA\xad\xa9\x86\a\x9b\b\xde\xf4H\x00x\xcb#v\xfd\xb6\av[\xd3\x03\xc3~\xc7\x03\x9e\xfb.\x87]\xcb#\xb5S\x0f(Wm*\xb1\x8e\a\x0f\xdcs&\x1fx\xc0\x16\xebz\x10K\xeby\xc4\xdc\xeb\xe3\xd8\xc0C\x92\xd5\xd0â_#\x8f.g7\xe6-\x1fz\x88\x8aM<\x82\xacM1\xf4f\x1e\xe6\a\xcd=@\xbe\x8f(\x89\x16\xec\xaa%O\xad8\xbd\xd6zi\xb6\apj\xf3\x00\xd5\xed\x1eRl\x87\xa7\xb0\xec\xc4m\x1f3SF\x96\xd3\x03N\xe7B\x17n\xcf\x03\x8dÝ\xe4?\xe9\xf8\xe7AP\xf4zĨ}\x1eP(\xbf\a\xbf\x06\xe5\xe1Jb\xc0\xc3W/\x06=LdB\x1e\xfaG\x98\x8a\x8bx\xa4:\x10\xf5 Ŏa\"\xa6\xaa6\xee\xa1\x13&<\x92\xdd%9hK\x15\x9d\xf2\xb0|\x9a\xe3\xc1KSڨ(\xdazX%h\xe7A\x92מ\xff\xed\x00qv\xa4\xc6?\xf6\xa0\xc4\xd5I{\xe9\xacR\xe8\xe2Q\xb6\xd8\xd5\xc3\xe8\xdb\xcd\xc3\xca\xd4'\x1e&\x9f\x9fz\x80x\x9fy\x10\x91>\xf70\xdfꎙ\u007fA1\xf4\xf0hU\xebK\xb5\xb8\xaf\xf4\xdc\xd3#Ѩ\x97\a\xcfI\xf5\xf6\b\xbc\u007f\xedњ\xca7\x1e<\x05\xf2-O\xdfy\x00\x1b}x\xea\vQ\xf6\x83\xd8\xfb{$\xa3\x1d\xe0a\xe0\xfeޣ\x8b\xbc\x03=$\x15\x83\xf0\xef\x1f\xa8\xa8\xc1\x1eb\xdc\x10*|(Ac\x98\x8ef8a`\x84N\xf4G=\x8f\U0010090d\xf2\xb0&\xf5\x13]v\xb4\ady\x8c\xb68փLx\x1cm{\xbc\xaah\x02?N\xa4\xb5M\xd2~&\xc37\xa6x\x10c\xa6z\x80\x99\xd3<\x82\xb2\xd3q\x9c\x81\xff\xcf\x14\xff\x98\xa5@\xf6345\xdb\x03R4\xc7#!h.d0\xcf#\xb5\xc0\xf9\x90\xe3\x02\x8aj!\xf5\xf9\x8bG8\xd0\"5\xdb\xc5\x14\xdc\x12\x8f\xc6F\x8f<.\xf1\x1b\x04\xb8ԃ躌\xfe\xbf\x9cw\xaf\xa0\x19\xfe\xee\x11(_I\x17X\xa52^\xad\xe75jkk=,\x19\xacÈ\xfe\x00|\xaeW\xebؠ\xff\xdb\xe8A\xb8\xddD\xc1o\xa6\xd1n\xe1ܷz\x18\x13\xb7y\xf80\xd8v\x0f_\x8f\xb7\xc3\xc3ȴS\xfb٥\xe7\xdd\x1c\xe2\x1e\xb6\xb5\x97\xa7?y\xfa\x8bM\xee\xe3\xa7\xfd\x1e\xdd\xfew\x80\xea9\x98\xf9|\xc8\xc3\xf2\xc0\xe1\f\xc0\x1d\xf1\xb0\xd8pԃ0~L\x87\U000b778f{\xb0\xfaw\xc2\xc3\xcc\xfd\x1f\x0fH\xd2IU\xf3)L\xf9\xb4\xfe\xf3Lz\xaa\xe1D\xd6Y8\xed9O\xbe\xe6\x9dҁ\x93\xa7\v\x1e>\xe9r\x91\x88y\x89\xa7\xcbP\xee\x15h\xfd_\x8f$~W=\xc2Ȯ\x11=\xaf{\xb8\xa0|#c\xda7q\xe5-\x9a\xe5m\x0e\xf8\x8e\x87\xebl\xffy\xf27\x8f\x19\xed\xb3\xee\xf2\xeb{\xbc\xe8\xbeG(J.\xaf(=\xb7Wr\x88<^@r^\xaf\x84\xf5|^:m~\xaf\x16\xdc\nxQ\x1e-\xe8\x05Z\x14\xe2\xb5\x0fxAD\v{e\x01\xbf\x88\x17c{\xd0+*\u007f\xc8+$\xe5a/\xd6H\x8az\xa1\xdbG\xbc\xea\xf3żܽ\xf8\xa8\x17\xa4\xae\xb8W\xea\x8a%\xbc\xe0{%\xd1\xd6c^\xbcN\xa4\x94W\x06\xf9\xb8\x97\xa5\xd4\xd2젌\x97\x8a*\xeb\x157~\x82_\x96\xf3\x8a\xd1?镤\xb1\xbc\x175U\x1c\x9f\xf22\ayڋ\xc7'\x9e\xf1R&\xcfz\xa5\xdaU!3\xb9\x8a\x1c\xc8s^\x8d\xa6\x95\xbc\\\xff{\xdeKmV\xf6\xb2\xa8\xf1\x02\xa7\xfd\xa2\x17\\\xa7\nf\xfa\x92\x97\x9b,\xab\xb2\x8d\x97\xbd0\xabW\xbc\xacҾ\xaa\xfd\xbf\x96\x99\xfb\xeb^<\xbf\\͋\xe2\xd3\x1b\xbc\xa9:\x05Tëy\ue6fc\xe8-\xaf0\x8d\xb7\xbd\x12wkzA\x03\xde\U0004257e\xeb\x95rk-/6辗\x19vm\xaf\x14|\xebx\x85\x12\xbe\xcf&?\xf0JE\xb7.\xe4X\x0f\x12\xa9\xaf\xe3m\xa0#k\xe8-\xd8\xca\xe8\xda1\x1d\xba\x1ayi\xad\x8d\xbdXZ\xf9Ы\x8e\xd0\xc4\v\xa0h\x8av\x9byA֛{\xf9b\U000cff28y\xb4\xf0jB\xdc\xd2\xcb\x04\xab\x95\x17\xe9`k\xaf\xb0\xd8l/W2m\x9c\x83\x1d\x13r\xe0_N\x1d\x8d+#\x1f\xb7W\xb7\x95{\xbc\x84\v/\xc6\ue8da\xfd0+\x83\xb6\x18\xf0f\x1e\xcc\f\u0080C鱤\xa7\x1c\xf6\xf2\xe7\x131ܨj0\xa6\xad\x99\xb4ظ\x97uڄv\x9fTuY^\x16W\xbd\xc8\xder\xbc@\xbc6^\"n[/\x88B;/\xe8G{\x95_\a/\xb7\x15t\xf42\xae~\xece\x89\xab\x93\x9e;{Y'\xef\xc2ۺz\x81\xffݼ$,\x9fxA\x89?\xe5\xe93/^\xf6\xed\x05Ru\xf7\n\x89\xfb\xc2KF\xd3\x03B\xfbҫ\x1b\x13\xbe\xf2f\x16\xc8{\xc2\xe6{yY\x85\xeb\rI|\xedEl\xfb\xc6\v\xfa\xf5\xed\xff\xe4\xf4\x1d\x8d\xb3\x8fW\xd8h_/\xde\xcbя\x93\xec\x0f\xdb\x19\xe0\x15\xba\xf5\xbdWpj \xe58\xc8+\x9b\xdc\u007f\xf0\x12\xb4\a\xc3φx\xb1\xa9k\xa8\x97\xe8;\f\xed\r\xf7\xeav\x1c/\t\xfb\x8f^Y\x99\x18\t\xf5\x8d\xf22)\xf9\xc9\xcbղ\xd1^\x14\x03\xc6x\x15\xbf\xc6\xc24\xc7Q\xc9\xe3\xbd\xcc\xda&\xf0\xe3D/\xf2\xb1I0\xf9\xc9^\xc0\xec\x14/\x8bNS\xbd\xc2)\xa6qZ\xd31\xd2\x19\xda\xc5L\x15\xdc,\x8a\xf6g\xc0\xdal\x85\xb59\xde\xcc/Iy\xc1\xe9\xe7y\x91\xb1·\xfc\x17x\xf1\xac\x87\x17\xb5\x84_\xbc\xe46\x8b\xbc\xa4K\x8b\xbd\xc8N\x97xɉ~\xe5U\xbf\xa9\xf8\x97z%|.\xc3q\xb9\xcac\x85\x17ώ\xff\xee\x95U\xb7\x95\x99\x19\xaf\xa2\x04V{Q@Z\xe3\x95Dr-\x9di\x9d\x17\x14\xef\x0f\x82\xc0zE\xbf\r^f7\x1b\xd9\xdc&/\vi\x9b\xbd\xd8N\xb0E\a\xb0\x95\xedm\xe3i;\xed`\x87W\xb8\xc0N/v\xce\xec\xf2\x82\x80\xed\xf6\"\xb6\xef\xf1\x92\xd1\xec%\x10\xfd\t\xfd\xff\x05m\xec\xa3\x13\xecW\xcf9\x00Q\x1f\xf4J0:\xe4\xc5j\xc7a\x9d\xe1\x91\fn\x1d\x85\xe1\x1c\xe3\xd4\xfe\xce\xd8\xea\xf1t\xc79\xc1\xac\x13^\xeeA\xfe\a&zҫ\xa4\xee\x94\x17\f\xe6\xb4\x17L\xeb\fOguX\xe70\x9e\xf3\xb4\xc4\vj\x83\x17\xd5\x1f/\xa9\x86/\xabj\xaex\x91d\xea\xb7W9\xc1k^D\xbd\xeb\xf4\xa9\x1b\xfaϛ0\xe3[^\xa9\x18\xdcVIޡ\xf4\xff\xf3b\xbd\xec.?\xddSL\xbc\xaf^\x9eˇ\x1d\x87\xb9}\xdc}\x9a\xc7'\x92\xca\xeb\xc3O\x0e\xfb\xf8Xu~\x1f \xa6\x80O\x18@A\x1f\xfa-\x84\xeb\x1e\xf0q\x0fba\x9fH\xa0\b\xbe{\x10LJ|R\x10x؇\x97\x04\x14\xf5!\xca<\x82\xef\x8a\xf1ã<\x15\xf7q8%|\xac\xe0\x95\xf4a\x92\x8f\xf9\x04\x16K\xf9\x10-}\xcc\x1aJ\xfbD\x17e|\xa8B\x96\xf5\xa1\xf0\xf1\x84O\x99{9\x9f\xb0\x89'}̸\xcb\xfbD\x1aY>\x85\xe0\xa7|\xe0\xa6O\xfb\xc0\xfe\x9fA\xab\xcf\xe2\x92\n>\xc1Ԋ>\xca\xff9\xf4Z\xc9G>\xf4\xbcO\x96\xb4*\xfbH\x9a^\xf01b\xbe\x88\xe9U\xc1}/\xf9H\xea\xaa\xfa\x10\xfb^\xf6\xb1T\xfc\x8a\x0f\x9b0^\xf5qU\xe55m\xe1u\x1f\xc0\xabZfTod\xc6^ݧ\x96S\xc3\xc7g\a\xde\xf4\xf17\x863\u07ff\r\xf1\xd6\xc4\xf1\x1d\x1f\xea\xdd\xef\xf2\x92Z>}\xe4ÇrU\xed\xf4?ӆZ\xc7'\xbb\xe5\xde\xc7\xf5\x1f\xf8\xb0\xa8Q7\xadK#jf\xd5\xf3\xe1\xf5\xe4\xf5}\x84\x9b\x06l\xa6\xa1\x0f\x9e\xd4(\xd3ac\x15\xf9\x87\x98f\x13\xfe\xb7)gٌ\xc2l\xeeCQ\x15\xc7\x16>\xc0UK\x1fӼV>\xb1\xf1\xd68f\xfb\xf8ت\xcd\a\xeb\xb3\xfb\x80\x80\x0e~r\xf2\xe4\x82H\xdd8zp\xf4\xe2\xe8\xf3a稟'\xc3\xc7@\x1c\xf01\xfc\x04}\xd8t\x19\xf2\x11\x88ô\xcb\bOQ\x9f\x00V\x8cFb\xfa\xb0\f\x14\xf71h'\xd4Ԓ>,\x15Y*\xf4\x94\x8fN\x97\xe3\xe3F\x866\x19=\xb5\xf5\xd1K۱\xb5\xf6\x87Uv\xd7+\xbf\xf0)\x03\xea\xe1\x93\xc7F\xbe\u052f\xbfRS\xea\xe9\x03\xb8\xf5\xf2\t_\xeb\xed\x03 |\xed\xc3k\xc0\xbe\xf1\xe9\x1b\xdf|\\]\xfd\x0ej\xe8\xe3\x03\xb1\xea듽.\xfd|\x1a+\xfaS\x1b\x03|\xf8\xbdEm{\xa0O`q\x90\x8f\xab\xb1?\xf8\xf8\xb3\xf9>D\x95!\xfa\xedP\x9f\xbcyc\x98\x0f\xaf#\x1b\x8eQ\x8c\xe0\x85?\x029F\xfa\x14?G\xb1\x87\x9f\xf4\xbe\xd1>2\xdd1ln,\xb44\xce\a\xbc\x1c\xefC؞\xa0\x16=\x91\xe2\x99\xe4\x03;\x9b\xac\xf04ŧ\x14n*\xc45\x8dS\x9f\xee\x035\x9c\xa1ҟ\xe9\x03#\x99\xc5o\u007f\xc6\xf0fg\x04:G\xd5;\x97\xed\xcf\xc3L\xe6g\xfe\xbb \xf3\xc7B\xfa\xc9/\xf8\xf7\"\x9f\x96\xd2\x16S\x81K|L\x88\u007fe\x0f\xbf\xe9ǥ4\xe7e>Th\x96\xfb\x98A\xae\xf0i\x80\xfc݇Ű\x95>)\x01\xac\xf2!\xe0\xac\xf6!ڮ\x81E\xac\x85\xb1\xac\xf3\xc9k\xcb\xfe\xf0\xa1 \xbf\xde\a\xa6\xba\xc1\xc7(\xbfч\x02\xeb&\x18\xdbf^\xb2\x85\xa7\xad>F\xb5m>\xec\x97۞\xd6b8k\a\x8e;} j\xbb\xa0\x9a\xdd\xc4\xdd=>,\xc5\xed\xa5~\xfeT\xfb\xfa\xcb\xc7\x15\xa4}\x90\xd9~\x1fX\xe9\x01\xb5\xff\x83>\xae\xc2\x1c\xf2\xa1Px\x98\xa7#\x19S?\x9a\x11\xd21\xe0\xf4\xdf\x18\xe3q\xfc}\x02\xcd\xfd\xe3\x93\xc4\xff\xa4\x0f\xec\xe5\x14\xc1\xe4\xb4\x1aƙL+g}\x8c\xa9\xe7`\x1a\xe7U\xed\x178̋\x98\xc1%\x1fꐗ}\\ȹ\xe2Þ\x84\u007f}X\x15\xb9\xea#i\xbd\xe6cx\xbb\xee\xc3&\xc0\x1b>}T\xe2\xa6\x0f{\xa7o\xe9\x84o+\xf2ߡ:\xfe\xf3\x91\x1b\xddE \xb8\xc7\xd0x\x9f\xe1/\x97\x9f\x0e\x95ۏ\xfe\xf2\xf8ɗ\xf2\xfae\x86\xf9\xfc\xf2DW~\xbf\x9a}\x01\u007f\x86\xa0\x16\xf4K\xd0,\xe4G\xb4\xf4\xc3\x00\n\xfb\x19_\x8a\xf8!\xc0\a\xfdH\x13\x1f\xf2\x83\a>\x9c>ɏa\xf8\xe9ŏ\xf8\x81\xde\xc5\xfcXZy\xd4/\x11\xa9\xb8\x1f\xe9\u007f\t\xbf\x9ajI?\x12L\xbf\xd0\xc1R~\xa4\x99\x8f\xfb\xa5\xdaY\xda/\xb3(\x83\x11\x94\xf5\xcbN\xed'\xfc\xfa|\xa4\x1f\xbf\xca\xefg\x8ePޏh\xe9\a\xa6<\x85\xa1>\xed\x17\x10x\xc6\xcf\xdf\xe3\xf7\x8b\xafU\xf0\x8bB+\xfa\x89\x96\xcf\xf9\x89\u0095\xfc\\\xb2y\xde/\x95\x98\xca~>\xcc\xf1\x02\xeex\xd1/j\xaf\xe2\x17\xdb~\x89MU\xf5c\u007f\xf7\xcb~\x86\xc6W\xfcT\xff\xab~\xa6\x16\xaf\xf9ս_\xf7\v$U\U000c3c3dᗼ\xa1:Z\xab\x81\xe3\x9b~\x18\xf9[\xfeB\x8ds\xda\x19\xe1v\x1d\xb3\xde\xf6\xb3\x94Wӏ:\xe4;~\xa0ѻ\xfe\xbc\x8dͬZ\x98\xd2{~8|m?ܥ\x8e\x1f\xc8\xf1\xbe\x1f\xde\xf0\x01Z\xad\xebG\xa9\xac\x9e\x9f{\xa6\xeb\xfbui\xa6\x81_\x12\xbb\x86~\xd8`#?\x1f\xedPI~\xe8'\xb5o\x92\x19yS?J\x85\xcd\xfc\xa8\xea7\xf7\xb3*\xff\x91\x1f\x11\xb2\x856\xdd\x12\xfd\xb5\xf23\b\xb7\xf6\x03\x04\xb2\xfdyZf\xd9\xfcؘm\xf7\xa3j\xe2P3p\xfa\x19(yrs&\x1e?\xa0ы\xd9\xf9\xfcj\xe0~U\x81\xe1\a\xef\f\xf0\x96\xa0_\xea\xa8!H5\xcc\xdb#~ť(\xac*\xe6G\xf80\xfd\xf2\x00L\xdc/շ\x84\x9f\xa15\xe9\xa7\xe3[~r\xb5\x94\x9f\xb4'\a\xb3h\xe3G!\xa2\xad\x1f\x15\xb6vj\x1f\xed\xfdxʭ\x83_\xf0\xb3#\a\xf1\xb1?OݬNi\x89t\xe9\xd81\xab\xb36\xdeŏ\x80\xdd\xd5\x0f_\xeb旜\xe2\x13Z\xf8\xa7~l_\xfa\xccOP\xfa\xdc\xcf\x00\xdd]\xcf_\xa8\xe9\xf5\x80y|\xe9ǂ\xc5W~\xa6\xc7=9\x9c^~\x81\x91\xde8~\xed\xe7\v\xe20\xeao\xfd\xd8\x11\xf8\x1d\xa5\xd4\xc7/\xf5\xfc\xbe~,A\xf4\xa3\xea\xfa\xd3T\a\xe0_\xdfû\x06Bԃ\xfc\x84\x98\x1f8\xab\xc1ho\x88\x1f\xf9\xd6P?\xaa\x04\xc3p\xe1p\xd5\xf6\b\x85\x8a\x1fUn#\xfd\x1a\u007fG\xb1\x85\x9f\x00!\xa3q\x1cC?\x19\xeb\x17\xe6?ί\x8fK\x8e\xf73\xb9\x9e\xa0\xfe2\x11\xedO\xe2\x1c'\xe3\xda)~\xbc\xc9e*E7MD=ݏ\x95\xa1\x19\xeal3uܳ\xd0\xd1\xcfD\x9f\xd9\xda\xe2\x1c?R\x9c\xb9\x9c\xc6~\xd5ϝ\xfe\u05c8N\xd73hu\xc3ύ\xdc7\xfd\xba\xab\xed\x96_^\x89x[\xb5x\a\xee\xf8\x9f\x9fQ\xf8.'~Ogr\x1f\xa2\xcae\xd0\xe1r\x1bTU\x1e\x03c\xcdk\xd0\xe8\xf2\x19\x10H~C\x1e\xbe.`\xd0\x02\v\xf2\xcbB\x86\xe8\xe4\x01\x1c\v\x1b\xc8 \xf5\xae\a\r\xda\xfbC\x06\x8c\xf4a\xfc\xb3\xa8!F\xff\x88\xc1\xbdz\xc5\f\xca\xefQ\x03\xc0Q\xdc`\xeeQ\xc2@\xfeX\xd2HK\xfe1\x83\f\xa1\x94\x81\x97\xd6>n\b\xa8\x95Ʊ\f:-k\xc8\x02\xd4\x13\x06\xc4Z\xce\x00\x10=i\xe0\t\x8f\xf2\x068S\x96\x019>ep\xcaO\x1bx\xee\xfc\x19\x83\v\x9c\xcf\x1a`\x06\x15\f\x04\x9b\x8a\x06\xca\xff\xcf\xf1Tɀ\xf3=o\x103+\xb3\xa1\x17\fٗ\xf6\"\x8eU\f\xbc*\xd5\x00Y\xadj\xe8o\x12\x1b\xaa\x99W\f\xb1\xdeW\r\t\xe4\xaf\x19b\x1b\xaf\x1b\x19RQ\xcd\x10'y\xc3@$\xaaN\x01\xd40\x94\x0e\xbc\xa9\x82y\xcb\xc0\x8a\xd3\xdb\x066[\xd64\x98\x19\xbfcH\x8d\xf9]\xdeSˠüǖj\x1b\xd0o\x1dm\xe0}\x83\xc1\xe7\x03Cܽ\xae\x81\xfd\xe6\xf5\f\xe4\xd0\xf5)\xe6\x06\x86T\xc4\x1a\x1a\x12V\x1a\xa9r\x1b\x1b\xe0G\x1ff&҄3o\xaa\x03hfpGzs\n\xed#\x03\xe9l\v\x830\xd4\xd2 ;oeH\r\xb0\xb5\x81b\xab\x01\xaf\xb7\x19\b[v\x03\xd6\xef\xe0\xc9ɓ\xcb\xe0[Si.\x1eCX\xaf\xd7\xd0\xfc\xcdg\xe8\x0f\xf5\x1bR42\x8c\xcc\xd6ۀ\x01\xac\x0e\x1at\xa0\x10\x15\x166𪸈\x81zS\xd4\xe0\xab\xe4(/\xd3\xd0\xe7\x1e\ra{\tUY\xd2\x00\xdcX\x14Iʐ|+\xc7\xd0]_mh\xeam\r\xa6\xc1\xed\xd8g{\x95U\aξ\xa3J\xfcc\x83\xb0\xd7\xc9 \r\xedl\b1\xebb\x80xt5P\xe6\xeaf\xb0\xe0\xff\t\xac\xe7S\x1c?\x835\u007fn\xb0~\xdb\xdd`\xa9\xe9\v\x95i\x0f\x03!\xe5KC\xf9\xf9Wl\xaf\xa7\x8a\xa5\x97\xa1X\xd3\x1bm}mHz\xf2\x8d\xc10\xfe-\xbd\xf9;\x03\xd9P\x1f\x83h\xd1\xd7`\xc2\xd3\xcf\xe0\xe3\xfa\xfd\xa9\x9a\x01*\x9e\xefu \x03\xe9\t\x83(\xc3\x1f\x14(\x06g\xfa\x1bB\x81\r\xe5i\x98!p:\x1c\xc7\x11\x06\xa0\xedG\x03\xc0<\xd2\xe0\xcbu\f0\xbb\x9fx\x1a\r\x17\x1ac\xa0\x161\xd6\x10\x98\x1ag\x90N\x8d7\x90\xb6L\xa0sN4@\xea&\xf149#\x84)\x06b\xfdT\xccy\x9a\xa1\x9bYi?3\ff\xc23U\x90\xb3\f\x04L\x9a\xdalv9ǐ\xbc|\xae\xde8\x8f#\x9do\xe8\"\xfd\x02C(\xc9B\x00\xd6/\x06H\xec\"\xbdt\xb1\x01\x80^\xc2)\xfe\xca\x1b\u007f\xd3\x1e\x97\xaaI-\xe3薫\x9cW\x18xz\xfbwEŕ\x06\xf76\xac2P\f]\xcd\xd3\x1a(n-\xbd~\x9d\x01\xfa\xf5\a\x1d{\xbd\xc1\fn\x03<`\xa3\x81\x15\x97M\x18\xddf\x03\xe1q\x8b!o`\xddj`G\xca6\x83\x99\xcev\x03\xe1~\x87\xc17\xcd\x01,w\xf1\xfa\xdd\x04\xcb=\x06\xb6\x9d\xee5\xb0w\xe5O\x9a\xc1_\xb0\xc6}T\xeb~\x03Y\xc5\x01\x83\xcb\x13\a\xd1\xc8!\xfdt\x98\xba<\x02%\x1c\x85\xcb\x1fcg\u007f\xb3\xeb\xe3\x1aTN\xd0d\xff!6\x9f\xe4\xe9\x14\xc7pZa\xea\f\x90\xed,\xaf\x10\x10\xfe\x11\f\xa0\n\x11\x82م\xd9t$\xc0\x10\x16\r\xf0Us\x01\xa4\xd5f\x00\xa5\xe2x\x00\xee\x9b\b\x803$\x03\xf4P+\x80\xdaK*\x80bFN\x00\xb5\xb46\x01<\xd7\xd4\x16Jk\xa7\x8d\xb6\xe7%\x1dhV\x1d1؏1\xd3N\x01\x81\xb0θ\xbaK@P\xb6+\a\xd0- \x19\xee'\xa2\xceO\x03\xc8\x16>\vpO\xc1\xe7\x19'\xe8\x0eS\xfc\x82\xa2\xef\x11P`\xf8R\xb5\xf9U\x80e\x99\x9e\x01q\xe0^\x01T\x97{Ci_\xd3\xed\xbe\xa1\x8b~\v5|\x17\x00\xea\xf6\tpY\xa0o@\xe9W\xbf\x00\xe8N\xff\x00R\x92\x01\x01ɭ\xbf\x87,\a\xd2C\a\xc9\x1di\xd0\xfc!\x80u\xae\xc1\x01\xc8\xd3!\b\xe60Du\x04\xb7\x1d\xa5\xeb\x1c\v\xe8\xafh(\xdc\x1d\xa7\xf6N\x044]\f \x16\x9d\f\xf0\x89\x90S\x01\tz\xa7a\x06gt\xb0g\x03\xa8\xe4\x9c\v 6\x9f'\xb6] \x94^̘\xe9\xa5\x00\xca\x05\x97\x03LO\xae\xe8\xf9\xdf\x00s\xe2\xab\x01l$I\xc7\xc0\x00\xea\xc4\xd7\x03x\x84\xe6F\x00\x1b\xfboR\x9f\xb7\x02`\x17\xb7\x03¾\xee\x04\xb8\xd8\x18\xc0\x13{w\x03x\xec\xc2̺\xc7\xee\xefC\xac\xb9\x822\xda\xdcAܝ'\x88\xf1\xe5\r\xc2\x02\xf3\x051\xfb\xfcAp\xbd\x02<\x15\xe4\xff\n\x05\x91\xa0=\x10\x94U\xec\xc2A.J\x16\t\xd2\x12\x1f\f\xe2w\x12\x83dJ\x0f\a\x05ъ\x06\x19\xb1\x1e\tʦ\x98bA\x96\xe8\x1e\rb\u007fS\xf1 L\xb7DP\xe2G\xc9 \xc2\xd8cAY\x17)\x15\x84\u007f?\x1e,Ԥc\xfb\x98\x99F\xd3\xd2A\xd1L\x19\x0e\xa3l\x90[\xb5\x9e\b\x8a\x99\x96\vҍ\x9f\f\xca\xee\xda\xf2A\x88\"+\br\xf7T\x90\xc9\xe8\xd3:\xaag\x82*\xf5g\x83\x82@\x15\x82\x10U\xc5 J\x90\xcfaȕ\x82B\x8c\x9e\x0f\u009a*\aAp^\bb\a㋐Z\x95 7!\xbc\x14\x94\xac\xbb\xaa\xce\xfe\xe5\xa0b\xdb+A\x84\xa6Wyz-\xc8\u05cd\xbc\x1e\xc4RD\xb5 m\xea\rL\xa7z\x90\xce^#H\v{3(\x9e\xfaV\x90\v\xc9o\a\x19\xaejr\f\xef\x04\x81c\xefr~\xb5\x82\x88\r\xef\x05\xb9)\xb6\xb6ίNP\x02\xc3\xfbA\xb0\xc7\x0f\x82Ȝ\xea\x06\x99^\xd4\v\xa2\xaeS?\br\xd1 (u\xe5\x86AzH\xa3 \xe0\xadqPb܇Aq\xab&A\x04\xa3\xa6A.\xa56\v\xa2\x10\xd3<Ȥ磠\x18z\x8b Yrˠ,(\xb4\xa2\xd6Z\a%Df\aɻl\xf8d\x0fb\xff\xa2\x03\x13t\x06\xf9ȓ\v\x8aw\a\xb1\xe9\xc0C\x81{\x83\xc8\x0e|A\xe4\xca\xfe \xf7\xd0\x18*\xa0\x806\x1a\f\xa2\xa6\x15\n\xc2#ÔL$\x888\x10U\xa1Ƃ |&m-\x1e\x04z%\x82\b\x87ɠ\xc0\x8f\x15\x04\x00\xa4\xa0\x8b\x1c6\xd9&(\xf4\xac-l\xa0\x9dε}\x10A\xa7C\x90\xa5\x87\x8eA}\x8f\\\x90\xaf\xda\xec\x14\x84Cw\x86@\xbb\x04\xf1\x0e\xa0\xaeA\xf1\xb2nAb\xde'\xd4\xe0\xa7A\xd9\x16\xfd\x19z\xf8<(q\xb3{\x90\xa9\xc8\x17\x19\xe3\xe9\x11$\x91\xf92\x88x\xfdU\x10\x01\xa4g\x90\xd9h\xaf )ho:\xe5\xd7A}\xe21HJ\xf1-\x95\xf8]P\x00\xadOP\tG_\xf1\xeeTV?\x18|\xff \x8a\x06\x03\x82\xa8\x14\u007f\x1f\x14\x1e90\b\xdc\x1d\x14DP\xfc!H\xec\x1a\x1c$\f\x0f\x81ʆ\xc2ӆQ\xc1\xc33N4\"\x98y\x89\x1c\x11d$\xae\x1dE\x03\xfbI\xcdwt\xe6\xea1A\xbc/yl\x10\xe4j\\\x10\x01n|\x10\xdccB\x90Qp\"e9\t\xae59\xc8b\xe5\x94 6\x1aL\r\xf2\xf9\x0e\xfa\xe2\xf4 \x9e\xf8\x9f\x01\xe5\xcd\f\xb2\x9c9K\a\xffs\x10I\xd7l*x\x8e\xcai.\x1b\x9f\a\xb3\x9b\x1fD-cAڢ\x8c\x80\x91\xb5\x90\xa6\xf4\v\x01fQ\x10\x11lq\x90\xf5\xee%\xaa\xa6_\xa9\x95߂\xacS,UK[\x16d\xf1f9ռB\x8d\xf5wE\x86\x95t\xc3U\xfa\xf5\xea\xa0p\xa85\x9c\xcc\xda \xd8\xf8\xba \xf2\x90?\x82\\\xec_\x1f\xc4s\xaa\x1b\x82\x12\xa57\x06Q\xb7\xd8\x14D\xc1k3\xa7\xbd%\xc8\xccgk\x90\xdbd\xb6A`\xdb\xe9\xa3;\x88\r;\x83\xfc5E5\xdb\xdd:\x9c=P\xd2\xde \u007f\xf6\xee\xcf +\xe3\u007f\x05ɽ\xf6\x05\xc9#\xf6\xab\xa7\x1d\xe08\x0f\xa6o\xc6NV \xff\xe1\xa0\xe49G`\x13Ga\xceǂR\xcb\xfb;\x88\r\xaff\xd6q\x18\xdb\t\f\ua7e0\x04\xfc\x938\x9e\xca\xd8\xf9\xe9 \xca8g(\x9a\xb3<\x9d\x83\a\x9d\xa7\x96.\x10\x9a/*\xd8^\nj\xb9\xe0r\x90\xaf\x91\xbaB\xf7\xfe\x97pxUUpM\r\xe0:\x00\xe9\x06\x05q\x93q\xe6V\x10\x15\x94\xdbA\xf0\xa7;T\xf8\u007f<\xdd\r\"\x98\xdf\v\x82k\xde'\x92\xe4\n1\x9d\xcc\x1d\x92\xc0\x96'\x84\xaaO\xde\x10ř/ā\xe5\x0f\x11y\v\x84\xb0\xef\xa3`\b\xb7\x16\n\x91$<\x10B)\xabp\x88L\xaaH\b\x01\xf7\xc1\x10\xf9\xd2C!\xa0\xdc\xc3<\x15\rIty$\x04~U,\xc4\xec\xf1\xd1\x10w\xda\x17\x0f\xc1\xeaK\x84`\x81%C\"\xee\xc7B\x98I\xa9\x10\x90\xeb\xf1\x10L\xb7tHxn\x99\x10\xc4R6D\x9b{\"\x04\x8c-\x17B&\xf3d\bor+\x1f\xd2'\x1dC\x94\xe8S!>\xc7\xf3t\bU\xb4gBHɟ\r\xa5y|\x85\x10\xa1\xaeb\b\x16\xfe\\\x88\xae])$\x11\xf7\xf9\x10\x14V9\xc4\xf4\xf9\x85\x10\xc8\u058b!\x90\xba*!ɑ^\nI\xf2V5\x04\xf0y9$\xf4\xf7\x15\x0e\xf7\xd5\x10ޗ\x13B\xd9\xe2\xf5\x10\xb5W-\x045\xbc\x11R\xde\\=D\xf7\xae\x11\xa2\t\xbf)cz+$\b\xfa6GT3D\xb6\xf4N\b\x1a\u007f7\x04\x8dׂ\xe2\xde\vIL\xac\x1d\x92\xe5\xb4:!X\xcd\xfb!\x92\xfb\x0f\xf8\xb1n\b\xd0W/\x04\xb8\xa8O\xb16\baSJC\x8a\xaeQ\bp\xd58$?=\xfc\xa1\x1aF\x93\x10b|\xd3\x10\xabS\xcdBj\xa3\xcdC\x99,\xfa\xa3\x10P\xa6E\x88\xf0\xd62\xc4p\xdd*\xa4\xbb\x83[\x87\x04\xab\xb3C\x82\xc36\xf6d\x0f1::B\xacQ8U\xae.ڎ;D\xf0\xf1\x84\x04ż!<\x19\xe6\xa3m\xfa\xd56\x8d\x10\x92\xfb\x00\x04\x1d\xa4,B\xff\x1bSX\x1b\x8c\x84\xf0\x00R4\x84\x8am,\x84\xbdifH\xf2\x80xH7D%BX-M\x86X\xb6\xb2\xa8\xb4TH8v\x0e?\xb4\t\x01tچPOm\xa7\xa6\xd1>\x84\f\xa8\x03%ۑ\x82\xfa8\xc4\xf4\xa8\x93\xfaLgt\xd6\x05Ǯ!\xe4Y\xdd\xd4(?\t\x917}\x1aҟ\xdeό\xe8\xf3\x10\xaaY\xdd\xe9n_\xa8\xc5\xf4\b!\xb6|\x19\x12\xdc\xf9JG\xdb3\x84p\xd5+\x84w\x88\xf4\xe6\xe9\xeb\x10\b\xc67!\xee_\xd5\xee\xbe\v\xf1U;}B\xc0\xe3\xbe!\x90\xec~!\x81\xca\xfe!\xb0\xc3\x01!Ů\xef3\u007f\f\x841\f\n\x81i\xfe\x10\"\x90\x0e\x0eɂ\xfe\x10\x1d\xd7А\xd6އ\xc1\x0f\x86\x87@\x83F\x84\xc8\v~\f\xa1\x8472\xa4\x11rT\x88\xab\x90\xa1\xcc\x06\xdc\xd1*\xa81\x98\xd7\xd8\x10*\x8a\xe3B\bN\xe3y\x9a\x10B\x00\x9d\b\xf0\x98\xc4\x0f\x939\xf1)!\xddɪ\xf69-\x04n2=$\xe4gFH\u007f\xa3\n7\xce\"\xbc\xfc\x1c\"\x1d\x9c\x1d\xd2g\xb5\xe7\x84\xc0\xca\xe6\x86$\r\x9dGl\x9bO\xd1-\xd0I,\f1r\x12q\x16\xc1,\x17븗\x84\xf8lů\x00\x8a\xdfB`\x94K\xb5\xe7e!\xf2\xf6\xe5\nR+BX\xe8\xf9]\xa1se\b/\xaeY\x15B:\xb2\x1a\xed\xaeQ\xebZ\xab\xe3\\\x17\x12\x16\xfa\aF\xb7>\xc42\xd3\x06\xc8jcH\xdf\xc0J\xcb\xdbL\xd1m\t\xe9V\xd6\x10\xab\xbc\xdbB\x8cr\xdb\ta;8\xc1\x9d!\xee|\xd9E\t\xef\x06\xd0\xec\x01\x96\xec\rIH\xfe\x132\xfb\v^\xb0/$\x04d\u007fH\xe2\xcd\x01\x8a\xe2`\x88\xb4\xe1\x10\x1d\xfapH\xaa\x8bG0\xac\xa3!\xe4\xf0\xc7t\xfc\u007f\x13\xba\x8f\x87\xb8\xc8w\"\x84(\xf4OH~\xb5\xe4$\x1a>\x85\x86O\x87$}:\x83\xbf\xcf\xe2x.$\xf9\xeayx\xf8\x054}\x91xu)\xc4|\xf42\xc6{\x85>\xfa/\xe7w\x95\xb2\xbc\x06\x87\xbb\xaeb\xbfA\xc5\xdd\f\x81'\xdf\"\xce\xdd&\x14\xde\xe1\f\xfe\x83\xf0\xef\xd2\xc7\xee\x85P&\xbaϖs\x85\x81\xf6\xb9ä\ry\xc2PZްxF\xbe0\x9c?\u007f\x18\xceT L{)\x18\xa6\xcb\x15\n#Iy \f\xb6X8,\x11\xa2HXX\u0083a\xd8\xc9Ca\xba\xf1\xc3a\xe9\xbdh\x98\x99\xd3#azV1=?\xca\u058b\xeb\xc7\x12a\x88\xb0d\x18\xdb]\x1f\xd3&J\x85\x91}=\x1ef\x9d\xb8t\x98\xf8]&\f}\x95\r\xd3J\x9f\b\x8b\xa2˅\x11\x13\x9e\f\x03Y\xcbs\x94Ya\xd1\xe1S\x18\xe4\xd3\xec\xf1\x990\x93\xd0gÐX\x850\x19RE\\\xf3\\\x98<\xbeRX\xc2\xdb\xf3a\x1ac\xe50\xdd\xe2\x850\x9f\x8c\xe4X\xab\xe0\x8e\x97\xc2\xfcݘ\xaaa\xc8\xf5\xe50\x9f\xf5\xe0\xe9հ\x04\x88\xd7\xc2X\xd9~=,\xe6^-\x8c\xa7\xa4\xde\b\x93\xdbV\x0f\xa3\x1eP#\x8c\xda\xfa\x9ba\x04\x80\xb7\xc2x,2̊B\xcd0\xfc\xeb\x9d0\x02\xf3\xbbaVRk\x85\x15|\xde\v\x8bQ\xd7ց\xd7\t\x03\xbf\xdf\x0f#m\xfa ,!\xb4nXJ\x90\xf5\xc2RZ\xaa\x1f\x96RC\x830\x11\xa6!\xc4\xd3(\f\xea\xd28,\x96\xf7aX\"[\x93\xb00Ŧ:\xeffl\xady\x98\t\xd4Gaо\x16aF\xf5\x96ad5\xad\xc2\u008f[\x87\x11\xed\xb2\xc3\xe2c\xb60\u007f\x8e\x18\x8d9\xb41'/w\x85\x19&\xdda\x84!OX\x16\xa8\xbda\xdd'\xe1\xd3\xe9\xf8y\xb1\x01\x81\x04(\x88 m$\x14\x16.\x15\x0es\xd3j\x18\xd8\x16\x85\xbccaT\x02M\x9dd\x9cm$\xb4\xffd\x98\x18b\xa9\xe9\xa7\xc2\xe009\x9cc\x1b\xda~\xdb0\x12\xc90\x11\xaf=>u\xc8H\xbc#D\xf4\xb1N\xbfSX`\xb93\xef\xeb\x12&dw\rK\xe9\xad[X\xb7\x14}\x02\v\xfdTG\xf0Y\xfaZy\bR?v\x0f\x13T\xbe\xc0${\xa8\\\xbe\x84@\xbe\x82\x8d\xf5ԙ\xf4\xd2s\xef0x\xd6\xd7a<\x1b\xf8\r&\xfd-\x8e߅IB\xfa\x84\x01*}i\xb0\xfd\xf4\xdb\xfe\xf4\x9b\x01aTп\xa7A\r\xe4i\x10\x8d\xfa\ahq0?\f\t#\x85\x19Jy\x0e\v\xeb\xda\xdcp\x9d\xe4\x880j\x89?\x86\xf1[Va\xc0\xd1(~\xf7\x13\x9a\x19\xad\"\x1a\xa33\x1d\x1b\x96\x9cc\\\x18\xf5\xda\xf1\xb4\xba\ta\xb0։\xb4\x9bIa\xb0\x84ɐ锰\xf2ȩa\xf2\xeei\x90\xeat\x95\xd7\f\xfdvf\x98\xaf˛\xa5\xdf\xff\x1c\x96H>\x9bN7G\xbf\x9c\xab\xe7ya\xc6\xc20\"\xf8\x02\xfdv!\xc7\U0008bdb4(\x8cz\xe8bu\xb5%\x9cگa\xbeP\xf97\xccmi\x18Yڲ0\xf6\x1e.\xa7\xff\xaeH\x1bQ\x97p\xd6\xefan\xa3]\t\x9d\xac\xd2q\xae\x0ecYd\r`q-\xef[\x17\x96\xb8\xf4G\x18\x8bi\xeb1\xeb\r\xda\xeb\xc60S\xc5Mj\xaf\x9b\xf5\xbc%,\x11dkXj\x12\xdb¬\x8cn\x87\xe9\xec`3;ì\xfc\xec\n3P\xec\x0e\xe7\xad\xdb>k\x0f.ً\x11\xfdI\xac\xfa+\x8ce\xde}aR\xb5\xfd\xfcx@\x87{0\xcc\n\xee!\x02\xd6a\xcc\xfa\b\x00\xe5hX\x02\xd71\x1c\xff\xc6\xf18\xa3\xc1\x89\xb0\xfc~\xd4?a\xc4ߓ\x84\x89S\xec\xect\x18\xbb\x02\xceP\x96gì(\x9e\v\x83\xa3\x9d\aB\\\xc0\xf1\"\x8e\x97\xe8\xeb\x97\x19R\xae\x84\xb9C\x15\x8ew\x15\x17\\\xd3Xp\x9dz\xb8\x11\xd6ň\x9b\xeaַ\xf8\xfd\xed07 ݡ{\xff\xc7\xd3\xdd0\xb6\xba\xde\vs5\xfe>f\x95+\x82u\x87\xdc\x11\xa4\x9by\"x\xfbFވ0\x95|\x11\xf8H\xfe\b\xc2q\x81\b\nr\x05#\xea\x0f\x85\"\xe2\x00\x0fD\xa8\xb7\xc2\x11l~+\x12A\x04x0\xa2\x88\xf6P\x84\xea|8\xc2\xdd\x1bE#\xd8*\xf2H\x04k\xec\xc5\"0\xcbG\xd1R\U00048a2aDD0\xa3d\x04f\xfcX\x040W\n\xa3y\\{*\x1dA\x04+\x13a\xf1\xbflD\x88\xc7\x13\x11ο\\\x04\xd1\xee\xc9\b3\x98\xf2\x11R\x95\xac\b#\xebS:\x9c\xa7#\xb0\xc3g\"\xf4\xd4g#\xac\x1eU\x88P2\x159\x99\xe7\"\x88Õ\"\xfc\xf1\xe2\x88\xf0\x90\xca\x11V\x1e^\x88\x88:^\xe4t\xaaDh\x90/E\x98\xd1T\x8d@\v/\xf3\xf4J\x04\xafZ\x8d\x00~^\x8b\xa0p\xfcz\x04^Q-\x82\r9\x19iU\x8f\x00\xe1jDX\x86~3\"\x00\xfc\x96\xce\xe5m\xaa\xa6f\x84[\xe1߉\x90a\xbc\x1b!A\xa9\x15QSx/B#\xab\x1d\xe1\xbaT\x1d\x8e\xe2\xfdH\xe6\a<2\u007f\xd4Uaԋ \x8b\xa8\x8f\x195\x88\x10\xb4\x1aF\x00P\x8d\"0\xe3\xc6\xfc\xf4aD\xd7)\x9bDX\xa4m\x1aa\t\xa8\x19\x15\xd5<\x82@\xfeQDw\x8f\xb5\x10\x1d\xc9k\xc9#D\xf0V\x11!\x83\xadiq\xd9\x11\xa4\x0f\xb6\bl\xddΓ#\x02\x02㌠F\xeaҹ\xb9iz\x1e\x9d\xba\x176\xe2\x8bhb\xe3\xcfX\xa4\x11\x91\xe2m \x82\xd4,\xc8S(\x82\xb5G\xb6\x1ea\x97Q\xaa7\x16a`7\xd9Y<´=\x11!: \xdc\xd2NS\x11\xe119\x11\xc2Q\x1b\f\xa1mD_\xa2\x03\xd3m\x8f\xef:DH\xda;F\x04s>\xd6\xeb;\xd1/:\xab\xc1u\xc1\r])\xdcn\x11\xa9R}\xc2Q|\x1a\x01D\u007f\x16\xd1Z\xe5\xe7\x19\xdb\xe8\x1e\xd1'\x8ab\x85\xbc\x05\xa7\xd62J\xfa\xda*\x8a\x12V\xeb(*\xadQ\x85$[\x945\x1a{\x14\xf9\x82#\x8a\x85ɨ$\xb8\xae(\xf3ewTPۃ\xffx\xa18_\x14\xb6\xed\x8fr\x19\xceP[\t\xa4\xdb\xcaIt\xcc\n\xa6{̱\x8c\xacP\xb4@\xab\x9c`\xba\xd5p\x14\x0f\xd7E\xd2\xdfw\x8c\xa6\xa3\xa4\xf6\x19\x8b\x12\xceͨ\xbc\x9c\"\x1e\xe5\x12N\"*\xe9F2\xaa\xbft\x15e\x92\x90\xa2\xa5\xe6DA&\xdbDA\x1c\xdbFIg\xdaE\x81\xed\xed\xa3\xc0\x8d\x0eQ\xe2f\xc7(\x9c\xf7\xe3(\xde$\xd1)*\xcf$u\xe6ȻDQ\xf2\xed\x1a\x05\x19\xec\x16\xe5\xaa\xc4'4\x89O\xf5\xe3gQ.\xea~N\xcf\xe9\xae\x1f\xbf\x80\xf2{\xd0پ\x8c\"\xe6|\x15E\x15\xb6'\xfe\xd5+\x8aH\xd6\x1bN\xf95\xac\xe3\x1b\xb5\xeco\xa3`Z\xdfEI8\xfb\xd0}\xfbB\xb4\xfd\xe0\x99\xfd\xa3\x88\u007f\x03\xa2xUyT\xf0d`T֗\x06E\x914\xfe@S\x18\xac\xee8\x84S\x1e\x1aE0\x18\x16\xc5\xc6\xfe\xe1\xd1O\x89*U\x9a\x1a-\xd0Đ\xdfL\x9d\x16\xd5$sz\x945\xfe\x19\x9c\xd1\xcc(\xc3\xcb,\xd5\xce\xcfQ\xd9d0;ʍ\xb9s \x82\xb9\x98\xf5<\x8eq~\x94\xf9\xd2\x02\xbdqa\x94/a\xe5i\x11%\xbe\x18B[\x02\xa7\xff\x15m\xfc\x16\x15$^\x8a\xe3\xb2(\b\xea\xf2(J\"+\xa0\x8a\xdf\xe9\xd6+q\xf1*\xc5\xc1\xd5Q2\xe15Q\x89\xd7k\xa3R}[\x17\x15\x18\xff#\x8a\xba\xdez\xbaކ\xa8>\x14\xb81\x8a=|\x9b\xe0\xe6\x9bՎ\xb7`0[\xa3R?\xda\x16\xc5\x0e\xff\xed\xf4\xea\x1dp\x9b\x9dQ\xbc\xf3}W\x14\xab\x9a\xbbi&{TA{ռ\xfe\x8c\x82]\xfe\x15\x05\xc3\xdaG0\xdf\x1feJr *)\xc9Az\xf2\xa1(\xb2\x87Ô\xef\x91(\xeb\x9dG\xa3,\x1b\x1f\x83\x9f\xffMY\x1e'\x10\x9e \xac\xfc\x13e>\t/?\x15\x95({\x9aXv&*e\xa6\xb3\x98\xc79U\xf1y\xcc\xe7\x02\xc5v1*E\xa2KQ\xac\x91\\\xa6\xbf\\\x81\\\xff\x8d\"Y\xbe\x1a\xe5f\xa7k\xec\xf1:\x9c\xf9\x06/\xbc\x19Ent\x8bZ\xb9\xad\xfa\xbd\xc3A\xfdGK\xbe\x1bE\x85\xe5\x1e\xc7s\x9f\x97\xe6\x8aa\x16\xb9c\xe8\"O\f\x96\x9d7\x96\xafAN\x97\xac|1\x06\xf8\xfc1tX \x06k-\x18\xe3\x93ՅbL\xbe\x1f\x88\x010\n\xc7\x18:\x8a\xc4\b\x1c\x0fƄ)<\x14+\xd0\xd2\xe8\"\x15֘\xd8x\xd1\x18\x8a5\x8f\xc4(\xc8b1xã18^\xf1\x98nC*\x11\x13\x95\x96\x8ciXħR\xb8\xff\xf1\x98\x98V\xe9\x98r\xa42zIY\xfc\xf3\x89\x98\xec\x12/\x17\xc3>\x81'cؐZ>F\xebˊ\x01\xb0\x9f\x8a\xd1\xe0\x9f\x8e\x89\xe0\x9f\xe1\x97\xcf\xea5\x15b\x02\xfb\x15c\xfa\x82rtU\x89\x93{>&<\xa1r\f\xb2~!F\xed\xbd\x18\x03\x95\xa8\x12\x93\x17\x1d\xbc\x84c\xd5\x18l\xea\xe5\x98<\x92\xffJ\x8c \xffj\fu\xd3\xd7bX\x15z]\xc5V\rc~#\x06\xceQ=F3\xaf\x11\x13\xc7x3=\x85\xf4\xb8ޢjގ!|ԌI\xc9\xf3\x9d\x18\xdf8\x17#\xb7\xaa\x15\x13sz\x8f\x83\xac\x1d#\x9e\u05c9\x81P\xbf\x1f\xc3R\xe8\a1}\x8fE\xdd\x18\xa2x\xbd\x98Ҽ\xfa1\xa1\x89\rb\xc2\xfe\x1b\xe2\xd8(F\xc2\xd1\x18S\xff0Ɣ\xaaILФ)\a\xd3,\xa6\x8f\x0e5\x8f\x91H|\x14\xe3\x8an\v=\xb7\x8cq\xb9\xa3\x15\xc7\xd4:\x06cˎ\xa1\xfcg\x8b\xc1\xd2\xed1>\x9f\xe4\x88Itv\xc6\xc0\xf8]\xe9q\x19\xed\xe5\xcd\xe41R\x17O,\x1d\xcaҨ\xe7\xa5\xd9\xf9Tn\xfe\x98@\xb6\x11ÊX@\r \x18\xd3w\xce\xc1V\xc21\xd6B#1\xae>Ƹ\xd6\x12\x8be~\xd1*\x96Y\xfc\x8a\xd3\xf2\x121d9\xc9\x188\x85\xa5\x8d\xa6b\b\x8891\xf2\x8e64\xac\xb612\xefv0\xa1\xf61\xb0\xa1\x0e\xd0C\xc7\x18b\xc4\xc7*\x9aN1\xf8[\xe7\x18\xb6\xaf\xc6H\xb8\xba\xd2Ѻň\xe0\x9f`\xc0\x9f\xc6PB\xf8,\x86<\xfa\xf3\x18\x80\xad{L\x1e:\xf8\"\xa69e\x8f\x18\xf8ї:\x9a\xafhi=鰽b\xdc\xc4\xd4[\x9d\xf0\xeb\x18\x99\xe97\xff\x9b\xe9\xb71\xc1\xac\xefbL\x88\xfa\xc4d\x1bD\xdf\x18\xa2P\xbf\x98 S\xff\x98@ր\x98\x00\xce\xf7\xb8x`\fI\xd4 \xb8\xc5\x0f\xd4\xff`\xba\xcc\x10\x8afh\x8c\x14}\x98z\xc4pU\xfe\x88\x18\xd2\xe2\x1fcX8\x1f\x19CX\x19\x15#\x95\xf9)&\x91|\xb4z\xe2\x18\xc0\xc4X\xbdq\\\x8c\x84j|\f\xefޛ\x10\xe3S\x1d1\xe2\xf7\xa4\x18Y\xea䘰\xb4)p\xa0\xa98N\x8b!\xbaN\xe7m3ԂfR\xad\xb3b\x82\xec?DŽ\xfeϦYΉ\xa161WM|\x1eqh~\x8cu\xba\x051D\xa4\x85t\x98_T\xe1\x8b\xd0\xd3b\x05\x8b%\xb1\x82\xf5:Z9\x1dұ1V(\r\xa4\t\xd3J\aH\xba\xdcR\xcajY\f\xd4ly\x8cU\xf2\x15*\xa4ߵӕ1\x06\xc8Uhw5\fa\x8d\xb6\xbe6\xc67į\x8b\x91L\xfc\x11#\x9f]\x1f\xe3\xf3p\x1bp\xf5F\x1c7\xc5\xc0L7ǰ\ak\x8b\xceakL\x1e\x8b\xda\x16\x03\xb5\xdb\x1e\xc3\xf6\xab\x1dj%;c\xd8ĵ+\x86:\xd8\xee\x18S\x84=1\xe5\x1b{i\x16\u007fBO\u007fat\xfbԞ\xf6\xc3c\x0f\xe0x\x10\x06s\x88\x8d\x1cV\xd4?\x12c\x92w4F\xfez,Fv\xf67,\xea8\\\xe1\x04\xc5\xfbO\xa6\xbf\x93\xea٧0\x9d\xd31\xb2\xd331\xa4\x89g\xd1\u05f9\x18y\xfdy Ӆ\x18R⋴\xc5K1n\x1c\xb9\x1c\xe3szW\xd0̿*\xe5\xab\xf0\xcck1\x89\xadש\x90\x1b1\xeco\xba\xc9\xd3-\xbd\xf0v\x8c\x9b\x1e\xee\xd0\xe6\xfe\xe3\xe9n\x8c\xc9\xf1\xbd\f\xeeݏ\x81\xf0\xe42a\xe1\xb9MP\xbf<\xa6\x18q^\x13y|>\x13&\x96ߔ\xa1\x160\x19M\n\x9ax3\x00\x8e\x0f\x98:\xf1\xc2&XF\x11S]\xfcAS\xf9\xddC&\f\xe8a\x938Y\xd4\x14\xc8{\x047\x173\xe1y\x8f\x9a,\a\x1479\xfa\x12\xa68iI\x13K)\x8f\x99\xd4V)S\u007f\xe5\xca\x04[/mj]\xa6\x8cY0m\xfd\x92.\x945\v6ɱ\xccx:x\x9a\xb4\xcer&\x82Ǔ\xa6h\xaa\xbc)\xe9I\x96I\xe3{\xea\u007f\xb7=m\xa6\x1d2\x91\x0e\xa0f:b\xcb/\x0e?k\"\xe5\xaf\xc0qW4\xb15\xee9\xb3@\x13\xfc2q%3m\x14\x91t>i\x02\xc9+\xeb\xf0^0\xa9\xf7\x17\xf9u\x15\x13\xd4\xeb%\x13ʭj\"T\xbcl\n\xd9|\xc5\xccWO\xdeFg\xc2!_\xe3\xe9u\x93+ \xd5L\xc0\xcb\x1b&\u0378\xbaN\xa4\x06\xe4\xff\xa6Iv\xf7\x16\xe6\xf3\xb6\t\x9b\xaa\xc9\xd3;\xa6V\x85\xdeՁ\xd42Y\xfbz\xcf\x14\x9b\xabmJ,\xaec\x82\xfb\xbco\xcar\xd0\a&\u007fL\xa8\xae)(Y\xcf\x04\xad\xaaor\xb3\xab)\x14\xa1\xa1\x89\xb7\x0152\x19\xcf\x1b\x9bBw?d\u007fMLĊ\xa6&<\xa6\x99\x89*^sZ\xcbG\xfc\xd4\xc2\x14N\xd7\xd2\xd4-<&\x8a`\xad)\x98l\nצ\U000f36d2\xa2;L0]'5\xec2%\xa9q\xe3\xe81\t;^\x93n\xe13\x198\xfc&8\xad\x819\x06LD\x91\xa0\x89(\x122\x89\xb3a\x93Q\"\xa2\xe7\xa8I<\x8d\x99\f\x8c\xa6\x8a!\x0e\xe5$L\xc1\x89$\xe7a\xe1\xab\x14\x87\x93cr\x03`\x1b\xf4\xd56\xfde\xba\xafv\xd4^{v\xd9\xc1\x14\\\xe8\x88&>f\x13\x9dL\x14\x15;\x9b\x88w]\xb4\xef\xae&\x98}7\x93[`>1\xb1\x1b\xe9SS6\x13\u007ff\x16HgN\xf2\xdbWf\xa1ƒ)\xb53\xb2\xba\x9bؚ\xfe\x85\x99\xbfI\xc7v9Y=L\x14\xb9\xbf4Q\x97\xfb\xca\x04\r\xedi\"E\xeb\x85\xe1\xf56\xe9\xe9_×\xbf\x81\xb3}k\x16li$\x8cD:p\xd2F\xfb\x98\x92x\xf6\xa5^\xfa\xd1\xf0\xfa\xc3\xd0\x06\xa8\r}o\n\xde\f4\xc1\xe2\a\x99\xf8\x81\x0f\x93\xa1\u007f\xb0I:=\xc4\x14\xe0\x1c\xaaz\x1c\x86\x9e\x86\x9b\xb2{e\x04-\xeaG\x95\xf3H\x93x=\xca\x04/\xf9\xc9$2\x8e\x86\xaf\x8f1Y\x15\x1ak\n\xeb\x1dDZ\x8cg\x03\x13(ɉ\xa6\x16\x8c&\x99`\x14\x93MD\x83)4\xe9\xa9\xea\xd6\xd3L)\x11L7\xb1Ap\x86\xf61\x93S\x9c\xa5C\xfe\xd9D\xb20\x9b&8\xc7\xc4v\xae\xb9\x14\xdf<5\xac\xf9&\x98\xcd\x02\xbde\xa1\xc9H\xf0\x8b\x89퉋L\xae\xeb.\x86\x85/1\xb5\xfa\xff+\xe6\xfd\x9b)\xf0\xbc\x14>\xba\f\xd2Yn\n\x88\xaf0\xc1\x9c\u007f7Q\x83X\xc9!\xae2\x916\xae6\x11kטB\x8bך\xe0q\xebL.\xc5\xff\x91\x16\u007f\xda\x1c\xd6ì6\x98,\xb3m4\x91\xd6n2\x19.6g\xc0v\x8b\x89$i\xab\x8ac\x9b\xa9\xcfC\xd2@w\xc0\xadw\x9a\fg\xbb8\x92݊\xba{\xe8\x99{\xc5\xfe\xda˛u\xd4\xed\xff\xd2q\xec\xa3V\xf6\xab\xaa\x0f\x98H\x18\x0e\x9a\xc2tӗ\x1f2\xf35kgf\x1d&j\x1f1\x91]\x1c\xa5\xa5\x1d\x830\xfe\x86\x18\x8e\xab+\x9f0Q\xa7\xfc\xc7d\xf6tRG|\x8aA\xe84M\xfc\fOgM\xee\x83;g\x82\x03\x9f7\x85L^ \xb6^\xa4\xfe.\x99\xdc\xf7y\x99 p\x05\xb0\xf1\xaf\xa9$\xf9*>^3I̮\x13\x9fn\xc0\xd6o\x9a\x88\x81\xb7(\x8c\xdb&7:\xdea\xe4\xf8\x8f\xa7\xbb&\xaa\xb2\xf7L\xd4b\uf6e8\xe5䊋\xb7\xe7\x8egHi\x9e\xb8\xb8o\xde8\xfd#_\x9c\x1e\x98?N\xd2S .ԣ`\x1cտBq\xaee<\x10\xcf<\xd1]8\x0e\x1aQ$N\xb6\xfd`\x9c\xf1\xfb\xa1\xb8(\xfd\xe18\xa8_\xd18\xf6\n=\x12\x17\x9f.\x16\xd78\xfbh\\ܲx\x1c\x10R\"\xce\x15\xb7\x92q\xf1\xa3\xc7\xe2b\x89\xa5\xe2\xfcy\x8f8M\xb8t\x9c\x19o\x194S6\x8e\x94\xe6\t^S.\x0egz\x92\x9f\xcaǵ\xf6\x97\x15\xc7j\xc6Sq\xa9\x9e<\x1d\xe7\xcf`<\x13\xa7y<\x1b'\x13\xae\x10g \xac\x18\x87柋\xc3\xcd+\xc5E\xf3\xcf\xc7\xc9x*\xc7\tx/\xc45\xe9T\xe1T\x89\xc3L^\x8a\x17l\x90\x93\xe8\xd8>'\xabj\\\xed\xf9e\x19u\x1a\x01_\x89\x139_\x8dKf\xfaZ\x9c\x1b{\xe2\xf0\xe0jqfHo\xc4a\x91յ\xd1\x1aq)X\xbcɹ\xbc\x15\x87\xe5\xbc\x1d\x87\xe6kƉI\xefı\xbd\xf0\xdd8jt\xb5\xe2D\xbd\xf70\xe6\xdaqxa\x9d\xb8>)\x19\x97,\xf8\x838\xb0\xa7.$Q/.̽\xbe\xf6\xde \x8e$\xaea\x1c\x15\xb6Fq\xe0Gc\xfd\xe7\x87q\x16\xb3\x9a\xc4\xd3\x115\x1d5\xa9\xedf\xdaa\xf38<\xef\xa38r\xea\x16q Q\xcb8\xe3M\xab86\xf3\xb7\x8e+\xcf\xcaV=\xda\xe2\xcc\x1f\xecq\x81\\G\xe6\xdfNh\xde\x15G\xb6\xe6\xe6\xc9\x13G\xa0\xf0\xc6\xe1q>ؔ?\x8etƈk\xf5!\xc01\x04\xe3\xac\x0e\x85\xe2\xc8\xfe\xc2Tf$\x9e\xd9\v\x1b\x8d\xf3\xb9\xf9X\x1c\x8cҌ+\x93\x8c\xc7Q\xb2J\xa8\x82\x93q\xe4.V\\\xe0/EY\xe4pzm\xe2p\xb4\xb6qD\xd5v\xe9\xf6:\xb67\xb2ڧ\xfb\xed\x98\xe6^\x1dⲕ\xafc\x1c;\x8d>\xc6H;ő\x0eu\x8es\xe3V\x17\xd8mWZL7\x9e>\x89\x13\xe8?\x8d\v\x00\u007fF\x95\u007f\x1eW\xe7\xef\xce\xe9\u007fA\xcd\xf4\x88\xf3)\xac/\xe3(V|\x05e\xf7\x8cc\xafE\xaf8\xeb\xe8\xbdiI_\xc7I\x19\xbe\xc9\b\xf7[\xaa\xff;\x88\xbc\x0f/\xea\x1bGQ6\x8eU\xb0\xfe\xb0\xb9\x01qP\xa4\xef\xe9\xaf\x03\xe3$@\x83(\xfe\x1f\xe2\xcc\xee\acrC\xd8\xf1и\xe4\xdf\xc3\xe2\xc2Ć\xc7ItGPB?ƹ\xebsd\x9c\x89\xff(\xdc\xf7S\x1cAqt\x1c\x98R-\xc1\x1fF{#A\x02Y\x1d7\xd6H\xc8J\xfb\x9bh\xf1\xad\x04\xdep\x9e\x10\xa6T\x93=\xbe\x93\x00\xaa\xbd\xab\xf7\xd6J\xa0\f\xfd\x9e~\xac͏u\x12\x9ay\xbe\x9f@\xc8\xff \x81\xd2b\xdd\x04\xb4_\x8f3\xae\x9f\x00\x05o\x80\xb15\xd4)7JЀ\x1a\xa3\xff\x0f\x13B~\x9b$\xf0\xab \t@v\xb3\x04\x98Vs\x88\xe6#\x8c\xacEB*_-U\x9e\xad0\xf1։|iꑕ\x8d)ٴQ;\xfe\xe5\xc0w\xce\x04\xa8\xa2+\xa1[_)V\x0f\xba\xf5r\x16>\x8c̟\xe0\x9exC\xdb\x0f$\x80\t\xc1\x84\xee@\rA\x9b\xe1\x84\xec\xf2\x89`\xa4QH-\x86oL\x1c\xe3\t\xbc\xf2$\x01\vN\xeaT-\n4\x95\x90\x97\x95\xe4\xf0C\x1b|h\x9bХ\xa2v\t\x84\x82\xf6\xf4H\xe0ͬ\t\x12\xb8\xaf\x12\xdcu\xd43\xa1iL/\xb6\xd9;\xa1[\xad\xbe\x86$\xbfI\x80\x0f}\x9b@p\xfd.\x81-\a}h\x85}\x13\xdc\xc9\xd8/\x81\xc7$\xfb'\x90\xed\x0eH\b\xca|\x9f \x80\x0fL\x80\xd6\fR\x1d\xfc\x00\xe1\x0fN\xa0\x162\x84b\x1e\x9a\x90|{\x18\x9b\x1e\x9e@eaD\x82/\xd4I\b\xa8\x8f\xc4\x05\xa3\x12do?\xb1\xc5\xd1\tԛ\xc7$X\x9c\x1a\x9b@\xf0\x19\a;\x19O\x03\x9a\x90\x99\xcaD\x1a\xe5\xa4\x04\x1f\x00\x9d\fALI\x00\xab\xa7&\x88\xe3\xd30\xb2\xe9\xecwFB`zfB\xa9\xc4,\x1d\xfd\xcf\xecd6\xfe;'QH|\xbec\xfb\x8eYsU\x03\xf3(\xa5\xf90\xdc\x05\t\xd6v\x16\x12K~\xe1\x18\x16\xa9\\\x16\xa3\xbb%\xf4\x9d_\x13\xc8/\u007fK \x85\xa4Ζ%PH_\x9e \x11]\xc1\xd1\xfe\xce\xd3ʄ0\xacU\xbcm5\x84\xb4\x06\r\xaeE\xd7\xebx\xd5\x1f:\x82\xf5\xf8\xd7\x06\xf5\x90\x8d0\xe0M\t\xa6\x1a\x9b\x13R\x82ْ@\xa2\xb8\x95\xa7m\x80\x87\xed\tT\xf6v`\xb2;a\x8a\xbb\x12\xc2Tv\xe3\xb8\aǽ\t<\xe8\x81\xe3_\xf8f\x9f\"\xfa\xfe\x04\xaa:\a\x88\xca\ay:\x94\x10\xa2t8\xf1\xbf\xe7Տ$\xc0d\x8e&H\x89\x8f%\xf0\xf4\xe6\xdf\x18\xd4\xf1\x04\xb8\xe9\tZ\xe0?\t\xe1\x05'\x13\xba_'\xc1\xc8t\x1a\xeev\x06ןM\xa0vy\x8efx>\x01\xfex\xe1\xff\xfb\xbaHt\xb9\x94\xc02\xe3e\xdcy%\x81\x12ݿ\td\x93W1\x83k\t\xd0\xd0\xeb\x1c\xf2\r\xce\xe3&\xfd\xf3\x16\xe4p\x1b\xc7;\x1c\xdf\u007f\t\xa9\xb1\xddU\x13\xb8\x97\x10.z\x9fM\xe4JJxΝ\x94\xd1\xe5I\x8a\x1b\xe7M2\xd3˗\xd4e\xc9d\xe6\x05\xacI\x91L\xc1$\xc2V\xa1\xa4.K&\xf1\x18ca\xfdX$IN\xf6 \xafz()\xe1\xef\xe1$v:\x15M\xf2\x8d\xe5I\xc9N\x8b%\xb1\xc9\xee\xd1$ܴx\x92\xfb)J$\xb1$\x99\xc4\xfe\xb0ǒ\xa8\xee\x94\xc2w\x8f'A\x14K')\xd42I\x85\xf6\xb2I\xd0\xfd'\x92\xb2\u0378\\\xfa\x0eysN\x12b)\x9f\x94\x1aQV\x92\xa5ɧ\x92,\x9d?\x9d\xa4u?\x83f\x9fM\xc2\xe4+$\x05\x8c+&\xf1ө\xcf%\t?\x95\x92\xb2Z\xf7|R\xaa\u0095\x93b\xf7/\xe8\xbd/\xe2\xbb*\xda\xe2KI8dUH\xf2e\xbd\xe4\x15m\xe4\xd5$\xa0\xe05\x9e^O\xf2'C\xaa%!\xfc7\x92\x8a\xdeՓ\\\x90\xa8\x91d\xcd\xe1\xcd$\xe3\xff[\xc9\xc2i\x91u0,\xb3kǬ\xb7\x93\x92\xc6\xd7L\xe2\a \x93ؓ\xf4n\x12\xe9Z\xad$\x90\xf7\xbd$KD\xb5\x93\x00\xad:IX\xd9\xfbI \xc2\aI\xd9(\\7IfY/IH\xa9\x9f\x84\x1b6H\x12v\x1a&\xc5\r\x1b%\xc1\xaf\x1b'\xc9\xf6>\xccȺI\x12\xd5\xf8\xa6\xec\xb4Y\x12`\xdc\x1c\xa2\xfb(\tCk\xa1zlɮ[Aƭ\x93\xcc]\xb2\x93X\x1e\xb1%\xb9\xa95\xc9\xf8\xe3H\x8a\xaf;qt%\xe1G\xee\xa4\xc4\fOR\x16N\xbdI\xae\xee\xfa\x92\nt\xfe$B\x94\x81\x1b\x02ɂi\x80\x93\r:A\xea<\x94d\x0e\x1a\xa6\b\"\xbc8\xaa}Œ\xc2\xceͤ\xb8F<\xc9%\x93\x84*,\x99ԇ@\x92\xc8/SI`[N\x92\x9bx\xdb$\x91U\xb4M\x92ֵK\n\x9e\xb5Or\xbfs\a=wL\"\t\xfb8\t\xd8\xef\x94\xd4\xf0\xd99ɽ2]\x92\xfa\xecGW\x1a}\xb7$\x17\x92?I\xa6S\x0e\xa9\xb2BZ\x9fѠ?OJh\xeb\x8e\xe3\x17I\x90\x9d\x1ePΗI)\x91\u007fE{\xea\x99$K\xef\xa5M\xf5N\x82f\u007f\x9d$\xb3\xfeF\xcf\xdf\xd2B\xbfK\x82\xef\xf6I\xe2ɘ\xbeIa-\xfd\x92H\x85\xfa'\xf9bV\xe0\xc0\xf7I\x14\x81\a&\x01\xb5\x83\x92\x02\x17?\xa8\xe1\f\x86܇$\xf5\xf9ءI\x86\xe8aI$\x88IĊ\x11\x14\xfe\x8fI\xa0\xd8\xc8$\n\x89\xa3\x92\\e\xf9));\xc8F'Q%\x1c\x93D\x02>\x96=\x8d\x83\x89\x8eO2+\x9d\x90\x9481\x11\x984)\x89\xb7'LN\x92GO\xd1K\xa6&A\xba\xa7a&ӓ\xc2 f\xb0\xa9\x99I\x84\xb9YI\xacI&A\xaeg\xd3F\xe7\xf04\x97\xa7yI\x81\xc6\xf9\xe8e\x01\xccc!\xc4\xfcKR\xaa*\x8b\x92\xc8{\x16SpKt\xb2\xbfr~\xbf%\x99\xe5-MJ\xa4[\xa6\b\xb8\x9c*X\x01\x1b\xfe=\xa9\xbb\x00V&\xf5uV\xab0\xa2\xd5I\x90\xce5t\x93\xb5\xe8q]\x12q揌٬W[ې\x94,d#\x1du\x13\x86\xb89\x89%\x8e-\xea\xa0[\x93`\x8dۀ\x0f\xdb9\xd6\x1d\x18\xd4N5\xdf]\xb4\xccݘԞ$\xc2\xc5^\x8c\xf0O\xc8\xee\xaf$71\xedK\n\x8b\xdcO\a?@\xe79\x98\x14\xb2t(ɼ\xf8pR+\x0eG\x92\xcc+\x8e&\xf1\xe6\x9fc\x18\xe5\xdf0\xe1\xe3h\xf9\x04\x80\xea\x1f\xcc\xf7d\x12u\xacSI\xf0\x8c\xd3I\xe5sg\x92H^\xce&Y\xcd8\x97\xc4\xf6\x85\xf3\xc0\xed\v4\x9b\x8b\x8c\x13\x97\xe8\u007f\x97\x15U\xaf\xc0\xff\xfeU\xb4\xbc\x9a$Q\xb8\x96d\xa5\xf5:۹\x91d\xc2x\x13\xc0|\x8b\x00q\x1b\xe3\xbcC\xf9\xff\x97\x94\x1c\xfc.\xcc\xee\x9e\xfa\xff}F\x84\\\x16\xb7\xb6綤\xa7<\x16~\xc1\xc3\xe2\x06V\x9e\xf2[\xa8\xb2\x14\xb0T \x05-\xdcX\xc8b\"h\xe1\xc7\x01\n[\x8c\xb9E,\x0e\xfcA\v\xcff>d\x89\x84\x1eF\xdbE-a\r\x8fX\xdc\x00Y̒\xc9?jI._\xdcBu\xb5\x84\x05s+\xc9\xf6\x1f\xb3\xc4\x01KY\x82\xba\x8f[(ܗ\xb6 \xd62\x96BdY\v\xc1\xf7\t\v3.gI\x18{\xd2b\xb0*\x8f\x99dY\xd8\xdb\xf5\x94\x05\xf9?\x8d\uf7b1Ĵ\x9e\xb5hu\x15,\xf0ӊ\x968\xd5s\x9cS%\x8b\xf4\xecy\vҫlѸ^\xb0\xf0\xb8\x87\xc5\xf4\xad\x8a\xa5&\xff\x92\x85zLU\x8bo'\xb7\xc4\x0e^\xc1\\_Uپf\t¿n\xd1\xf6\xaaY\x00\xff7,E\xc9\xea\x9cv\rK\xab\x1eoZ\xcc|\u07b2\xb8\"\xf3\xb6\x05\xe4\xaai\xc9s\x99\xefX\xe0\xf7\xefZ\b1\xb5 \xa3\xf7,!\x1f\xb5-@L\x1d\xcc\xf1}\v>\xf6\x81E]\u05f5\x18\x17\xeae$W\xdf\xe2zo\x83\x8cR\x1bZ\xe0,\x8d,zZcK\x9e\x95\xfcPzIf5\xb1\x98\xc86\x85\x1a\x9b\xb1\x9f\xe6\x16\xb9\xe5G\x16\x92\x84\x16\x96\xb8WK\x8b\x05\x85V\x9cbk\xca6\x9b\xb6`\xb3\x00\x02v\xca\xd8a!CrZ\b?.\v\xb1\xd2mq\x85\xcac1\xbczu8>\v\xbe\xee\x871\x18\x96Xv\xc0\x02\xa7\fZ\b\xd5!UQ\x18\xb3\x8fXB\u0896.3Z\xa8#\x98\x16(i\xdcb6\x9e`\x8fIK\x00Ѳ\xb8\xf1$\x05\xa3́T\xdbXL4\xdaZ,j\xb5\xb3\x88\xc5\xed\xd1E\aKbkG\x8b\x14\xf2c\v\x85\xd5N\x16xUgN\xbb\vu\xd0\xd5\xd2\x10\xd2\xcd\xe2\xf3\f\x9fX@\xeeO-\xb0\xd8\xcf2\x1a\xf8\xdcB\x14\xe9n\xe9\xfbu,ҹ\x1e\x96l\xad\xf9\xd2\xe2\v\xca-\xacB\xf6\xb4P.\xea\xc5)\xf4V%\u007f\xadV\xfb\x8d\x85Bͷ\x16\x1f^\xfe\x0e\r\xf4\xb1\b!}\xf5\xa2~\xdaM\u007f\x8bTj\x80\x95\xa7n\xd6\xf7\x96 \xe0@\v\x94k\x90\xc5\xdd[?X($\x0f\xb6$\x9b\x1b\x02q\r\xb5\xc8\xee\x86\xc1\xbb\x87ӹF\xe8\x97?\xd2\x1dFZ\x92\xab\x8c\xa2`~\xb2P\xca\x1a\xad\"\x1dC\x13\x18k\xe1\x99H\v\x11~\xbcZ\xfd\x04KX\xf3D\xba\xf4$\v\xc5\xfa\xc9\n.S,]\x0e\x99jI\xde>\rښn\x11\x14g(\xb0̴X\xa5\x9ee\xe9\xf6\xbf\x9f-!\xb1\xb3-\xd6\x15\xe6dژkI&;\xcf\xe2\v{\xe6c,\v(х\x16B\xe6/\n%\x8b,\t\x9d\x8bq\xc5\x12\xb8\xf7\xaf\x16\xc2\xcco\x96\xae\x00-\xa5\x13,\xb3\x84A,W\xa3]a1]\xfc\x9d\xc0\xb0Ғ\xb7q\xafR\xa9\xae\xb6d\xd1u\r\fz\xad\x85,s\x9d%1\xe5\x0fK\xe2\xd9z\vkp\x1b8\x9e\x8d\x16\nÛ,r\xbe\xcd\n+[,\xae\xf7l\xa5eo\xb34\u007f\xd9n\x81\x13\xec@{;-\x92\xb2]\x16\x82\xdcnKb\xc4\x1e\v+%{1\x99?\xa9\xb0\xbf,V\xf4\xf6Q\x9d\xfb\x89J\a\xb4\x8f\x83\x16\b\xc2!\x8b\x15\xeb\xc3\x16\x19\xc2\x11K\xb2ͣ\x16h\xd71\xf8\xc4ߖ\x84\xc3\xe3\xf8\xfb\x04z\xfb\x87\xd2:\t\xdc=\xa5\xd6z\xdabX=c\x81\xf1\x9c%蟳\x90\xfc\x9d\a\xa2]\xb0X\x1d\xbd\x88\xb6.\xa9=_VǸb\xb1\xe2\xf1\xaf\xfa\xe7U\xe0\xe35\x8bD\xf1:-\xee\x06\x15s\xd3\"\xf7\xbde\x81\xb3\xdcV8\xbc\xa3\xb3\xf8\xcf\xe2~\xf6\xbb\x9c\xf3=\x8e\xe5>ƛ+\x05\n\x92;\xc5v\xf3\xa4\xc0y\xf3\xa6\xe4M\xe7\xf9R\x10J\xfe\x14[/\xa0\xd7\x14LI\x0eU(%h\xf9@\n\xceXX\xffU$U0\r\xb3\x01y:2\x85\xfd\xac)1\x83\x87S\xe2+ES\xb0\xf9GR4\xf6b)&\x8e)\x91p\xf1\x94\xa8\xde\xcc*\x91\x12Q\x94\xd4\xd6\x1eKQ\x14\xa5\xf4\xfcx\n|\xb6t\x8a\xaeT&\x85pYV\x1b|\"\xc5\a\xb4ʡ\x8d'S@\xe3\xf2)\xfa\u007fVJ$\xfe\x14\x06\xf2t\x8aH\xf7L\nk\x91Ϧ\x04o+\xa4\xc4\xfc+\xe2\xf8\\J|\xb4R\n^\xfc<%R9%1\xee\x85\x14\"\xe6\x8b)\t\x1fUx\xc1K)\xa9*VMq\xc9\xfb\xe5\x14T\xf0J\x8a\x80\xfdjJ\u007f\xe7*%\xc2~\x1d\x92\xab\x96\x02o{#E\xfc\xae\xce;j\xa4\xf8\x9e\x00\xbd\xf1\xad\x14u\xf6v\x8an_3\x05T}'\xc5\r\xad)\xe8\xbdV\n\x99\xf5{)\xacD\xd4N!\xc6\xd5I\xa5\xa1\xee\xfd\x14\x02\xe8\a\xbc\xabn\x8a\xc0T/%.S\x1f\xc7\x06\xfcW\xc3\x14m\xa4Q\n\xdbM\x1asN\x1f\xa6P!k\x92\xe2\xaax\xd3\x14\x93\x86f)0\xa6\xe6)!\x13\x1f\xa5\x84.\xb4\x80\x19\xb4L\xa1>\xdaJU\xd1Z\xcf\xd9)\x89\x9a6\x95\xb7=\xa5uyG\n\x8fv\xa4`\x86\xae\x14\xf9\xb6\x9b\xa3\xf7\xa4\xb0\x88\xe0Mi\xd8\xf0\xa5tS\x86?\x85\x92\x8a\x01\xed\x06R\x92\xa6\x04iB!\xde\x19NI \x88\xa4\x94\x93FS\x8c\b1h\xdeL)\x1b\x8f\xa7m\xa9cz\x86\x89\x14\x96\xa5\x93)\x12\x05Kl\\\x9e\x80\xe4\xa0rR\x84\xa26)R϶)p\x94vj\x98\xedS\xd8\x14\xd4!\x85\xc71:Rs\x1f\xa7ȸ:\xa5\xb8c\xa2sJѳK\x8aT\xa2+\x15\xdd-E\x87\xfe$\x85(\xf7)\\\xe4\xb3\x14\xe2\xfc\xe7\x9cJ\xf7\x14\xb6\x01|\xa1\x16\xd2#U\xa0\x05\x9eh\xf8\x92\x8a\xfe\x8a\x9e\xd4S\xa5\xdc\v\x8a\xe8\r\x87\xfb:\x85\n\xc27)\xecj\xf8\x96\x17~\xc7S\x9f\x94\xb0\xe4\xbe)D\xef~\xbc\xb0\xbfNt\x80\xce\xec\xfb\x14\xa8\xea@\xfd8H\r\xe7\x87\x14 |p\x8aU\xa1!)\xee\xb7\x1c\x9a\x11\xf60\x18\xf6\xf0\x14\xc8\xfd\x88\x94\xbcX\xe7ǔ\xe0\xd5\xc8\x14\xd8\u0528\x14\xd2ğR(2\x8dN\x91\xf9\x8d\xa1\x03\x8e\xe5\xa4\xc7\xc1*Ƨ\x80\x97\x13R\xb2\xcbfbJ\x88\xce$\xda\xe4\xe4\x14~\xca#\x85\xed\x9bSS\xc2a\xa6\xa5$\xbeL\xa7\x87\xceH\xe9Z\xe4̔\xe4\x01\xb3\xd8\xea\xcf)\x89\xf9\xb3\xd1\xf6\x9c\x14˺sSX\xf6\x9a\x97Bd\x9e\x9f\u008e\x89\x05\xd4\xfc\xc2\x14\xabe\xbf\xd0\xc3\x16\xe1\xf6\xc58.I\t\xf2\xff\x9a\xc2\x16\x1c\xb6\xbe\x14\xa8\xb0\x8c\x03\\\x9eB\xa6\xb3\"\xc5ױ\xf2\xb42\xc5\x17\x06\xacJ\x91\x1d\xadN1\xf5]\x93\xc2*\xdeڔ\xc4\xdeu)݈\x93B\xa4[\x9f\xb1\xf9\r\x98\xdfFX\xc8&\x80\xd2\xe6\x14\x97\x10\xd52\xb6\xa6\x84\"l\xa3\xcflOa\a\xc2\x0e\xe0\xd1Nܴ+\x85ط;\xc5\xdfoݓ\x02'\xdb\v\xef\xf9SM\xff/:\xf5\xbe\x146\xc4\xee\xa7\x18\x0e\xa4\xb8\x86\xa8\xf6\u007f\x88\xdd\x1e\xa6\x05\x1f!\xe8\x1f\xa5\x95\x1eӱ\xfc\xadv}\x1c\x86x\"\xc5\x17礸\x10v\x92&x*\x85\x1a\xd6i\xfa\xf2\x19\xf8\xf2Yj\xe3\x1c\xb0\xe2<\x86v!\xc5\x05\xe4\x8b\xec\xf6RJi\xfd\xe5\x14^\x81rE\xe3\xc1\xbf)-\xba_\x85l\xae\x01c\xaf\xa7d{\xf6\r\f\xe2&ڼ\x85\xe3mz\xe9\x1du\xc6\xffRȍ\xef*J\xdc\xe3\xec\xefsJ\xb9r\x18\xf1r\xe7\xc8\x00\xf3\xe40\x12\xe7\xcd\x11\xa8˗#t+\u007f\x8e\xbec.G\xb0\xb4`\x8e\xe8\xa9\x10\xfe\xff@\x8e\xf4\\\x18\xc7\"9\x14߃9\xf0\xb9\x87r\xc0z\x1e\xce!\x13)\x9a\x83\x1d5\x8f\xe4`\x92\xc5rಏ\xe6\xa8\x15\x17\xcf\x11s/\x91\x03\x89\x97\xcc\xc1\xf6\xa0Ǵ\xd7R\xda\xc2\xe39x\xa0\xact\x0ea\xbaL\x8eTZ\xca\xe6P\xd9O\xe4\b\x01,\x87\xa1\xfd\x1fOW\x1d(E\xf5\x85\x95\x0e\xc1n\x85G\x18\b\x88\xa2\x12\xa2t\x83t\x83\xe0\xa3{cf\xb6{6\xe8\xee\xee\xc6\x00\x11\x14\x03E,J\x10TB%\x14Q\x10D\xba\u007f\xef|\xdf\xd9\xdf?o\xde\xee\xceܹ\xf7\xc4w\xbes\xee\x9d;\xa5\x1c\"\x9a\xd2\x0e:{\x8eC\xf0\xbb\x8cC\xa5W\xd6\x01\x16V\xceA_/\xef\x80\xe6\x9er\x88\xff>\x8d^<\xe3 \xa1x\xd6A$\xaf\xe0`T{\xce!nU\xd1\x01\xa1Ur\x88\xc5Uv0\xb5y\xde\xc1eNU\x1c\xa0[/8ȳ^t\x80\xbbUu(K})\xfb\xcf\xcb\x0eM#^q\b\x9aUs0\xc6Ww`\u007f\xa4\x1a\x0eݎ\x95\xe2xՑ]-R\xcb\xc1'\x94_s\x80X\xbd\xee\x10\x16_\xdb\xc1$\xbd\x8eC\xed\xa6\xaeʫ\x9e\x83\xa6Z\xdf\xc1 \xdb\xc0\x81<\xae!;\xd5\xc8A\xa6\xddX\xcfj\xe2\xd0\xf7@:`\xbb\xcdxh\x8e\xfe\xb5Pa\xb5\xa4\xf2\xdep\x00\x01Z9\x84̴v\xf0%\x90\xaa\xac\xb6\x0e\x81\xddv\x0eV\x82\xdb;\xe0m\x1d\x1c|\xde\xc3!0\xd6\xc9!\x0f\x04v\x86\xf9tq\x00D\xba:\x00\xcfݴ\v\xddU\x01=\xf4\xf8&\xb4\xda\x13F\xd6\v\xff\xbf\xa5*\xc9u\xa0(\xd8ۡ\x10\xd2\xc7\xc1\x18\xe9@\xa6\xd1\xcf!@\xd6_\x856\xc0A<\x1c\xe8\xd0\xe08\b\xfa\x1c\xec\x10o\x1a\xe2@D\x1a\xea\x00\xf7\x1d\xe6\x80\xc7\x0ewp\xda\xd8A\xc19\x1d\x88G\xae<\xad\xe4\r\xc6\xed\xe0\xd3\b\x86C\x02\xb1\xe9\x00;\xb3\x1c\\h\xe3\xc0ۚ\xbd\x0eqo\x9f\x83\x85d\xbfC\x03\xa3\x1e\x83\xd4C\xc8\xc1\xac3\xec\x10\xf4\x8f8\x90\x1cD)\xe1\x98C2\xa38\xfe&\x1c\xca\x11l\x87\x16\xe5\x92\xd9oR\x0e@aZU\x90q M\x1b\xe1\xc0^l#\x1d\xe0\xeb\xa3\x1cx6u\xb4\x83\v\x8a\xc68$\xd9\x1b\xcb.\x8cSs\x1dOט\xe0@\xa4\x98\xe8@0\x9b\xa4\xd63\xd9!\xe0;\xc5\x01̞\xea\x10 \x9f\xe6`\xda9]\xedu\x86\x8et&t4\xcbQ\xa0\xf5\x90\xbc\f\x92\x864\x87~6\x97\x9f\xe6\x01j\xe6s\xb0\v\xa8\xb4\x85\x0e\xe7\x04Ө\xe8\x14\x16R\x89?Uvr9*\x0fU\xf4V/8\xc5\xea^t\xe2\xe5VNZ\xe7K:\xf2\x97\xf5\x9cW\x9c\xf0\x87jN\x00Eu\xa7\xf8[\r=\xa7\xa6\x13\xd5\xf9WU\x82\xb5\x9c\x98\xd8{\xcd\xc9-\xf6^׳j;aQuح\xba\xce\xec\x02\xa8zNr\xfe\xfaN\xdaU\x03'\x83}C'wdu\"\x907\xe6\xd5M\x9c\x8c\xa8M\x9d\x92]5SI6w\xeak\xae\x9c2?\xd1\xd2\xc9x\xfa\x86\x13k\aZ9\x11\xbb[Sdm\x9c\x85\x9a\xe2i\x97\xb6N)\x91\xb7s\xc2\xca\xda;I\x17;\xf0\u070e\xd0L'\x88\xa5\xb3\x93P\xdaʼnH\xd4\xd5IP\xec\xe6$\xc4w\xd7A\xf7\xa0H\xdf\xe4]z:e!U/\xaa\xe7-v=\x97\x87\xdeN\xae\xae\xea\xe3d\x85U\xef\xdcω\xc8\xd6_\xedg\x00;2Љ\x901\x88\x9f\x06;\x05t\x87\xd0҆\xc2\xee\x879A\xe7\x87;\xb5\xc8\xedpr=\xa0\x93&\xefR\xa3r\xebр1\x98N\x06\x12\xcbI\xa8\xf68\x81\xf7^\xa7n\xf5\xe9s\"G\xf5;\x91&\x05hzA'\n]!\xd5R؉\x82@\x84\x87\xa8\x13x\x1eSό;\x05\x99\x12N\r\xef6m'\xe9īFRN\x8d\x9ai'_\xe7\xe1\x14\x9e2\x02\x165\x12n3\xca\xc9Lv\xb4S\xa0m\x8c\x13\x8b\xc3\xc6\xd2m\xc7Q\x14\xe3\xf9\xe5\x04'\xb3\x9c\x89N\x99w\x9c\xe4$K\x98\xec$Ϙ\xe2\x04\x99\x99\xea$%\x9c\xe6D\x01vzVV3\x9cX\xc05\xd3\t*5+\xfb\xf5\xec\xec?s\xe8zs\x9dʌ\xe7Q\xc5\xf3\x9d\x92f.pbI\xdaB\xa7\xce\xc0/\xa2\xae\x17;%!]\xe2\xe4\xcc\xdeR\xa7D\xeae0\x86\xe5N,\xc3X\xc1\xc3J\x15\xfd*\x15\xe7j'i\xd4\x1a'61\xa7\xad\xbd\xed\xc4\xd4\xd8;Y\x81\xbd\xcb\xcf\xef9\x91\xee\xae\xe3a\xbd\x93S\xff\xef\xab\xec78\xc9b> \xe8ld\xbf6Q\x97\x1f:\x19h?\xe2\xd06;\x11|>V\x9f\xff\x84*\xfe\u0529\x1c\xee3\x0ex\v\x8c\xe6s'\xe7g\xbe\xa0\x19oU\xd8\xfaR\xfd`\x9bS\xe8\xe9WN\xe2\xfe\xd7N\xd6$\xbeqb\x8a\xe2['c\xf9w\xda\xc7\xedj\x8e;\x9cH\x81w\xaa\xd1\xefr\",\xedv\n9\xfd\xde\xc9j\xec\x1e'J\x00{\xd9\xd5\x1f\xd8\xf1}:\xc8\xfdNp\x8e\x1fi\x85?\xd1(~V\xf3>\xe0D 9HM\x1d\xa2 \x0e\xab\xa0\u007f\xc9*\xeeW\x98\xdfo4\xab#t\x9b\xa3Y\x138\xe6\xc4l\xcaq~\xfd\xbb\x13\xd3e\u007fP,'\bd\u007f:\xc9>O:\xb1=+\xcf\xfc\x9b\xa7\x9cr2\xf19\xedD\xc0\xfe\xc7)\xd5\xcd3\xfc\xed\xacS\xa8ٿ\xb0\x90sY\r\xff\xe7\x04S<\x8fpsAE|Qm䒓5\xc5\xcbD\x8e+N̊\\u\x922^sr\x02\xe6\xba\xe2\xd5\rh\xed\xa6\x82\xe9-\xaa\xf6\xb6\x93U\x86;\\\xf2\xe3\x9d.\xe9B>\x17\xa8G~\x97\x10\xc9\x02.\xc0PA\x97\xe6\x02\x88<\xeeB\x00z\u0085\x89\x9b']\xa8\x96\x94r\xc1\xb3K\xa3\x839.\xa4depײ.\xdaB94Yޅ\x97&\xbbPru\xd1p\x9fq1\xa2<\xeb\xd2\x1c\xd3\xc5\xe9\xc4\xe7\\\xfa\x00@E\x17\x9e\x15\xac\x84\xc6+g\xbf}\xde\x05\xf0\xa8\xe2\xa2n^\xe0@^䡪K\xc0\xec%\x8a\xefe\x97\xa0\xf6+.\xd6i\xab\xb9\xf08Au\x97@[\r\x970\x88\x9a\x14ɫ.-j\xd5r\xe9\x84\xc3k.\x9a\xd6\xeb<\xa16\x0fux\xa8\xcbC=\x17\x97hԇ\xfa\x1a\xb8\x90\x185t)\xf26rqy\\c\x17|\xa0\x89\x8b\xabut\xa8\xcd\\\xf4\xfb\xe6.\xfaD\v\x17\xbd\xb7\xa5+\xfb\x9aH\x95Q+\x17Cxk\x97\xa4Qm\\\xa4\x04m]\xf0\xa8v.\xb8d{\x17\x8b\x01\x1d\\,\xecv\x84\xbc:\xb9X\x1c\xef\xac\xc7..\x86ˮ\x14T7\x97\x1a~\xf7\xec?= \xad7](\x12\xf7ta=H/\xde\xe1-\x97xX.\x05\xdf;\xab\x88>.\x14\xe7\xfb\xf2\x8a~jI\xfd]\x02G\x03\\\xc4\xe1\x81l`\x90\v\x1e;\u0605\x12\xfd\x10\x17\b\xd6P\x97\xbe\b\xcb\x05\xa8\x1a\xceA9\\\xe0\x93N\x17\x1f\xe9pa\xe1\xafۅM\xbd\f8\x8f\xe9BnfQ\xb2\x1ej\xc3˃\xcf\xc5\x05=~\x17ʏ\x01\x17\x1et\t\xba\x88\x06!\x17\xc3T\xd8EO\x8d\xb8\xf2\xb5ʉ\xbad6 \x96\x95|\x9c\x0e\x94\x80\x10m\x98r\x12\xff\xa7x\xdb4ԝ\xc1\xdf\x11\xf8a$\u007f\x18\xe5\xd2\xe9\xef\xd1.\xd4\xe8\xc7\xe8M\xc6f\xe55\xce\x05<\x1dOG\x9f\xe0\x12\x8e3Q\ra\x92jv\xb2\v\x91y\x8a\x8bh1\xd5U\xa4\xb5#`I%x\x1a\a8ݥ1q\x86\vLv~\xab\xe4\x96bce\xb7Ү\xe7\xdd\xe0\xb2UܚL\xbf\xe0f\x16\xf8\xa2[*\rU\xdd\xdcB\x807z\x99\x1d~\x85\x87jn\t\xe2\xd51\xde\x1an\xa1\xd65\xddX\x1f\xfc*\xbe\xaa冺^s\xcbnƯ\xbb\xb3Yfm\xb7\xac\x1c\xaa\xe3Vܪ\xeb\xceVr\xeb\xb9eڢ~^\xaf\xe5S\x036\xd50{^#73\xcc\xc6\xd9/\x9a\xb8\xf9P}S7\xe6\xe0\x9a\xb9\x91\x954w\xd3\xcdZ\xb8\t\x15-ݘ\x06}\xc3-e\x88V\xff\xbfWk7\xf6\xa9n\xe3\x06\x19o\xebf\x94h\xc7{\xb6ws\x91N\a\xb7D\x82\x8e\xff\xbf\xa8\x93\x1bU\x8fΔE\x174\xd9\xd5-\x93\x01ݲ\xc2\xeb\xeeFf\xd5\xc3]\xa4\xa31p\x90 śn\xach\xe9\xe9F&\xd7ˍ\xe5Zo\xb9\xb1\xfe\"\xd7-\xc9Ro7j\x93}\xdc\u061d\xa3\xaf\x9b\xe5\xf3~n.k\xe8\xef\xc6\\\xf1\x00\x9c:\xd0M\xee2\xc8-qh0\xadp\b\xe46\xd4\rr6\xcc-H4\\G\xefp\v\xadq\xb2\xdf.\x9c\xe7v+\xb91Т\xe9\xc6~\x02nB\xa2ǭ3\x98z\xf4\xb9\xa5f\xe8w\xcb\xd6(\x01\x18{P\xbb\x17r\xeb\xbb@\xf4\xca\bz\x14\xc5\xfdb83\x0e\xf7I\xe0\xaf\xed\xc6\x06*I\xb7\xb0Д\x0e\"\xedF\xb0ȸ\xb1\xfe`\x84\xder\xa4\x1bu\xeaQ\x90\xeeh7\x9e\x82tc\x9d\xd1X\x1d\xd487c\xd4x5\x80\tn\xc9\xf4&\xe2\xd4In\xe0\xc8d7J\x82SБ\xa9\x94\xcc47\x92\x90\xe9n\xf0\x89\x19n\x99^\x9c\xe9Fmz\x16\x8d|6\xfa:\xc7\r\xa4\x9f\xabN9\x8fڞ\xef֙\x84\x05\xee\xc2͆\xf4\x1b\x9e\xd7\xc4B\x9c\xbe\b\x1d]\xec\x06gY\xe2\x162\xb9\xd4\xcd\xe9\xa2eğ\xe5\xe8\xc5\n7Al%\xef\xb5\xca\r~\xb8Z\xbf]\xa3\xa3_\xebFXx\x1b\xde\xf4\x0e?\xbc\xeb\x06Kz\x0f\xc2[\xe7\xc6\xec\xe7z7\x17\xfb\xbc\xef\x96%\x81\x1b8\xa6\x0f\xdc\xd9\x14`\xa3[ӦM\x18\xe6\x874\x80\x8f\xdcL\xbf7î?vc]\xe3'0\xe0O\xddH\xf8>s\x93\xb4m\xf9\u007fK\x9f\xbb\x11\xe4\xbf\xc8b\xe4V\x1a\xf8\x970\x9bmnΒ}\xe5V\x96\xfb5l\xea\x1b\n\xfc[\b\xe3;\xe8e\xbb[fNwdmo'1g\x97\x1b\x94m7\xbd\xe1{\xb7\x10\x89=\x8ae{ݨP\xfe\x80F\xf6\xe1\xf2\xfd@\xed\x1f9\x98\x9f\xdc`t?\x13X\x0f\xe0.\aݨA\x1frK\"y\x18\xb6\xf8\x8b\x9b\x01\xe6W\xb7\xaez\xd5\x11\x1eq\xe35\x90G\u074c\xe3\xc7\x10S\x8e\xbb\x19\xba~\x87\xa8\xff`x8\xe1&o\xfb\xd3-\xf5\xe5\x93\x1ag\xfer\x93=\xfd\xadfr\x8aN|\x1a\xd6\xf0\x0f\xc7uƍL\xf9\xac\x1bS\x11\xfff\x01\xf7\x1c\xd0\xf9?76g8\x8f\x0f\x17\xdcx\x16D}\xeb\x12\f沛\v\xa9\xae\xc0̮\xb2\xc9k\xd4\xd3\xf5\xbc\xbb\xcbo7\xdcL5oR\xe2\xb7\xe8Q\xb7\x89\vw\x18\xe0\xecw\x1ar}>\x03\xec+\xbfAq\x140\xc8&\n\x1a\x80\xc7B\x06\x84]\xd8\x00b\x171\xc4\xfa\x8a\x1aR:*f\x88\xe4\x8b\x1b\x8c\xb2w\x19\xb8}\t\x035̒\x86\xf4\xf3n\x83ny\x8f\xa1\xc0|\xaf\x81\x8c\xf0>\x03\xbd\xb8\xdf@1\xff\x01\x83\xa4\xf1AC\xa2\xd9C\x86&V\x0f\x1b\xc2\x1c\x1e1\xf8\x9a,C%\xf4\x98!6\xf9\xb8\xc1\"\xfb\x13\x06L\xfeIC\x1e\xa4*e\xa0\x16Y\xda\xc83\xe5\xbcX\x96c`\xed^\x19\x03\xbe]\xd6\x10\v*\xa7\xb7*o\xe0u\x92\x06\xfc\xedi\x83\xa0\xf1\x8c!)\xe7\xb3\x06c\\\x05\x83y\xd6s\x067\xaa3\x80\xb5\x95xIe=\xe9y\x95Y\x15\x83y\xd5\v\xe8\u074b\x06V\x98U5\x10\xfc_2\x00Q/\x1b\xf0\xa9W\fƓj\x06L\xb5:\x0f5\fк\x9a\x06bΫ\x94u-\x03f\xf0\x9a!.\xf9\xba\xc1e\x11\xb5\rXL\x1d\xed@]\x03\xf3\xe3\xf5\f\xf1\xe5\xfa\xfae\x03\x03\xb5݆\x86\xd8O#\x03\x96\xd6\xd8@ͳ\x89\x01\xc8j\n\xd9638k\xd3\xdcȆ\xb1\x16<\xab\xa5\x01\xce\xfd\x86ZB+C\x01\xae\xb5!>\xdc\xc6\xd0iٶ\x06\xb9s;C\x9c\xaf=\xfev`\v\x1d\r\xe4\xf7\x9d\f0\xda\xce\x06HA\x17CL\xb9+\xfev3\x94\x80w7\xa4\x8c\xd2\xc3\x00#~\x13]\xeei\xd0qzQ\x0eo\x19dV\xb9\x069Xo\x83T\xbeO\xdeȼyI\xa5\x91\x17\xaf\x87\xe7\xf4\x83\xc5\xf57$\x90\r0\xe8\x1f\x03\rq\xb9A\x06\xd8\xe6`\x83\xd3ZCTgC\xa9\x9ea\x86\xf8\xeep\xfd\xd2ap\xee\xd2 $\xb8\fDh\xb7\x81j\xaea`+\x14S\x8d\xcf2\x00\xec\x1ej\xdbk(\xae\xfa\f\xdd)\xc0\xc0N\x01h>\xa8͇`\xe2a\x83\xcc/b\x10p\xa2l!FK\x8d\xeb\xb9\t8\x99M\xd1%\r\xdd\xdf\xdc\xe0ʯ\xb4!\xd17\x83[\x8c\xc0ߑ\x10\xdd(C\xe6pG\x1b\x9cS\x19c <\x8c\xcd\x13Jn\xce8\x83\xd3^\xe3\ry\x82`\x02\x1cy\"\xfc`\x92\xa1s\x96\x86 \xf7\x14\x83\xf1f\xaa\x81\xb2\xf64\x03\xecd:\x9a\x9f\x81\xf3g\xe2\xffY\x06s\xf2ٰ\xbd9\x06\x1f\xe1\x99kH\xb0\x9fg\b\xfd\x9bo\xf0\xd1H\x03!h!|c\x11-b\xb1\xba\xc1\x12\x83\xab\xf9\x97\x1a\xc0\xeae\x1c\xedr\x03\x91e\x05?\xad4PJ[\xa5`\xb2\x9a\xbe\xb2\xc6\x00\xa0\xaf5$\b\xbcM\xb7|G\x05\xfa\xae\x8a\xf0\xbd,\xee\xac3\x189\xd6\xd33އ\xf9o0$@~`\xb0\x94\xb4\xd1`\xd5u\x13\xba\xfe\xa1\xca\xfb#X\xd5f\xb8\xe0džL\xc1}b \xb8|J\xaf\xfe\xcc \xa7\xdcbH\\\xf9\x1cg|a\b\xe9\xd8j0\x8544\xaem3\x98\x1e~\x85{\u007fm\b\xf3\xfd\x86\x8d|k`F\xf7;\x83\xd1d\xbb\x81t}\a\xfcd\xa7\xfa\xe8.=\xee\xa6L\xbe7d\x12e\x8f\xc1\xbcb\xaf\x81\xa4\xe8\a\x83\x11k\x9fJj?\xe0\xfdǬa\xfe\xa4R\xf9\xd9`\xd1\xf6\x80\xaa\xec \xd1\xf8\x90\x81:\xefaU\xeb/\x86\x14g~\xa5M\xfe\x06\xb3:B(\xae\xbf%`}6:\x96\xd4!\xa48\xe64=6\xa3\x8a\x1ea\n;\x19\xa9:\x18e\x82\xe0\x8c6\x95Ɍ1A^\xc7Ҫ\xc6iK\xe3Mp\xcf\t\xb0扐\xda$\x935\xbf\xc9b1\xb2Ϲ\xc9\xf2\xe3T\x13(8͔\x19\xef\xe9&Ak\x06M{\xa6\xa9\xef\xcd\xca\x1a\xeblS\xd0tNV\x8fs)\xc9y\xa6\x10\xa1\xf9&9\xd7\x02\x13\t\xdeB\x0e~\x115\xb1ؔ\U0003b13d\\\xaag.\x83L\x97\x9b\x12-V\x98(ޭD7V\x99(\xe8\xad\xd6ެ1\x19\x1d֚X\xcd\xf8\xb6\x89\xe9\xcbwL\x90\xa3w՚߃1\xad3Q\xfc[or\x96\xe0}\xdep\x03\xcc\xef\x03\xf6h#\x84\xb2\x89\x9a\xfc\xd0$\xe5\xf9\x88\x06\xb6\xd9Ģ\x96\x8fMԣ>\x01\x06~\x8a\xae\u007f\xa6\xdaآN\xf4\xb9\tb\xf5\x85\x89\xe5\x8d[M\xac\"\xf8\x12\xbe\xbc\rJ\xfd\x8a*\xf8\x9a\x1e\xf9\r\xbe\xfb\xd6\xc4r\x80\xef\xb2\xe2\xdcnb\xfeo\aŸ\x93}\xdfeJ1o\xb7\xc9\x00\xf9=z\xbb\a\x97\xef\xe5\xe5?\xb0w\xfbT\xd7\xfbMaf?\x9a\x1a\xb8~B\xa7\u007fV\xe4:`\xe2\xb9\xf4\x83\xbc\xf2\x90\xca갉\t\x87_L!\a\xbf\x9a\xa8\x0f\xfdf\"\f\x1e\x81\xdf\x1ee珙\xcc\xf9\x8f\x9b\bp\xbf\x9bd\u007f\u007f\xa07'LYI\xf9\xa7Y\xbca\xbf!\xb9\x03,w\xee0+礩|\xfb/\x93\x15\x94\xbfi\f\xa7L,\xa4?m\xb2\xfc\xf3\x8f\t\x06t\xc6\xe4\xe2᳦T\xcf\xff5\xefj\xd8\xcf\x188H\x9b:g\x16\u007fc\x90;\xfb\xe9\xbf<\x85\xe6Eܜ\xf3\xe8\xde\x05\rK\x17\xe1Y\x97L\xf2\xa0˦p\x98+f\xf1\xa6\xfd\x86X\xa6C\xae̹j\x92O_\xcb\xfa\xeau\x13U\xf4\x1bf\xe1\xf6\xb9\xee\xdc!\xfdrn\xaa\x91\xdd2ɍo\x9b\xa8\xea\xdca\xf1y\xe0;-6\x9e\xcf\"\xed\xcbo\x91\x82\x14\xb0\x88\x1e\x05\xad\xe2\xed\xfb\r\xef\xc3n\xe7\x14\xb2\xa4\v\x85-j\xa6\x88%>^\xd4b\x98.fa2\xa2\xb8%T\xf5.K\x86\\\xc2\xe2=KZ\x8a\x1cw[\x9cm\xbdǂ\xff\xddk\xd1G\xef\xb3ă\xef\xb7$\xc2<`Av\x0fZ\x94\xddCz\xaf\x87-\x84\xa8G,P\xdfG-\x84\x83\xc7,}іEwxB\x8fOZ`\n\xa5,\x15JiKCl\x8e\xc5\xdaG\x99\xec\x17e-&J\xe5\xf4X\xde\x02r?\x95\xfd\xfdiK\x00\xe1\x19\v\xdb\xc4\xcbO\x8d\xb2BjlI\n\xd9\xc4\x02\xc1oj\xc1\xae\x9b\xa1\x1f\xcd->\x87\xd8B\x87\xd0\xd2B`}\x83\x87Vz\xdf\xd6\x16\xa2p\x1b\\\xd2VOm\x87F\xdb[y\x88\xe6\xcc\xe9`\xa1\xda\xdb\xd1R'\xead\x89\x87u\xb6dAH\x17\x8e\xb5+;\xd5\xcdBy\xb1\xbb\x1aa\x0f\x8b\xfb\U000a0c5e\xf8\xdb˒gy\u07b2PIȵ\xf8\x9af\x8c\xb5\x0f\xda\xebk\xc1\xc3\xfbYx\xd5j\u007f\x8c\u007f\x00?\fd7\x06Y\x05[\xf43rs\x06s\xa8C(͡\x96@\xe90\xfc\x1d\x8e;9,pT\xa7%\xbe\xe8b\xff\xdc\xf8ݰ\x80,\xa6\xc5r\xa0\xc5\x16<\x16\x1d\xcek\x91\xb6\xfa,\xc1W\xbf\x05\xfe\x15Ȏ=\x98\xb5\xb0PV\xfea\x8b\x95\x8a\x88\x05\xac\x8dⲘ\x85\xbd \xe2\xbcm\xc2*\xdc!\xd7\x14\xbc\xb0\xf5\xe4$\x1d)\x95m6\x9d\xfd'\xa3\x9d\x1aa\t\x06\x8edOG\xf10\x1a\x8e6\xc6\u0083,c\xe9o\xe3,,c\x18\xcf\xc3\x04Jo\"\xc6<\xc9\xca\x16!&[\x98\x14\x9db1_\x9d\nyO\xa3\x19L\xb7\x18KgX\xcc]fZ\x92\xefϲ\x90\xa3\xceVT\x99c\x81Y͵\x98Oͳ$\xbd\x98\x9f\xd7j\x9eZ\x16X\x82\xba\v-r\xb2E\x16\xb9\xf2b\xba\xcb\x12K\x92⥼\xd52\x8b\xb9(;\xb9\x82m\xae\xb4\xc0?V\xa1O\xab\xd1\xf15hq\xad\xc5\xca\xd2\xdb\xff\x1f\xc6;\xea\x9a\xefZȚ\u07b3P\xd7\\\a\xa9\xac'\xc0\xbcoI\xa6\xb8\x01p\xf7\x81\x85r\xc4F\xd8\xd0&\x8a\xebC\xb5̏,Ἓٓ\x8f-.\xe1\xff\xc4\xc2\xdaY(\xf03\vE\xba-\x96\xc4\xfd\xcfՓ\xbf\xa0\xf3oU\xe4\xfc\x92J\xdef\xe9\xb6\xe8\x96Tᄊ\xb4\xae\xf6\x8d\x02˷\x16Y\xc5w\xeao\xdb-M\xd2vX,\xca\xecĠwYXų\x9b\xde\xf8\xbd\x05\x12\xb1\xc7²\xa9\xbd*\xdc\x1f,\x04\x89}\xec\xf6~\vU\xd5\x1f-\xa9\x02\xfcd\x81\x8d\xffl\xe19\xe6\x03zσ\x16\v;\x87\xf4V\x87-!\x88\xbfX\xfazfH\xee7=\xe7\x88\xc52\xe7QKX\xd31\xe8\xe08\xef\xf4\xbb\x05\x06\xfa\a{s\x02\xa8\xff\xa7\x85\x82\xe7I\v\xa5ؿ,\xad\xd0Z\x8c\xe4\xa7TD\xa71\xb6\u007f4|\x9c\xb1\x94\x1d\x9e\xb5H\x03\xfe\xb5@\x99\xceQ\xd8\xffYB\x95\xcf\xd35.p\xac\x17\xa1\x8dK\xaa\x80˖\x14\x18\xae\xf0\xa7\xab0\x99k\x16\x92\x9e\xeb\x16_KiI\x1d\xfa\xa6\x85\xa5\xa0\xb7\xac\xbc\xf4\xea6\x05z\x87G<\xffN\x0f\x8c-\x9fGD\x96\xdf\x033*\xe0\x91RgA\x0f죐\x87\xb8^\xd8#\x91Ob\xa5\aO\x99\xe0\xeab\x1e\x14\x1c\x8b{h\x86wy\x84\x10\x95\xf0P7%\xf5ʻ=\"\xe2{p\xc1\xbd\x1e\xf0\x99\xfb< q\xf7{(\x9b\a<\xb2\xca\xe8A\x0fl\xe9!\x0fx\xf2\xc3\x1e\x94f=X>\xeb\xc1c[\x8fy\xe0-\x8f{0\xd8'\x0f\xb7\xb0\xf3H\xc9.\xe0\x11h\rz\x98օ\xf0)\xec\xc1\xfb\xab#\xc6)\x9fxX\"\xfb\xd4#y\xe4g\x1e@\xed\x16\x0fvnUS\xfa\x02>\xb1\xd5\x030\xfe\xd2\xc3\x1a\xc56\x8fNU~\xe5A\x9e\xf55z\xff\x8d^\xf2->}\a\xff\xd9\x0es\xdaA\xe1\xef\xc4\x0f\xbb\xea\x058>\x96\xed\xf9\xe3\xec\xc5\x13^\xf1\xff'\xbd\b쥴\x89\xd2z\xcc\xd1\xf1\x94\xf1J\xa6[\xd6\xcb\xea`9/\xb8uy\xaf\x98\xe1S^\xbcd\v\u007f\x9f\xf1r\xbd\xe2\xb3^xB\x05\xaf>\x84\xe9%۫\xe8\x95\xf2F%/#Oe/j$\xcf{\xb9\xc7G\x15\xfc\xf8\x02\x9b~\x11MW\xf5b\xa3s\x15\xf9\xcb\xda\xda+\x1cG\xb5\xec(\xab{Y\x17\xa8\xe1\xc5ҕ\x9a\xda\xf5W\xbdܓ\xbd\x96\x8a\xf05/\xdf\x11\x82\xe1\xd6\xf6\xaa\x97\xd5\x11\x1d\fʩ\xeb\xa5\xf7\xd5\xcbʹ>\x85\xd3\xc0\v\x0fo\xe8\xe5\x04K#/`\xaf\xb1\x97\xac\xa4\t\xa4\xd2ԛ\xdd\xc1\xc7\v\xca\xd7\\\xfb\xd3\u009b\x87\x97-\xbdp\xcf7T\xb9\xad\xbcba\xad\xbd(2\xb6\xf1\"\xe4\xb4\xf5\xd2C\xday\x99)\xb4\xf7\xd2H;xQ@\xee\xc8\x16:y\x89ڝi\x18]\xbcZw\xea\xea\xc5\xfc_7\x19ݠ\x9c\xee^\x92\xd8\x1e\xd9q\xbc\xe9\x15\xc7\xe9\xc9;\xf5\xa2\xc4\xdf\xd2\x0e\xe6z\xb9_Lo~\xdd\xc7+H\xd5\xd7+\x90\xde\x0f\u007f\xfb{\xc5\xdf\x06\xb0\x1f\x03\xbdB\xd5\ay\xc5c\aC=C\xbc\x882C\xbd\x98\xfe\x1e\xe6\xd574{\x05_\x1c\xbc\xc8\xe9\x15z\xea\xf2\"mq{\x89\xd3\x06\x84`zQ\x01\xb4x\xb5\xc7˒\xb8W\x85\xe1\xa3\xfc\xfd\xe8S\xc0\v\x12\x19T\x03\by\xa5\\\x1e\xf6\x02\xc1\"^\x84\xa8\xa8\x17Ō\x18m \xee\x15HHx%\x8e\xda^0\xbe\xa4\xba`\xca\xcb(\x95\xe6\xe5\x19X\xdd\b/+0#\xbdxz{\x94\x97\b4\xda\xcb\xc4l\x8cW2\xa4\xb1^бq\x1c\xcc\xf8\xac\x02&Ћ'\xaa\x95N\xf2\xa2\xa8;Ye;\xc5+\x81m\xaaW\xa9\xdd4\bn\xba\x97\x1cn\x06\\}&,r\x96\x97[\xdd\xcf\xf6\xb2\xa8<\xc7\v~7\x97\xc6>\x8f\xad\xce\xf72\xa4-Ț\xdcB\xaf\x84\xedE^\xc2\xefb/\xa8\xc5\x12/V(-\xf5\x82\x19-\x83\xb4\x97{I\xf3Vx%SYIo_\xe5%\xe1X\xad\b\xb1F\xfb\xb0\xd6K\x8e\xf8\xb6\x17\v\xfc\xdeQ\xe3}\x97@\xf5\x9eW\xe2\xf1:\x88x\xbd\x97I\xd2\xfb\x94\xfb\x06/\xa8\xd9\a\x94\xedF\xbdn\x13.\xf8\xd0+\xb1\xfa#/\x92\xb4\xcd^\x06\x8e\x8fUݟx\xb1\xb1\x9dz\xe1g^!W[\xbc \x8b\x9f\xab¾\xf0\x82\xe2o\xf5\xb2\xc6\xf4\xa5\x17\x95\x9dm\xc4ï\x88\x83_\xab \xbeQ\xd8\xfa\u058b\xca\xf0w\xfaq;\x9caG\x168vz\x91-\xecR\xb3ڭg}϶\xf6\xc0\xd2\xf7j\x8b?(h\xed\xf32~\xed\xa7B~\x84p\u007f\xf2\xa2f\xf3\xb3\x97S\x14\a(\xa7\x83\x14\xf2!\xda\xc7a/\x9fĤ\x84~\x85\x93\xfc\xe6\xe5Z\x82#^)\xf0\x1cew\x8e\xd1\xc1\x8f\xc3R~W\xcd\xfc\xe1E4;AG\xf8S\xbf=\xe9%+\xf8\v8\xff\xb7:\xe0)/\x99\xcei\xc2\xd1?^N휁\xfc\xcf\xea\x8f\xffzY\x8e<\xc7\xc1\xfe\xe7\x05\xcb<\xcf{_\xf0\xb2\xe8qы\xe0\t\x1b\xbb\f\x80\xbf\xe2EȽ\xea\x95t\xf6\x9a\x17ˢ\xae{%7\xbb\xe1թ\x97\x9b\x10\xc9-/\x88\xc5\xed\xac\xac\xef\xf0ѥ\xee\xf4\t \xe4\xf3!\x11\xce\xefC8,\xe0\xc3\xc0\v\xfaD;\x85|\xc2:\n\xfb\x10m\x8b\xf8\xf0\x82\x86\xa2>t\xb2\x98\x8f\xcbÊ\xfb \x8c\xbb|һ\x12>\b\xbb\xa4\x0fXt\xb7\x0f\x13\xfb\xf7\xf8\xf2\xa0\xf7^\x9fd\xac\xf7\xf9\xb0{\x8f\x0f\xd5\xf9\a||\x88\xc4\a\xc0{ȧ\xa5ڇ}\xb0\xf3G|\x88\xf4\x8f\xfa\x10\xbb\x1f㹏\xfb\xe0SO\xf8\xc8\x06\x9e\xf4\t\xa6\x94\xf2\x89˖\xf6!\xca\xe5\xf8\xb0q\x8fO4Z\xd6\a\xe6_\xce\aɔ\xe7\xe1)\x9f\x84\x89\xa7}\xcc\xea\x9e\xf11>=\xeb\x13}T\xf0!\xc1z\xce'H[\xd1\a\x8b\xa9\x04AT\xf6\xb1J\xfd\xbcO^1]\xc5ǂ\xe8\v>ݥ\xc0\xc7pP\xd5\a\x06\xfb\x92O\x96S\xbc\xec\xc3+\x99}\xc2\v\xab\xe1\xff\xea>ZJ\r\x9fl\x85Y\x93\xe2|\x15\xed\xd7\xf2Ѣ_\xf3х_\xe7@k\xe3\xc7:\xbe\"\xf5ܹ\x03r\x87\xf7˩\xeb\x83q\xd7\xf3q=e}\x1fK2\r\xd8׆>Up#ꨱH\xbe\x89\x0f\xf3\x86M}Hכ\xf9đ\x9a\xfb\xe0\xcd-0\xea\x96\x14\xd3\x1b>L\u07b7\xf21.\xb4\xa6\xc4\xdbh\x87\xda\xeaP\xdb\xf9Ġڳ{\x1d|\\%\xd2\xd1G\xd7\xe9\xe4\x03\x1aw\xf6\x11\u07ba\U0002cbbcY7\xfd\xb6;\xbf\xed\xa1Cx\xd3\xc7Ģ\xa7\x0f̾\x97\x8f\xf5\xf0\xb7|\x9cS\xcc\xd5[\xf4\x86F\xfb\xe8\xc9}y\xa3~>\xd2\xcb\xfe*\xbd\x01\xbe\x82Ͱ\x87\x0fMh\x10\x0f\x83E\nC|Ȩ\x87\xfa\xe8\xa4\xc3\xf4\xcaἫ\x83\x83u\xd2\x13\\>0\x027ni\xb0\xbb\xa6\x98\xfb\x90A9\x16\xe4\xe7\xf11\x16y}\x1a }(F\xf8}|\x9c<\xe0\xe3\xfctP\xc7\x18R\xe1\x85y\x9b\x88\x8fS Q\x1f7[\x8b\xf9\xf8\n\xf58|(\xe1c%\xc2\xf6q\xca \xe9c\xa5\xd5\a\xf2\x93\x86=e|Ą\x11\xf4̑>\x86\xb0Qڝ\xd1>\xc9-Ơ\xabc\xa1\xe3qt\xb2\xf1\x1c\xee\x04\xb5\u05c9>d;\x93|Ȣ'\xfbPt\x9a\xe2\x138\x99\x8a\xbeL\xf3aw\x9d\xe9>\xb0\x9e\x19>f\xd43}\\\x92;ˇ\x95\x01\xb3q\xea\x1c\x15\xc9\\\x1f\xd9\xfd<\x8eu\xbe\x0f\x14r\x81vx\xa1O(\xc4\"\xba\xe2b\xc2\xc4\x12\xd5\xc9R\xf5\xb1e>\t\xf0\xcb}\x98\xbd\xa4\xed\xacT\x03[\xa5\xee\xba\xdaG\x18]C;X\xabx\xf6\xb6O\xb8\xc8;4\xf8wU\xe8\xef\xa9P\xd6\xc1\xf1\xd7\xfbP\x1cx߇\xca熼v\xfb\xe5i\xf5\x03\fa#U\xbdI\xd1\xe5C\x9f.\xf8\xa1\xd06\xf3\xf010\xf3\x13\x1f\x8b͟\xfa\xb8\v\xdbg>\x99\xae\xd9\"\x96\xf6\xb9\x0f+~ho[}\xb2\xc0\xfaK\x1fc\xd16\x1f\x97\x8a\u007f\xe5C\xc1\xfek\xa0\xe17\x1c\xe2\xb7>\xbe\xe1\xf5;\x1fJ/\xdb}\x88\x17;t\xc0;9\xd0]\xe8\xe7n\x1f\xa7\xac\xbf'6\xeea\x03{}Hz~\xa0\x16\xf7\x11c\xf7C\x96?\xfa\xa4\xd0\xf7\x13\x87\xf7\xb3\xda\xd5\x01\x1f(\xdf\xc1,\xee\x1eR\xcf;\x8c{\xfc\xe2\x93\xc9\xf1_U\x86\xbf\xc1\x94\x8e\xe0\xefQb\xfc1\x9f\x86\x98\xe3\xaa\xf8\xdfiE\u007f\xb0\xff'|,\xb9\xfd\x89\xd6N\x02\xa2\xff\xc2\xff\u007f\xfbPW8\xa5f~Z\xbb\U000cf3f3\x16g\xd8˳>\x89s\xff\xfa\xc8\xc2\xcf\xf9P\b\xfb\x0f\xe0~\xde\xc7hy\xc1G\xc2tQ\x15|\t\xd0t\x99\xeerE\r\xee\xaa\xda\xec5\x9f\x96#\xaeSh7\x80\xc17\x15\x93n\xc16n\x03m\xef\xf0so\xb9;\xfd\f\xce\xf9\xfc\xe8P~\xbf\xb2\xc6\x02~^SЏ\x00U\xc8O^U\xd8/hQ\xc4O\xd7.\xea\x17\x0f,\xe6\a\f\x17\xf73\xd8\xdc\xe5\x87hJ\xf8E'%\xfd@\xf2\xbb\xfdx\xd4\xd2\x0fۺ\xd7O;\xbe\xcf/t\xe3~\xbfN\xa9<\xe0\x17;y\x10\u007f\x1f\xc2m\x1e\xf6c\xe5\xcf#~\x11\xe8\xa3~\xf1\xf0\xc7\xfc(\xbf?\xee\xd7\xdd\xedpޓ~:c)?\x89Si?\x02T\x8e_L\xb4\f\aV\x96\x87r~<\x91^\xde/\xaf;zJ\a\xf5\xb4_\x03\xca3\xec\xfa\xb3\xaa\xf9\xc9\xe9\xaa\xfb1\x19_Ï\t\xf6\x9a~n\xe4\xe3\a%\xaa\xc5\xc3kh\xf6\xf5\xbc1\xca\xd8j\xfb\xb1ַ\x8e\x1f\xcb\x06\xeb\xfa\xc5\xf1\xeaA@\xf5\xfd\xb2\u0c81_\x1c\xb7\xa1\x1fs*\x8dt\x80\x8d\xf5\u0604#j\x8a\xf3\x9b\xf1Cs?'T[\xf8\xe1Q-\xfd\x9a\xa5\xbeA\x81\xb5\x82\x92Z\xb3+m\xfc\x98-j\xcb+\xdb\xf9\x99\xed\xb6\xf7\v}ꀿ\x1d\xfd\x92\xdbt\xf2\xcb\"\xbf\xce<\xad\x8b\x9f\x13\x16]\xfdH\xaf\xba\xf9\xf1V\x83\xee~1\xf2\x1e~\xbceҏ\xb8\xde\xd3O\xe2\xd0\xcbϔ\x11\xea\xcb\xf5\xb3 \xde\x1b\r\xf7\xf1\x8bo\xf6\x85\xd6\xfb\xf9\xb5\xa0\xd2ߏe=ڛ\x81~\xe6+\x83\xd8\xe8`?ʐC\xfc@\x96\xa1\xec\xd20\x98\xe3p?\xd28\x87\x9f\xe0\xe3\xe4G\x97\x9fY\x94\xdb\x0f\xf2l\xf8\xe9\xf7\xa6\x1f\xe5*ˏ\xa0\xe9\xf1\x8b'y\xfd\xa8K\xf9h\xff~?\xd6\x0e\x05 ܠ_\x92\x8b\x90j9\xecǺ\x90\x88\x9f\x01)\x8aQ\xc4\xfc\xacP\xc4y\xa3\x04\x1b\xb3\xd9\xc3$NI\xf9\t\fi?\"X\xc6\x0f\xb4\x1b\xe1G>3\x92\xca\x18\xe5\x17<\x18\xed\a,\x8d\xf1\vc\x1e\xcb\x0e\x8dӁ\x8d\x87f&\xf8u/;Hk\x92Z\xc4d?+\x15S\xfc\x12S\xa6B\xd9Ө\xa8鰭\x19\x18\xe9L\x1a\xc3,?#\xfelmy\x8e\x9f\xb5蹐\xe7<\np\xbe\x9fa`\x01\xae_\b{\\\x04\xdb\\\xccV\x96\xf8\x19q\x96BRˠ\xcc\xe5\x18\xc5\n\xb4\xb3\x12\x17\xae\xf2\x13\xa8W\xfb\x11\xca\xd6\xf8\xb9\x88y-\x8d\xe3m8\xc5;~V\xfa\xdf\xf5K\x9d\xe0=Jp\x1d~Z\xef\xe7b\x93\xf7\xe9(\x1b0\xbe\x0f\xe8f\x1b\xe9ߛ\xfcX[\xf3!:\xf0\x11\xfen\xf6#\xf5\xfd\x98\xee\xf3\x89\x1f\v\xef>\x05\xca}\xe6\x97\t\xa3-~\xacx\xfc\x1c]\xfc\u008f\xa7\x93\xb6\xe2\x97/!\xfam~Dܯ\xfc\xc8Y\xbe\x86<\xbfQY\u007f\xab\n\xff\xce\xcfh\xbaݏ2\xe7\x0e\x1ev\xfau\xbeq\x97\x1f\xf9\xc4n?\xf7w\xf5#\xde\xed\x01\x9a\xee\xf5\xb3\xa0\xf0\x03\x8dd\x9f\x9f\v\a\xf6\xfb\xf1>\x10~\xf9\x13\x06\xf23\xdc\xe5\x80_Ҥ\x83~L\x97\x1c\x02\xb2\x1e\x86\xa8~\x81\x8c~\xa5\x05\xfd\x06o=\x82\x9f\x8f\xfae\"\xf8\x98\x1a\xeaq`\xd7\xef~\xcd\xfc\xffЎ\x9f\xa0\\\xfeT\xdb9I\x8f\xfe\x8b\xf7\xff\x9b\x87S~\x14XO\xfb%M\xfc\a\xb29C\xb3<\v%\xff\x9bm\xf4\x9c\x9f\xd4\xfb?^w\x9e']\x80\xcc/Ҫ/\xe1\xf2\xcb<\xe1\x8a\x1ft\xea*e\u007f\xcd/L\xeb:\xe4|\x83\x1e\u007f\x93\xeeqKm\xe36\xac\xff\x8e\x80\xd8ם\x01<\t\x95/@֝? ?\x15\bH\xb9\xa4`\x80n_( \x8eW8\x007/\x12\x80\x1a\x8b\x06(\xe7b\x011\xa2\xe2\x01\xed\xf9]\x01\xd1{\x89\x00\xebt%\xb5\x81\xbb\x03x\xcfr\x00\x94\xf2^\xb4v_@\xc0\xe0\xfe\x00\xc4\xf4@\x00\x1d\u007f0\xc0\xcd\"\x1f\n\x90,=\x1c@6\xfcH@\x84\xf3h\x00C}\x8c\x87\xc7ٙ'\x02|\a\xef\x93\x01\xb2\x95R\x011\x89\xd2\x01\x1aUN\x80%\x8b2\x01Qfـ\x18A\xb9\x80е\xf2\x01,\xcbx*\x00\xe7{: \xa2z\x06\xe2x6@*Z\x017}\x0eҨ\x18\x10[\xaa\x14@\xd6V9\x00Y?\x1fИP%@6\xf3B\x00\xef\xbd\xca\n\xa2j\x00\x8e\xf7R\x00U\xaf\x97\x03\xe4m\xaf\x04Dk\xd5Г\xea\xb8C\r\\V3\xa0\xc5R\x8e\xae\x16/~-\x80\xc4\xedu\x9cX;\x00$\xa9\xc3C]\x88\xaf^\x80+;\xeb\a\x00\x00\r\x02\xb0݆8\xbfQ\x80Yz\xe3\x00\xd6\xd8\x04\x88\x9aM\x03x*\xb8\x19\xef\xd3\x1cg\xb6\x80BZB0o\xe0\x9bV\x01F\xef\xd6\xec}\x9b\x00\xbc\xb1m@\x96\x03\xb6\xa3\xcc\xdaCf\x1d\x02ʭ:\x060/\xd6\t\x97wfw\xba\x04\x98\xb2u\rH\xad\xab[@\xb7\xdf\t\x10/{\x04h\xe3o\x06\xf0v\x9d\x9e\xe8D/^\xf9V\x80v\x9a\x8b;\xf6\x0e\xc0x\xfb\x04\x88^}\xa9\x87~\x10b\xff\x00*K\x03p߁\x01<\xb5?\x88\xbd\x1e\x9cUŐ\x00\xb7\xdf\t\x90\xb8\x0fÝ\x86\a\xc8R\x1d\x1c\x8eS\x1bwi\xef\xdc\x01\x01\x03#\x00l4\x03xB\xda\n\xc8\xda!O\x00\xcbK\xbc\x01\xad;\xfa`6\xfe\xec\xc7@\x80\x99]\x10\xdd\v\x05\xb0\x8a+\x1c`98\x12@\x95,\x1a\xd0\xe5\xc115\xf88\xbc&\x11\x00յ)\xcad\x80\xd41\xc5k\xd2j\x97\x99\x00\xd6\x14\x8e\xe0\xb7#\xd5\xd2G\xa1k\xa3\x03p\xf61*\xe9\xb1\x01\xd2\xd5q\x01\xe6\x88\xe3\x03\x12\xe5&\x04\x10\xfc'\xb2\x81I4\x84Ɂ\xec\f\xfc\x14~?5@\xe60Mۚ\xfe\xff\x13f\x04t\nq\xa6\xaawVVγ\x03\xa8\xda\xce\t\xf0\xe1\xe2\xb9\x01y\x82j^\x00\xb5\xa5\xf9\x01A\xdd\x05pυ\x012\xe5E0\xfdŰ\xbb%\x01\xb2\x82\xa5\x01\x94,\x97\x05\xb0\x16d9\x95\xb3\"\x00\xc8[\x89\x06Wa\xa8\xab\x81(k \xe4\xb5\x01\xc1ӷ\xb3\x1ax\x87#x\x97\x87\xf7t \xeb\x02\x9c\x12Y\x8fK\xde\x0f\xb0\xb6\xbfAE\xf3\x01\x9a\xd8\x18`\xbdu\x13T\xfa!\xc4\xf5\x11;\xb4\xf9\xff\xe3\xff8\xa0\xef\x00a\xdf>\r\xf0\x05t\x9f\x05Hm\xb7d\xa5\xf1y\x00\xc9\xce\x17\x01R\xaf\xad\x01L\x12\u007f\xa9\xc2٦\xba\xf9*\xdb\xeb\xaf9\xe2oxڷ\xda\xdaw\xb4\x85\xed\xb0\xc6\x1d\x01\xe4\r;\x03\xe0\x9d\xbb\x02x]\xd9\ue03e\x19\x92?\xee\t\xb0h\xb27\x80-\xcf\xf5\xc7}j\xfa\xfby\xe9\x8f\x01n]\x17`\xe4\xfc\x19\x83?\x10`)\xf8 \x8d\xe8P\x80\xe1\xee\xb0\xf6\xe4\x17\xb5\xb4_Ք\u007f\xd3㑀&GG\x01\b\xc7\x02B\x02\x8e\aP\xb1\xf8];\xf3\x87\xba\xfa\x89\x00\x8b$\u007f\xea\xb5'U\xfa\u007f\xe1ҿ٫S\xfa\xe3逐\xe1\u007f\x88\xb6g\x02,\xbb\x9d\r0\xfb\xffW\x03̹\x80\xa6\xb0\xff\x05XH9\x1f`fx\x81ҿ\x18\xe0\xeek\x97\xb4\xd1\xcb\x01d\xb0W\xf4\xe3U\x8e\xf5\x1a\x0f\xd7\x03R\xbc\xbb\xa1\xa1\xe4&\f\xe5V\x80\xbb\xf00\xe8\xdd\x11d\xed\xe8\xce .\xc8\x17,\xdc\xd14\xacގ\x9c\xfcA\t\xb9\x05\x82\x82B\x05\x83\xf4\x8bBA\xa1\r\x85\x83d\xfaE\xf0[\xd1 q\xbeX\x90FT<\x88\xa7\xfa\xee\n\x8a5\x97\b\xd2/J\x06\xc9J\xef\x0eJ\"pO\x90%\x97{\xb5\xd9\xfb\x82X\u007fx\u007f\x10+W\x83\x12\x99\x1e\f2\xf3{((r|8ȉ\xbcG\x82\x84\xfcG\x83:\xd1\xf8X\x10|\xff\xf1\xa0\xb0\xd7'8\x84'\x83\xf9[\xe5\xe6\x94\nb\x94\xa5\x83\x12Tr\x82ble\x82\xb0\xbc\xb2A\x94\x99\xca\x05\x81:\xe5\x83\xfa~\xe5\xa0h\xf9\xe9\xa0\x04\xc7g\x82R4\u007f6\x88Փ\x15\x82\xa8\v=\x17\x94yЊA\tv\x95p\xbb\xcaA\x9a\xd3\xf3A!\x16U\x82RK}\x01\u05ff\x18\xc4#TU\x83\xc8\"_\n\xf2\xf9Ƞ\xac\xe4{\x85\xdfU\v\xc2j\xab\aQ\xc0\xaf\x11\x94\x82jͼ\xef\xfa\xf9s^Ց\xd7\n\xaa\x1b\xbd\x16\xa4\xb9\xbe\x9e7h\x875\xa4_N\xed q\xb3N\x10\xa5κA\xa0_\xbd\xa0\x18W}\x0e\xbbA\x10\xf0\xd20\b\xc0h\x14\xd4\xfc\x10\xa2m\x92m\xb8i\x10롚\x05i_̓\xc4\xc5\x16A$*-\x83\xacƾA}\xb6\nʞ\x15\xad\x83R\xa3n\x13\xd4D\xba-%\xde.Hgh\x0f\x83\xe8\x00Iu\f\xb2\xf2\xda\t\xb2\xec\x1cTj\xd1%\b@\xed\x1aĬa7t\xa8;\xb4:(\xa7G\x90\x05\x837\x83:\xc3\xd8\x13\xd7\xf6\x82L\xdf\n\x82 \xe7\x06%\xac\xf4\x0e\xcaR\x94>\xd0j\xdf Y^\xbf\xa0\xb8|\xff Aw\x80Jq`\x10Ӫ\x83\xb4\x87\x83i/C\x82\b\x11C\x83\nl\xc3Ԣ\x87S-\x0e\x0e\xcb\x19\x04\x93s\xa9\x95\xba\x83\xba\xc3k\x90+\x85M\xf4\xce\n\"\x97\xf7\x04\xc1a\xbcA\xa6/\xbe \x9c\xcb\x1f$[\v\xe4}\x9d;\xc0\x91\x13T\xc3\x0fq0\xe1`\xbe\xd69\x91 \x01-\xaa\xb7\x89i\xcf\xe3A\xa1\x18\t\n\xca\xce\xeb\x8al\"\x00y\xa5\xf07\x8d\xbf\x19X\xd5\bj}d\x90+\xa3F\x05\xc5\xcfGkCc\xd41\xc7\xc2\x15\xc6\xf1\xd4\xf1Y\x19O\b\x127&\x06Q-\x9e\x14\xe4L\xcf\xe4\xa00\x8a)A)gL\x85Z\xa7\x05Q\x8a\x9a\xae\xa2\x9a\x11\x04\x1e΄rf\x051]1\x1bw\x98\x13$]\x98\xab\x03\x9b\xa7\x864\x9fW,\br_\xbd\x85\xaa\xa9E\n\r\x8b!\xce%\xd0\xe2R\xdcw\x19\x1ck9\xbdvE\x10U֕\xb4\xf9UA\x84\xe0\xd5*\xb35Als\xb2VG\xfcv\x90\x99\xe7;\xea(\xef҄\xdf\vj%a\x9d\xfa\xc3\xfa !\xf8}\x85\xb0\rYX\xf9\x000\xb5\x91ְ\x89\xd6\xf0a\x10\x19\xe6GA\xce&o\x86\x80>\x0eJ@\xfdD\xe1\xe9S\xed\xceg\x10ؖ p\xfe\xf3\xa0\xf2\xc9/\xd4#\xb6\x06\x91w\u007fI$ئ\xe2\xf8*\xa8\xb0\xffu\x90[;|\x13d\xe1\xf6[\xde\xfe\xbb \x03\xcfv\x8acG\x10\xd3u;\x83\xa0\xb1\xbb\x82\f{\xbbU\xe4\xdf\a\x11\xc8\xf7\x04\x11\xe8\xf7\xe6\x8dcА\x9c\x1f\x82\x98\xf6\xddG\xdb\xdb\x1f\x94\xcc\xeb\xc7 \x93\xf8\x9f8\xb8\x9f\xd5^\x0fК\x0f\x061\x83t\x88\xd2=\x9c5\x99_\bo\xbf\x06\xb1\xf2\xf97\xbd\xe7\x11\x1d\xfc\xd1 \xe9\xc0\xb1 \x03\xf4\xf1 wG\xfc\x1d\xf1\xe4\x0f\xf8\xec\t\xd8\xee\x9fA\x12ӓA\xc4\xf9\xbf\x14\x06\xfe\xa6\x85\x9e\xe2\xb8O\a\xb52\xfb\x8f\xc2\xe0\x99 JigU\xd7\xff\x06奰\xe7\xd0\xec\u007fA\xb0\xea\xf3h\xfd\x02@\xfab\x90sޗ覗\x83\x8c\x84W\x82 \xb1W\xb3B\xbf\x16\xc4^\x00׃d67\x82`.7\xe1G\xb7\x82\xa8%\xdeΞ{GH\xcc\xf2\xce\x10\xb6\xad\v\xe1\t\xff\xfc!\x89R\x05Bb5\x05C,h\x14\n\x01\xe9\n\x87duf\x91\x10-\xadh\x88\x02.\x16\x12\v)\x1e\x02r\xdf\x15\x12\xbaX\"\x04\xf5\x94\f\xc1\x95\xee\x0e\x89y\xdd\x13b&~o\b\xeb4\xef\vQ\xb4\xf7\x87\n\xb7\xcf5\xe4\xbd\xfd\x0f\xf0\x87\aC\x12\xe6\x1f\n\x91I<\x1c\x92\x84=7\xe7\x91\x105\xf2hH\xbc鱐\x94\n\x1e\x0fI\x98{\"D\xa4}2DvX*\x04w*\x1dby9'$N]&D\t\x97\r1^\x86`L\xe5Cxf\xe4\xa9\x10\xc9\xde\xd3!\x06\x8fgBj\xe5φ\xe0k\x15B \xfeυ\x90mTԞT\n\x89\rT\x0e\xc1\xaf\x9e\xe7oUB\xb0\xd5\x17B\xd0\xc0\x8b\xa1\x82Ms\xcdܜ\xaa\xfc\xf8R\b8\xfarHP\xe1\x15\\]\r\x92\xa9\x1e\xe2{C0\xb4\x9a:\x9cWCȻj\x85\x84\x92\xbf\x16Ұ\x19\xa2}\xd6\x0e1_\xad\x83Kꆈs\xf5B\xc0\xd7\xfa\xecD\x83\x10l\xab!\x87\xda($\xeao\x1c\x82k4\t\xa1N\xd34D\xa8h\x16\u009eR\xcdCR*h\x01ѶTݾ\x01ݶb\x8b\xadu\x04\x18\xe8\x10\xc2\n\x90\x8e\xfam\xa7P^\x1c\x19\x9c\x97v\x86\xe8\"]\xf4\xf2\xae\x18|7\xfc\xed\xae\xfa\xe9\x11\x022\xbe\x19b\x82\xdb\x13\xbd\xee\xc5q\xbd\xa5B\xc8\r\x15\xc9\x13\xf9pCV\xb0\xaa\x1c\xfap\xac}U<\xfd8\xcc\xfe!\r\xd4\x03BB'\x06\xeaɃBD\xd2\xc1jbC\xf2\xfa%ǡ!\xbca\vF5<\x04hvh?\x9clХ#p\x87$32 )3\x84\x8dw8rO\x88\xe5`o\bUO\x9f~\xf4\x87\x84O\x05B\xdc\xf65\x84J^(D\xdc\x0e\x87\xf0\u0590\x90\x94\xb8\xa2!,\xa5\x8cQ\xdcq\x1dMB\xe47\xac_\x8e\x1d\x12(H\x86P\x19K\x85\x04\xac\xd3!\x04\xbaL\x88\xb5\x84\x11!T\aG\x86P\x8d\x1b\x85\xf3G\x87\xc0\xb4ǰͱ\x18\xe3\xb8\x10\xe3f\xa8h+kH\xee\xb0A\x86#gB(\u007f;G\xceĐp\xe2I8i2\xcdu\n\xccujHXݴ\x90\xd4֦\x03\x05f\xa8;\xcfT-\xcf\xc2E\xb3\xe1\xfesB\xe4\xa1s\xf1ݼ\x90\xe4:\xf3\xf1˂\x10\x91\u007fa\x88\x1cf\x11F\xbf8\x84pI!/\xd5\x06\x97\x85\x10\xa3\x97\x87\xb2[ج\b\xa1ܴ\x92#Y\xc5A\xae\x86\x81\xae\xe1wk\xf3\xb0\xc4\x1ab\rs\xe4\xbc\x1d\xd2W\xb6\xbc\xa3b~\x170\xf0^\xf6\xebu!<{\xb9>\xc4*\xd9\xfb\xe8\xe8\x06v\xe0\x03X\xe4F\x8c{\x13\xfa\xfe!e\xf8\x11;\xb09\xa4)\xd5\xc7\xec\xe1'!V\x98>\r\x91}|F\x1c\xdd\x12*\x94\x87\x99yv\xffy\b\x9b\x13}\xc1\xc3\xd6\x10\xa3ԗj\x85ۨ\xb3\xafB\\U\xf3u\b\xb1\xf0\x1bXط\xda\xf2w\x00\xe3\xed\xea#;\x885;C2װ\v=\xdfM\xd3\xfa\x1e\x83\xdc\x03[\xda\x1bB\xfd\xfd\x87\x90И}!\x94\x80\xf7\x87\x10\n\u007f\xd4.\xfc\x14B\xc0\xfd\xf9\xffR;\x10\xc2\xcb)\x0e\xfe_\xe4\x87\xf4\xcc\xc30\xf5_8\x82_)\xf9\xdfBH\x85\x8f\xa8\xed\x1d\r\xe9\x9c\xd81\x18\xc9q\x9e\xfb;\x0f\u007f\x84P\x13:\x818\xf3gV\t'\xb3\x97\xfc\x15\"\xd3\xfc\x1bpv*\xc4e\x83\xa7\xa9\xd4\u007fT\bgT\x93g\xd9ֿ!>\xeet.\x84\xc8\xfd\x1f\xe4p>\x04\x96{\x01>y\x91\xbf\\Rc\xbcL\x8c\xb8\x82\xa1\\\r1?\xb8\x16B\xb5\xe8::v#\xc4(\u007f3\xa4\xe4\xe0\x16\u007f\xbd\x1dB\xdd\ue3b0\x98ڝaT\xcb\xf3\x85i\xa5\xf9\xc3X_U \f\xd3)\x18\xa6y\x17\n\vn\x15\x0e\v-)\x12\xc6\x14}\xd10j\xa8\xc5\xc2(~\x15\xe7\xe1\xae04P\",Y^ɰ\xf8\xc2\xdda\xechvO\x18\xfb3\xdd\x1bF\x15\xe1\xbe0\xe8\xd7\xfda\xa4\xe0\x0f\x84I\xdb\x1e\fc\xde1\xcc\xc7<\u0080\x82G\xb4\a\x8f\x86Y\x8eE\x9f\x1f\x0f\xf3\x95\xcca\xb0\xa5'ٓRa\xc6\xc902\xa4\x9c\xb0\xb8g\x990\x17\xe2\x96\r#\x14\x97\v\xc3\xeeˇ!\x86\xa7\u0088\xe6O\x87\v\xb6\x184\xf8\x18\x86\xe5\xbe\x12\x06g\xaa\x16\x16\xec\xae\x1ef~Z\x83_\xd6\f3}y5\f\x8c\xaa\x15ֵ\xabh\xee\xf50bH\xed0\xa3[\x9d0\x82|\xdd0\xb1\xbb^\x98\xef\xd6\nklh\x10\xa6Y5\fK\x11\xbaQX\xabg\x8dÀ\xd9&aA\x81\xa6a1\xe9f\x94~\xf30&\xc6Z\x84\vv\x96`\xd82L\xaf\u007f\x83Rm\x15\xe6\xb6\x19\xad9\xa66T\\[v\xbb\x9d\x8a\xa4}\x18\xc9j\x870\xf0\xa4c\x18\xeb\xba:\x855\x11\xed\x1c\x16o\xef\x02Qv\r\x13\xa0\xba\xe9\xb1{\x18\x13v=\xd8\xfa\x9ba\xa9\xda\xf5\fӖ{\x85\x99\x9b\xbc\x15ƴ_n\x18!\xacw\x98\xf9C\x9f0\vD}u\xbc\xfd \xab\xfey\xe7\xe6\xc9z@\x98p<0̂\xf5\xa00}f0mk\bu6\x94w\x1d\xa6\xf7\x19\x1e\x16\x8cr\x841k\xe0\fc5\x89K\x1br\x87%\x8a\x1aaup3\xacي\x15\x06\xab\xf5p\xc8^Z\x8d/L`\xf3\x87\xb9\xe06\x10Fx\n\x86\t\x89!Ud8,\xb8\x1c\tg_CI߉Q+\xf10\xe7$\x12\xda\x01;L\x96\x96\xa4\xf7\xa5\u0098\xfeI\xab\x183a\xe0ሰ0\x9e\x91\xfc0\x8a\xa3\x1b\xad\xe6:\x06\x06:\x16R\x1a\x17f\x96<>\x8c\xf2\xf7\x840\xa6\x9d'\x861\xc71\t\xa7LV3\x98\x02\xe5M\xe5\xc0\xa6\x85A\xec\xa6g\x8djF\x98i\xd0Lx嬼\xd6\x1cCrf\xe7\xf9S\xae\xd1/gNXww\r#\xaa\xcc\v㉸\xf9a\xc1\xb5\x05\x18\xfa°\x02\xe7\"\xb5\xa3Ū\x89%\xea\x0eKØ\x9d\xc4\xdf\xe5a֮W\xe0\xd3J\x8aaU\x98\x94b\xb5\x1a\xc1\x9a0\x1f\xd9Z\x1b\xe6\x16\xaf8\xf7\x1d\xdaػ\xbc\xe4\xbd0\xd3\xfcuanS\xb4>̹\xf7\xf7ì\x86m\bca\xf2\aa\x04\xbd\x8dD\x8cM4\x8a\x0f\xc3Xj\xf3\x11\x9a\xdd\x1cֵ\xact\xefO\xc2\\U\xf2\xa9z\xf6g\xaa\xed-8\xf9\xf30_E\x19\xc6D\xeaV\x15͗a,\x8c\xd9\x16F\x01\xe1\xab06\xe7\x01\xd8|\x13f>\xfamVD\xdf\x11\x9d\xb6\x87\x91\xf5\xed\bc\xd9\xf3\xce0\xd8\xff.\xa0\xf3nm\xf4\xfb0\xa8\xdb\x1e\x88x/l\xfa\x87p!\xa1}Cs\xf6\x855\xd4\xef\x0f\x17\xceS\x92\xec\xba\xf9#\xce\xf8)k\xd0?\xab\xed\x1e\b\vO?\xa8r=\x14\xe6\xea\x8e\xc3:\xa8_0\xa8_\xc3$\x9c\xbfe\xb1\xe7HX\xe2\xd4Q\xf6\xeaX\x98y\xef\xf10k\b\xbf\xd3\x1e\xff\b\xa3\xb4w\x02\x0e\xf5'm\xfed\x98\xdc\xe1\xaf0#\xe5\xdfj֧\xc2L\x10O\xebm\xff\xa1Z\xcf\xc0B\xcf\xf2ÿ\x18\xfc\xb90\xd2\xde\xff\xf4\x9e\xe7y\xaf\va2\xe4\x8b*\x9aKz\x9f\xcb\f\x1cW\x88\x06W\xe15\u05c8a\xd7i\xe97x\xc6ͬs\xde\n3q\xbcM\xf0\xbd#\x82R՝\x11}7e\x84\xed珈,\vD\n\xb6\xca\xf5\xe6\xe6\x14\x8c\x14n\xdd\xd7!\xb4\xbeP\x84\xe3)\x1cAf[$B\x16^4\x02\x9d\x16\x8b\x88ދGh\u007fwE\x84픈\xd0\xfdKF\x84\xee\xdc\x1d\xc1J\xa2{\"\x8c\xd1\xf7F\xa8\xa3\xfb\"X\xb9u\u007f\x84^\xf3@$\xbb\xb2\xe5\xc1\bz\xff\x10{\xf9pD\x9f\xe5\u007f$\"\x10\xf0h\x84\x98\xf8X\x04n\xf1xD\x84\xf9\x04\xafx2Bh*\x15\x01\xb1*\x1d\x91\xbabND \xa0L\x04;藍0\xd9)\x17\xa1e\x94\x8fH\x9c}J\xaf{Z\x05\xf1\f\xdb~6\x92\x87=nGN\x85\b|깈\x10\xf8\x8a\x11\x82F\xa5\x88\xd0\xee\xca\x11\xc1\x83\xe7#\x88\xcfU\"P\xc8\v\xe8\xe7\x8b\xdaV\xd5\b\xc0\xfa\xa5\x88&g/G\xe8\xf3\xafD\xb0\x14\xbbZ\x04\n\xab\x1e\x11s\xaf\x11\x91\xc0V3\xc2J\xc1\xab\x11\xaa\xbfVD(\xd2k\x11\x06\x8e\u05f5\xdd\xda\x11\x01\xad:\x11V\xad\xebF\x00\x85\xf5\"p\x9e\xfaY\x915\x88\x10\x1d\x1aBG\x8d\"X\x8b\xde8\x82U\xefMT\x1dM#Z\x05i\x16\xc1\xc3\x0f\xcdE\xcb\xc3rZ`\x14-\xf3t%\x0fǾ\x11\x01(\xb7\x8a`c\xae\xd6ڗ6\x11\x84\x8f\xb6\x11&\xec\xed\"\\o\xd6>\x02\xdaׁC\xeb\xa8]\xec\x14\x01\xf7\xec\x1c\x01\x9ev\x89\x88\xddwe\u007f\xbbEX+\xe8\x1e!\b\xf5\x88`\x93\xe87\xd5Pz\xca]\xf3\x02jDW\xbcF\x80\x1e\xb9\xecRo\xf6\xa1O\x04+\x05\xfbF\x98<\xf6C\xef\xfbg\x876 \x92\xbf\xe3\xf0\x9c\x81\x11\xa9\x1e\x0f\x8ah%n0\xaf\x1c\x82S\x87Fd\xeet\x98\xdeqxD#\xa5#B\x97vF\xc0\xae]\x11z\xb4;\x02\x1eaD\x18\xea͈\x10eK\xfb灰\xbd\x11,\xae\xf4Q1~|\x17\x88\xb0\f\x15\x84\xa3\x85\xa0\xc1p\x04S\xb2\x115\x8bh\x04\xb48\x16\x91\nm<\x026\x9b\xd0A\xd9\x11\x81\xadd\x84u\xa6\x14L/\r\x93\xc9\xd0\xc8FD\x80\x1b#\xf5\xfcQ\x11\xa50\xa3#`\xcccp۱\x8a\x90\x98o\x8e\x80H~L\xc7\xff$\x82$\xe1\xd3\b(\xd0g\xda\xff-\x11\x90\xe2ϩ\x9b/\x80W[U\f_F\x84\xe5n\x8b\xe8\xf6v\x90\xdb\u05fc\xd37\x11\x8dJߪ<\xbe\x8b\x80\x02oG\x03;\"\xa4\xcf;#\xa4\x1a\xbb`_\xbb#d=\xdfG\x00\xf3{\x14\x85\xf7*d\xfe@\xab\xdd\x17\x11\x9a\xba\x9fN\xf5c\x04D\xf1\xa7\by\xf2\xcf\xf4\x89\x03\x11\xf0Ѓ\xea燲\xeep\x98\n\xfe%\x02\xbe\xf8\xab\x9a\xffo\x11\x92\xbc#\x1a'\x8eFH\xf6\x8eEH \x8f\xd3S\u007f\xc7\xf0\xfe\xa0C\x9c \xc4\xfe\t͝\x8cH\b\xfb\v\xff\xff\r#=\x15!\r9\x1da҉/\xcf\xc0\xfe\xcfF\xc0!\xfeU\xac8\x17QN\xf0_\x04l\xe4|ֽ/\xd0\x0e.F\x18|/Eȭ.+$^\x89\x90\xb4]eG\xaeE\xc0\x8d\xaek0\xba\xa1Fp3B\x96t\x8b\x16w\x1b\xdeqGTw\xe6\x89\n\xd7\xc9\x17\x15\xaa\x91?\xaa\xaeQ J\b)\x18\xe5}\vEa\xff\x85\xa32\xc4\"Q\x06\xf8\xa2Ql\x02\x1b\x85\x89\x16\x8fB\xe6wE\xc5%JD\xc13KF\xc9M\xee\x8e2\xe4\xdf\x13\xa51\xdd\x1b\x05Q\xb8O{p\u007fT7\x1a\x88\nsy0*\xbd{H/y8\n\x1e\xf3H\x94A\xfe\xd1(\xa2\xf5cQ1\x9fǣ\x12\x19\x9f\x882\xec=\x19eH+\x15հQ:\x8a\xf8\x99\x13%\n\x94\x89\nҔ\x8d\x8a\x89\x95\xd3ᕏ\x02\xb6\x9e\x8a2V=\x1dE4{&\xcaկQ\xc2F\x85(\xe2\xe2sQ\x04\x9c\x8aQ\x84\x9fJQ8B\xe5(1\xff\xf9(\xc3C\x95(\x83\xc7\vQ\xb5\xb5\x17\xa3DĪQ@\xefK:ԗ\xa34\xbaW\xa2ĜjQ\xa4\xe4\xd5\xd9j\x8d(=\xba&\xbf}5\x8aU\xb6\xb5\xf8\xe9\xb5h\x91\x16\xb9\xb9C\xfa\xb9\xf3\x92Ҩ v\xed(K\xe7u\xa2\xa8\xf2ԍ\xf2\x8d#\xdaF}\x1dE\x03=6\x8cr\x01v\xa3(xI\xe3('\x99\x9aD\x11\xac\x9aF\xc1\x8d\x9bEu\xb3\x98\xe6Pq\x8b(\x1f\xd1k\xa9m\xbcA\x01\xb5\xe2ɭ\xa1\xb06\xdaP\xdb()};=\xb6\xd7~t\xd0\xcf\x1d\xa3\x98o\xec\xc4^v\x8e\xc2\x15\xbb@\x9f]\xa3B\x11\xbaEe\x06\xb5{T8W\x8f(\x90\xe9ͨ$\xcc=\xa3\x98\v\xee\x85_ޢ]\xe4j뽣Rc\xec\x13\x05}\xec\v+\xed\x17\xa5\xdf\xf6G\xdb\x03\xa2\b\xd8\x03\xa3H\x9e\x06E9\xe378\n\xe4\x1f\xc2\xeb\x86F\tGâؕ'\n\x04sD\xe5Ih':\xe5\x8ab\xf3 w\x14\xd0h\xc0\xd4ͨ@\xb1\x15\x95՝\x9e(\xdcϋ\xce\xf8p\x89\x1fV\x17\x88r!J0\no\x0eE\x85\x83\x85\xa3\x82\x14\x11\xb5\x98h\x94\xf0\x13c\x1bq\xfc\x98\xd01ؐLR{\x9d\xa2\xd0Ҹs\x06'\x8e\xc0ߑQnv0*\xca\xd2\xc5\xe8(&f\xc6D\x95@\x8c\x8d\xca\xea\xd3qQL\xaf\x8dד&D\x11V'\xf20)\x8a@1\x19C\x98B\x0f\x9a\xaagN\x83\xe4\xa7g[\x9b\x11\xe5f\xfc3y֬(\xc1n6\n\x02\xf7\xbe:\xf1\x06\xca\xe0\x03\xfd\xb81\xca0\xb7\x89V\xff\xa1\xb6\xf8Q\x14{\x01o\xce\xde\xf7cřOp\xdfO\xa3,\xfb}\x86\x8en\x89\xea\x9a\xd6\xcfՓ\xbe\xe0-\xb6F\x19\u05ff\x8c\xa28\xb1\x8d\x87\xaf\xa2\xc8w\xbf\x86\xe1~C?\xfd6\x8a\x1d\t\xbeCsۣ\xf2\x80\xc7\x0e\xfc\xbf\x13V\xb0K\x85\xbf\x9b\xd6\xf3=mwOT\xca\\{1\xc6\x1f\b-\xfb\xb2\b\xbf\x1f\xdf\xfe\x88\xbf?\xc1\xcb\u007f\x8e\x92\x00\x1f \x92\x1f\xe4\xe1P\x94a\xf0pT\xa8\xe5/\xda\xe1_\xb3\xf8\xf6\x1b\x14|\x84=>\x1aEP;\x16E0:\x1e\x05=\xf9\x9d\x83\xfa\x83\x06qB\x91\xf7\xcf(\x83\xf2\xc9(*g\u007fE9\xa7\xfbw\x94\x11\xebT\x14\x91\xea4\xfb\xf0\x0f\x91\xedLT8\xd4Y\xfc\xfd\x970r.*ϭ\xfc\xa7\xf8}\x1e\x12\xb9\xa0\xfa\xba\x88\xd1_\"\xd0\\\xa6\xff_\xa1\x18\xaf\xeaP\xaf\xe1\xf2\xebT\xff\r\\}\x93\xb7\xbc\xa5\xfd\xbb\x1d\x05\xff\xbb#\x86u\"w\xc60i\x97/\xc6gDbXE[ \x06\\*\x18\xc3\xf4L\xa1\x18^\xf9\x1cC\x84+\xc2CQ\x1e\x8a\xc5\xc0R\x8a\xf3pWL\x1e\xb0/\x11\x93ղ\xb99%cL\xee\xefƷ\xf7İ\x01ֽ1M\xb7\xee\x8b\xc1\xf4\ue3f1d\xf4@LB\xf6\x831\x1a\xe2C1\x82\xcb\xc31L\xc0=\x12C<~4&+@\x1e\x8b\x89u<\x1e\xe3\x9e<1\x01\xac'c\x98L.\x15â\x9b\xd21\x81֜\x98ȴ\f\u007f)\xcbA\x96\x8b\x819\x96\x8fa\xa3\xf4\x18\xfd\xe6\xe9\x98<5\xfdL\f3\xb1\xcf\xc6\xe0\x01\x158\xba\xe7b\x9cƮ\x18\x93\xd9\xdfJ<\xa52\xae~\x9e-V\x89aM\xe6\v1 ͋1\xe1\xfcUc\\\xac\xf5\x12\xc6\xfd2e\xf8\x8a\x8e\xb3ZL\x1e^\xae\x1e\x93\x84\xa7F\x8c\xeb\xc8jƸ\t,GY\x8b#{-\x86\xb2\xf3\xeb<Ԏ\xd1H먀\xea\xc6\xe0\r\xf5\xa8\xb5\xfa1T\xf0\x1b\xf0܆1\xb2\x87F1<<\xd88F?j\x12\xd3j}Sv\xb6YL\x16 6\x8f1ڷ\x88qҰ%\x1b~#\xc6Ǣ[\xc5X\xe0l\x8d\xb1\xb4\x89\xc9\xfa\xae\xb6Tv\xbb\x18\"L{v\xa1C\x8c\x16ڑ\x1a\xeb\x14S\xb2\xd79\x86\xfd\x04\xba\xc4\xf0\xb8O\xd7\x18yp7\xa8\xa8{LbF\x8f\x98\xb8ۛ1\xd4\xc5z\xc6@\xe3{ńV\xbe\x15#\vȍ1\xb9\xeb\x1d\x93\t\xcd!\xb9\xa6#\xa7\x0f\xef\xdaW\xa5\xda/\x86E\xb4zހ\x18\xa6r\aƊu0,w\xeep\xe1\x15\x83\xf8\xdd`=e\x88vwh\x8c\xe05\x8c?\x0f\u05ef\x1dzt\xea鮘8\x95;;(C\x15g\xea\xed-^\xed\xa1\x1a\xbc\x14\xa1\x8f\xda\xf0\xab$\x031Tꂰ\x9e\x90^\x18\xd6cD\xa5\x1f\xc58b\xf8\x1b\x8f14&`\xe2vL\xea!ɘ\xf8s\n\xff\xa7\xf1}F\xcf\x1a\xa1Ǒ1L\x93\x8d\u0089\xa3c\xb2D`\fn96\x86\x92ظ\x18\x9e\x91\x1d\x1f\xc3\fք\x98\xac$\x9a\b\rL\x8a1,M\x8e\xe9\xac\xfe\x14\xb415\x86\xe4kZ\x8c\x989=&\x04uF\x8c\xa9\xcf\xcc\x18cά\x98\xcc\xd2ώ\x91\xb9̉1\xfc͍!\xc1\x9a\x17\xc3L\xd0\xfc\x98,{]\x10S\x1a\xb60&\x91w\x11e\xb58\x06\xe8[\x12\x93\xf0\xb04\x06\xc8]\x16C\x9c\xb0\x1f\xa2\xfdQ\xa5\xf8\x93B\xc4\xcf1\x12\x8b\x031\x14\x86\x0eRև\x88\xa3\x87\xb3\xe6\xfe\v\x9b\xfd5\x86\n\xebo1Y\x88\u007f\x04=;\x1a\x93\x12ޱ\x98$Z\xc7a.\xbf\xc7\xf8\f%\x1c\xf8D\x8c,\xebO5\xef\x931\xae\x99\xfa+\xef\x82ܜ\xbfcB\xb2O\xa9(O\xc7d\xee\xff\x9f\x18\xc8\xec\x19\xed\xe7Yq\xacܜ\u007f\xd1\xf4\xb9\x98\xee\xf9\xfe_\f\x137\xe7a\xbc\x17\xb4͋\xec\xe4%\xb5\xd3\xcb1}\x1bݕ\xec?W\x01a\xd7b\xf9\x1a\xe5\\\x8f\xb1\n~#\xc6\x10}3\xcfGQ\x8d\x85\xa8nǰB玸\xd6h\xef\x8c\x13\x03\xf3\xc5q\x87\xfcq\x10\x83\x02qP\u0382q(\xb8\x10\xbf,\xccOE\xe2\x12\a\x8aƱ\x17L\xb18\x96\xfdċ\xb7\xee=ȟ\x97\xe5\xe4\x1aV\xce]q\xc0W\x898\xe7sK\xc6\xe5\x8d\xc0w\xc7I>\xef\x89K\xb8\xbd7\x8e2\xd5}q\xa1\xc9\xf7\xc7\xf9 \xc1\x03q\xb2\xe1\a\xe3\xe0\x12\x0f\xc5\xc5H\x1e\x8eÒ\x1f\x893[y4\x0eG\u007f,\xcet\xec\xf18\xe5\xfbD\x1c\xc0\xf0d\x9c\xf5\x84Rq\xd4xK\xc7\x01\xcf9q\xf2\xc22q\xe2QY\x0e\xae\\\x9c\xb5\x99\xf2q\x9a\xefS\x1c\xde\xd3q\xd9\x1a\xec\x998\xe8\xeb\xb3q)\xb7V\x88\x83\x0e?\x17\xc7\xc6G\x15\xe3\x98Y\xac\x14\xc73\x94\x95\xb3\x82|>oHy\xadV\x89\x8b)\xbe\x10\xc7r\xab\x17yjո\xac\x92\xc8K&\xf3Nq\xf8s^\x8e\x8b\x1f\xbe\x12\x17\x88\xa8\x16\x87\xb9W\x8fK\xd6Q#N`\xae\x19W\a{\x95\x82\xaf\x15\a\x17~-\xae\xd3\x1a\xaf\xc7\xe9a\xb5\xd1P\x9d8\x1f\x92\xa8\x1bG\xfc\xaf\x17\xa7\xda\xeb\xc7\x11s\x1a\xc4\xf9\xea\xca8\x17\xe55\x8a\xc3\x1b\x1b\xc7\xe9tM\xd8\xe9\xa6*\xc6f\xbcQ\xf3\xb8\xd0\xd5\x16q\xb1˖qI\xa7ވs^SE\xd5:\xce*H\x1b\f\xb6-\xfe\xb6\x8bK\xd4k\x1f\x87\x9bw\x88+\xc9\xeeH]v\x8aӎ;\xa3\xc7]ص\xaeqr\xc7nq>\v\xdc=.\xabYzP;o\xc6ɯzƕR\xf5\xa2\x81\xbe\x95'\x92\xbc\xa1\xe7Rn\xbd\xe3Bo\xfa\xc4\x05\xe2\xfb\xa2\xa7\xfd\xe2\xb4\xfe\xfe\xb4\xb0\x01q\x81Áq}\x96cP\x1c\t\xe1\xe08\xc8Ȑ\xb8\"\xdaP\x9aϰx\x9e\xfb\f\x8fcɕ#.\xcbr\x9dq\x00\xa8+N\\vǁp\x86Z\x9f\x89ƭ8\x00\xc5\x13g\xed\xc4\x1b\x97\xb8\xef\x8bk$\xf0C8\x818\xa1(\x18\x17\xf0\n\xc5Y\xa4\tDž\xa4D\xe2\x9c\x1c\x88\xc6\xf1`L,\xce\xf2Y<\xae\v\u007f\xe2\x02$6NMƱ\xa6*\x15'\xb9K\xc7%Tf\xe2\xacʌ\x88\vb\x8c\x84\xd6F\xc1wF\xc7I\u007f\xc6\xc0I\xc7B\xf6\xe3h\xe5\xe3\xe3\xc8\x01'\xa0\xf1\x89qTU&A\x98\x93\xb3=\x9f\x82ߦRW\xd3\xe2Ĭ\xe9\x94䠜\x19qR\xf8\x99qM,fa\xa0\xb3\xb5ks\x00\x12s\xe3\x12]\xe6eU8\x1f-.P\xe9-\x8c\xb38\xb1\b\xdf.\xa6z\x97\xc8\xf5\xeeܜ\xa5\xea\x9c\xcb\xd4%\x96Ǚm\xaf\x88\xb3\f\xbd\x12\x82\\\x15\xc7j\x82\xd5:\xce5t\x97\xb5q@\xdc\xdbq\x82\xe5;*\xdfw\xe3\xc8\x1a\xde\xe3\x8d\xd6i/\xd6+8\xbc\x0f\xe3\xdb\xc0\x16>\x88cS\xa0\x8dqͽ6\xc5Q=\xfc0\x8e\xa0\xfd\x91v|s\x1c唏\xe3|3\xdf't\xadO\x15\xf6>ST\xdb\x12\xc7Kt?ׁ|\x01\xf3\xd8\x1a\xe7\xf3\xa7_\xc6Q\xd2\xdc\x16\a\x91\xff\n\xba\xfb:\xce\x1a\xc27\xf0\xc2o\xf5\xba\xef\xe2(cmgk;\x00\x1e;\x81\xa5\xbb\xd8\xe7\xddT\xe2\xf7q\x14\xef\xf7ą\xc9썃\xc0\xff\xa0ػO\xadp\xbf\"Ə\xea+?\xc5\xf9x\xccϸ\xe3\x818\xa2\xd4A\xa8\xe5\x10[8\xac-\xfc\x12G%\xe3\u05f8\xc4\xc7\xdf\xf4\xcb#\x8a\xdbG\xa1\x93c\xfa\xed\xf1\xb8\xee;\xa0v\xfcG\\\x18҉\xb8\x86\xb9?\xf5\xb4\x93q\xe4y\u007fe\xbf\xff;.Q\xea\x94j\xe7t\x1c\xa4\xf3\x9f8\xab\xa6g`\xc8gi\xbb\xff\xea\x97\xe7ԛ\xfe\xe3\xb9\xe7\xa9\xdf\v<\\\x84S\\R\xff\xbd\x1c\a%\xb9\xc2߮\x12֯\xc5A5\xae\xc7\xc9\x1an\xc41;pSeu\v>~;\x0e\x96wG\x82YeB\xbe˗\xd0\x1aA\xfe\x84Xy\x01\xfc-\x98\xc0ՅpFa\xfc-\x92\xa0\x94\x8b&\x04 \x8b\xf1\x84\xe2\tĒ\xbb\x12x(\xbe\x04\x0f%\x13\x85\xbb\xe6\xf6u\xe4\x11\xfa\xbby\xd6=\t$\xdb\xf7&`h\xf7%Pƺ\x9f_>\x90 \x84?\x98\x80\xd9=\x94\x90\xa9\x9b\x87\xf1\xf7\x91\x84\x06\xcb\x04f\xa7\x1e\xe3\xe5\x8f'\x14\x04\x9fHp\xe7\xaa'\x13\xa8T\x94J\x88\xe5\x97N0\xa6\xe4$\x90?\x97I\x90\xed\x96M\xd0=\xca%\xc44\xcb'\x10ٟJH\xaa\xfat\x02r{&\x01\xb7z6\x81y\x82\nٛ<\x97 K\xad\x98 \x95\xad\xc4\xdf+'\xb8\x01A\xa2p\xd3~\x86l\xd8R%\xa1\x18\xfcB\x82:z1Q\xb0\x81\\P5\xc1\xcc\xee\xa5\x04\xea\x10/'$K}%\x81:u\xb5\x84\xc0[\xf5\x04UT#!:\xae\x99\xbd\xf3\xab\t\xe0q\xad\x046{M0\xb5|=A\xfc\xaa\r-\xd5\xe18\xea&\x04\xb2\xea%P魟`9\xbaABBqÄnb\a\x156N\xf0\x89\x8c&\t2\x88\xa6\xbc\xbeY\x02\xe1\xa09z\xd3\"\xc1=\x1cZ&\xb8$\xf2\r\xa8\xbbUB\xf3\x8d\xd6\tN\x04\xb4I0\xe8\xb5M\xe8[*\x13(F\xb4O\xc02;\xb0\xe1\x8e\tƀN\t\x98]g\b\xbfK\x02\b\xd0U\xe5\xd3-\x81$\xa7{\x02y{\x8f\x04\xe6^\xde̻\x8b\xc3=(\xa7'\r\xb5W\x82\xa8\xf9V\x9ea9\xfa[\xc3\xfb\xe5\xe4&\xb8\xb0\xb87m\xa2\x0f\x84\xd4\x17\xed\xf7K \xf8\xf7W=\fH\xc0\xf3\a\xea\xb0\x06%\x04k\x06\xe3\xfc!\xf8;4\xc1\xfaٰDv\x06}x\xa2@^\xfe\x9e\xe3H(OqjS.=\xba\x13R\x922\x12\xc0\x17\x13·\x12\xe4\xf1\x1e~\xe9\xe5\xc1DZ\xf9y\b\xa0{\xc1\x84\xa4{!\xd5@8\x01\x1e\x18I\xc8\xde\x1a\xd1\x04\xaa/\xb1\x04\xa9lb\x8b\xce\x1e\xb5uE\x8f\r;{\xdc\x06v=aӳ\x9e\xb4\x89\x0f\xa5l\xb2\x98Ҷ`@\x8e\r\xc6P\x06\xbd,k#\xbb,g\x8b'\x94G/\x9f\xb2\x91l=m\xcbJ\xf6gl\xe5\a\xcfڲ\x88\xb3\x82ͪ\xab\xadD\xa7\xa2M\xac\xa8\xc4aV\xb6\tZ\xcf\xeb\xb1\n\x84\xf9\x02n\xfb\xa2\rK\xa8\x8a۾d+\xe1~\xd9\x06\xbc\xbc\xc2\xfeW\xb3\x11*\xaa\xdb2\x89UC\xc7V\xd3\x16\x16\xf8j\xb6#\xb5\xf4\x96\xafٺ\x8b\x9d\xad\xb1\xb76\xc5W\x87\xf7\xa9k\x83BֳY2\xabo\xa3L\xd1\xc0\xe6\x1cuC\x1b\xf5\x89F\xb6\x14y\x1b\xabZ\x9a\xd8\xe48M\xf5\xa4f6\x90\xbb\xb9\xfe\xdc\u0096\xeaDK\x1b\xa9\xa4\xcd@؊\xe3nm\x03\xc2\xda\xd8\xfa\x0e.[\xa6\x02\xdb\xd9\x1a5\xdb۴\xc1\x0ePYG\x1bu\xacN6\xaaӝmС.vv\x9dzWt\xa9\x9b\x8d\xf0\xdf\xdd\xc6B\x83\x1e\xb6\x18蛶\x90\x87\x9e6'\x11zQpoق\x83\xb9\xb6\x06D\x8a\xa0\x8fJ\xae\xaf\x8dUI\xfdԒ\xfa\xdb\xe0\xbd\x03\xf4\xe3@Jh\x10\xfb3\xd8\xc6\x03BC\xa8\x88\xa16\n#\xc3lz\xdfp\x9b\xbc\xcaa\xc3G\x9d\xb6\xd4\x12]6\x1c\xc6m\x03\x15\r\x1b\xc5\x02\x13\x1a\xb7(\x0f\x0f\x1b\xf3\xdaR\xe7\xf7a\x10~[\x9e\v\v\xd8@\xd7 \\$dK\b\n\xdb\b0\x11\x1e\xa2\xb6\x16$bj\xbaq\x18h\xc2\xceK\x0f\xf2\x82\xa1-\xfc'i\xb3О\x82XӸq\x86\xca\x1f\x01?\x1c\x89\x1fF\xfd_\xb4\xa3a\xdecl\xb0\xaf\xb16\x8b\xad6\xa6\x8b\xc6ۘm\x9f\xa0w\x9bh\x93FM\xb2\x05^&\xdb\xc0\x8e)6aw\xaa\x9a\xc94\xb5\x8b\xe96\x8a\xf83lě\x996\x1e\xa7\x9ce\x83\x03̶\x19\xe9\xe7\xd8|\xfe|\xae\x8d\x9a\xce<\x1bQ|\xbe\x8d\x9a\xea\x02\x1b\xd1a!\u007f[D\xd5/\xb69s\xbd\x84?.\xb5\xb1>f\x99MF\xb7\xdc&J\xae\xb0\xc9\xe9Vb\xf4\xab켰\xb1\x1ac_c#Ʈ\xb5\x11F߶Q1x\xc7\x16L\u007f\xd7榮6\x1eEZg35[o\x83\x90\xbfoK\x82\xb3\xc1.\xd4\xd1\xc8\x1d\x90\x97C\xea\xaf\x1bm\xf0\xa8M\xeċjA\x1f\xe9qs^\xf3V\x9f\x9c\x8fm\xa6\xbb\x9f\xa8\x87~\xaa\xc7\xcf\x00\x1d[8\xe6\xcfUj_@\xf7[m\xc9\xc1\xbe\xb4\x89\xe6\xdb\xd4\u007f\xbe\xb2\xb1*\xf6k[\xd0\xf9\x1b\x1b\x19\xa4-D\xe0;\xe8c;\ro\a%\xbd\xd3\x06\xeaﲱ\x84y7l\xed{[2\x88=\x80\x9a\xbd6\x12\xad\x1fl\x94\x00\xf6AP\xfbmT\x99~\xb45\xb2\xfed3\v\xfb\xd9\xe6\xac\xc1\x01\x9b\x85\xa6\x836\x97\xb0\x1c\xb2\xb1\x97\xc9a\x1bK\xb9~\x01\xe2\xff\n\xeb\xfa\r\xc6{Ė\xc0v\x94\xea\xad\xc7g\x92,\xd3<\x9b\x942q\x85$\xb0幤\x10\xa9\x8aI\xe5ʕ\xd8\xcb\xcaI\x89\x86\xcf\xe3o\x95$\xe2c\x92N\xf6b\x92\xb3\xbfU\x93\xe4r/%\xe1\xb5/'\x914&\xc51\xab\xa9\x94\xab\xeb\xfdj\xa0\x99\x9aIVK_M\x82\x91\xd4J\x8a\x05\xbc\x96\xcc\x03\x83\xd79\xc4\xda\xda\xcd:I\xb8q\xdd$\xea\x8e\xf5\x92\x1a\x84\xeb'\x99?4Hb\x8e\xa1!\x0f\x8d\x92b#\x8d\x93\xb0\xe6&I\x14\x17\x9b&Ŋ\x9b%Q~k\x8e\xfb\xb4H\x8a\v\xb7LҔ\xde\xc0\x88Z%\xc5[['\xe9\xeam\x92\x98\xfen\x9b$\xa8\xb6K2\xa8\xb4\xd7c\x87$\xe6\xd9;R\xbf\x9d\x92\xe2P\x9d\x93\xc0\xf1.I\xf1\xe0\xaeY\tvK\xb2r\xdb=)L\xa8G\x12\xee\xf3\xa6ޥgR\xca\xf0\xbd\x92\x1at\xdf\xd2ss\x93\xe2]\xbd\x93\x88\xf7}\x92\x04\x9a\xbeIzC\xbf$\x89R\u007f\x1aހ\xa4\xb8\xf1@~\x18\x94\x94\xfa\xc0\xe0\xa4Č!0\xfe\xa1IV{\x86%\x81\xd0Ó\xac\x95:\x92\f\x8aI\xc0\x91+\xa9\xcb\\\x93$\x11\x06[3\x93\x04l+\xc9y\xc8$\xe3\x827\x89\b\xed\x83X\xfdI@p\x006\x1dLb\xed[(\xc9L;̛D\xd44\xa2I\x84\xa4X\x12q4\x9e\x84\xc7&\x92\x82*vR\v\x95\xc9$\v(\xa9d\xf6\xa1\xb8t\x12\xcc2\x93d\xce;\"\x89\xdd\x1cG\xb2K\xa3hᣳF1&\x89\x9c|,\xfb?.\x89\a\u007f\xc7\xf3\xa4\tI\x94p'&\x11\xdf'\xb1ߓ\xd56\xa7\xf0\xe3T\xf6mZ\x92\x15\xa9\xe9IF\xaa\x19I\x99D\x9a\xa9^6\vH2;\x89zʜ$\xf70\x98\xab\x83\x9c\x97\x14\x84\x9d\x0f3\\\x00\t-L\xb2\x00\xb2(\tα8)y\xe1\x12X\xcc\xd2$֠,\x83\x9b,כ\xaeP\xfd\xaeL\xcaZ\xd1UIy\xf2su\x12av\x8d\xded-\x9bz\x1b\x8e\xf4N\x92e\xcfw\x93Xu\xff\x1elg\x1d\xfe\xaeOb\x9dk\x92\xf9\xf3\x06t\xe7\x83$\xd2\u038d\x1c馤\xce\xde\u007f\xa8\xbe\xf0\x11lgs\x12\xe9\xd0\xc7I&o\x9f$\xf37\x1a\x9e\xf3)\x8c\xf5\xb3$f\xe2\xb7$A\xbd>\xd7N\u007f\x91$\xc3\xdfJ!\u007f\x89An\xe3\xf0\xbe\xc2}\xbfN\"0\xaaD\xbfM\x92\xed}\x97\xe4D\xf9v\xda\xfa\x0e\x8es'\xb5\xb1+ɒ\xf6n\x02\xc2\xf7I\xe1\xd4{\x92`={\xd56\u007f t\uf8e9\xedO\xa2\x00\xfdcRh\xdbOj\x86?'\xb1P\xf4@R66=\x98D\"q\b\xb0{Xƒ\x17\x1bi\xb6\xbf\xd2N~\x83\x04\x8e@\x8fG\x93xN\xf5\x98\xf6\xfa8\x04\xfe;\xcf\xfbC\xf1\xe9DR\xb8˟I\x92\xa5\x93\n\xf2\u007f\x11\xd6\xfeN\x92\xf0\x9f\"6\x9cN\xb2\"\xf2\x0fP\xe8\x8cb\xe8Yu\xbf\u007f!\xa8s\x1aX\xfe# \x9fO2B\xaa\xa1^$\x80\\\xa2\x85_Nb\xe6\xe2J\x12\xa4\xff*\xbaw\r\xc1\xe6:Fq#\xc9\xfa\xfcM\b\xe4\x16\x10\xeevR2\xf0;R\xc2i\xefL\xe9|W\xbe\x14D\x9c?\xa5\x8eT \x85\xf5\x9d\x05S\xc0\xb7B)\x06\xed\xc2)i\xb4H\x8a\x13\x10ES\x90k\xb1\x14\x18D\xf1\x14R\xc7\x14\xf6\x1fHɍJ\xa6P[\xbd;%\xef*\x92\xcc\x11gܛB\xbd\xe7\xbe\x14\x88\xfc\xfd\xbc\xcf\x03)\x01\xb1\aq\xc2C)\xd8\xc0\xc3),py$\x85gP\x1eM1\xcc<\x96¸\x1fO\t\xb6=\x91\x92\nǓ)1\xcbR)x~\xe9\x94L\x1b\xe6\xa44\xbd*\x93\x02\xf9)\x9b\x12'*\x97\x1d\\y\\\xf1T\x8aR\u007f:\xc5L\xf6\x99\x14\x92\x93gS\x80\xc4\n)\xc0\xc4s)\xbaEE\n\xa2R\n\xa0U\x99?>\x9f\x02\bUI\x91\x80\xbf\x80N\xbd\x98\x02\x03\xa8\x9aBI\xe8\xa5\x14\x9d\xf9唬z|%E0\xae\x96*\x9cG\xd9\x1cC\x1c9\xd5S:]W#\x85T\xa4f\x8a&\xf1j\n\xec\xa6\x16[y-%Ŷ\xd7S\xa2\xc4\xda)\xec|N9ԅB\xeaQ\x96\xf5SZ!h\x90B\xe6\xdf0E\xbcj\x94\x92٥\xc6)\x02h\x93\x147fhʋ\x9a\xa5\x98\xf56O\xa1\x00Ղ\x87\x96)\xac\xa2z#%\x06\xd5*\xc5\xec\xadu\n\xa8\xd3&\x05:\xd7V%\xd3.\x85\xac\xab}\nx\xd2!\x85\xc8\xdb1\x05\xce\xdd)%a\xb63\xac\xa1K\niaWJ\xb9[*/\xd0wOq~\xa8G\n4\xeb\xcd\x14jZ=S\x98\x00\ue552|\xfa\xad\x94\xf8|n\n\x98՛\xf2\xee\x93b\x15\xbd/\xfb\xda/%5\xad\xfe)A\x9c\x01)\x89\xe4\x03\xd9Р\x14\xd9\xe9\xe0\x94\xa4aCR\xf4ġ)Y\xdf2,E.2<%.\xe7H\xb1\xb6\xe1\xe4 \\)8\xb8;%1\xcd\xd0q\x9a\xfc\xcdJ\x01\xf7<\xea\x11^HȗB)ҟb1*\x00\xad\x04S\xa4\n!\xdc0\x9c\xe2\xa4T\x84\x92\x8d\xaa=\xc7\xd8j\x1c\xcd$\xd0\x19;\x05\x80M\xc2%R\xfc\x90\x86\x01d\xa8\xf4\x11\x14\xe1H~\x1a\x85\x9b\x8dNI\xba2&\xa5\xa4cl\x8a\x13C\xe3\xe8l\xe3S\x02\t\x13R\xdc\rrb\xd6\x0f&\xa5\x80n\x93Sd\x1dS\xb2\xb68U\x8dpZJ\xb8\xe0\xf4\x14\x83Ό\x94$\xf93SR&\x98\x95\"2\xcfN\x81T́e\xceM1\xf1\x98G\x11\xcd\xd7V\x16\xd0\x1a\x17\xa68\u05fc(\x05\x00[\x9c\x92p\xb0\x84\xd7/\xa5[-SQ/Oa\xb5\xe0\n\x15\xe0ʔ\xf0\xd3U)\xa4\xaa\xabi\xb6kR\xc0\xfb\xb5y\xa2\x90T1\x85\xa8\xf3\x0e\x9by\x97\xa7\xbc\x97\xc2c\xa2\xebRX\"\xb6\x9e\xd2z?\xc5%\x19\x1bRL\x9e>\xa0\xf5n\x84\x99mR`\xfb0\x85\xfc\xe5#\xe8\u007fs\n\x93\x14\x1f\xab\x93\u007f\x92b\xc0\xfb4%\x85\x95\xcfRR\"ܒb$\xf8<\x85\xac\xed\v\x05\x96\xadjc_\xa2\x9dm\xb0\xe7\xaft\x88_\xc3\\\xbf\xa1A\u007fK\x91|\x97b\xa5u{\x8a\xa1d\a`jg\n\xc1iW\na`w\x8a\x11\xf9\xfb\x14ˡ{RZhۛB\x99\xef\x87\x14\xc3\xe2>\x9a\xf0~\xb8\xfe\x8f\xfa\xe5O\x00\xe7\x9f\xe1\x8e\a`\x12\aэC\xd4\xc2a \xcb/\xf8\xe1ה$\xb0\xbf\xd1X\x8f\xe8@\x8e\x02\x9f\x8f\xa5Pl>\x9eB\xe0\xfd]}\xf8\x0f*\xe4D\n\xc10\xc5\xf4\xe7\xa4\n\xf4/\x9a\xc0\xdfTө\x14R\xb6\xd3)Į\u007f\xb2#8\xa30s\x96\x9a\xfa7\x05\xf2p\x8e\xc6\xfe\x1f\xc7s\x9e\x12\xbe@\xaf\xb9\xc8//\xa1ߗ\xa9\xfc+\x04\xa9\xabp\x98kD\xd1\xebT\xe1\r\xf5\xb9\x9b\xaa\xca[)\xa4u\xb7S\xc8/\xeeH#ۺ3-`\x91/\rh\xc9\xcfC\x81\xb4(\xb0`\x9aK\\\n\xa5\xb1\xddkZdY$\x8d1\x14M\xb3hP,\xcd\a<\xd20\xb9\xbb\xd2\xe2\xd4%Ұƒiqӻ\xd3\xfa(\xf2=i}\xbe#-\xf2\xba/\xafi\xa9\\\u070fF\x1fȞ\xf4`\x1a\xe3{(\x8db\xc1\xc3i\x01\x84G\xd2܉\xf3Ѵ\xa8\xe91m\xe6q\xdcꉴ\xb0\xce'\xd3\xf4\xbbRz,\x9dF\xb0\xcaI\xabw\x97I\xd3Z˦\tz\xe5\xd2\x04\xc6\xf2i\xc8\xfd\xa94\x1e\xbcz:M\x80{&-\x10\xfel\x1aڭ\x90\x86\x1e\x9fK\x8baVL\xc30+\xa5aÕ\xf9\xe9y\f\xb3J\x9a\xc8\xfbB\xbaH\x8b\\S\x16K漘\x86)U\xe5\xe1\xa54b\xc8\xcbifW\xaf\xa4aR\xd5\u0603\xeal\xb1F\x9apR3\xcdu\xaf\xaf\xa6\xf1>\x10\xdc\xe05\xf4\xe0u6V[O\xa8\x93\xe6\x1ev8\xa3\x1e\x1b\xa9\x0fe5H\xd3\xeb\x1a\xa6\xf1Hb#ʵ1/o\x92f\xd2\xd34M+o\x06\xc96O\x13A[\xa4QEi\x99F\x11\uf374\x84\xf8V\xf8\xdb\x1aM\xb7I\xe3\x05\x17m\xf1\xa1]Z\xa0\xbe\xbd6\xd4!\xcd\xd8\xdf1\r\x93섫:\xa7\xc5㺤\xc1q\xba\xa6%\\u\xc3W\xdd\xf1\xb7\a\xday\x13\xa7\xf6\x84R{\xa5\x11\x01\xdeJ3N\xe7Bǽ\xd3(\f\xf5I#\xda\xf6M\x83\x9c\xf4S\x81\xf5\xd7\xe3\x00\x8a``\x1a\xf3\xbf\x83\xd2\x12\\\x06\xa7\xe1%C\xd2Z\x98\x1e\x9a\x06\xc9\x1c\x96&\xe4\f\x87\xec\x1c\xfcҙ\xd6z\xa6+\r:\xee\xa6،\xb4\xa0\x80\x99\x06=\xb3\xd2\xe0x\x1et՛\xc6$\x86/-\t\xb0?M\xf4\x0e\xa4\xe1\xc0\xc14\xc9H(-ip8\xcd\xc2i\x1aq3\x9af\xc9?\xc6\xfbġ\x80DZ\x18\x93\x9d\u0588\x96Ls\xfa+\x95\xce#\x0e鴤\xb9\x994\xea\xd8#T\xcc#Ө\x1e\x8e\xe2a4]wL\x9a\xf3\xb9c\xd3\x12\xa1\xc6Ѷ\xc7\xf30!\r\xa67\x11\xde=)\x8d9\xa8\xc9t\x96)\xf8nj\x9aդiiF\xc0\xe9i\xf0\x8a\x19\x1c\xd1\xcc4\xcab\xb3p\xea\xec\xb42\xce9ia#s\xd3\x12\x8e祥t9?ͪւ4H\xccB\x1e\x16\xa59\xe9\xb68\xcdգK\xd2\xc0\xa8\xa5\xb0\x88e\xec\xcc\xf24\xe2\xc2\n|\xb7\x92\xa3]\x95\x06O\\\x9dַF*\x00\xacMK z;\x9d\xbfC\xbf\x9cw\xa8\xbew\xd3\xe0`\xef\xa51\xf9\xb4.ͬb=\x9by\x9f?nHK*\xfbA\x1a\f~#Lk\x13\xdc\xecC\xdc\xf3#\xf6c3\x9b\xf88\x8dy\x94O\xd22a\xf6)\xd0\xe83\xbd\xfd\x964қ\xcfՌ\xbe\xa0\xb7lMs\xd2\xec˴\xc4\xfaml\xec+\x00\xca״\x9bo\xd2(j}\xab`\xf3\x1dNܞF)m\a>\xecT\xb4څn\xedΚ\xe4\xf7heO\x1a\x0f\x9a\xed\x85M\xfd\x00\x99\xef\xa3r\xf6\xf3\xf0\xa3\x02\xf3O\x18\xce\xcfi\xae\x1b=\x90\x96%]\aӘE:Į\x1f\x86\xd1\xfd\xa2\xe3\xf9U\\Ǒ\xf3\x9b:\xdd\x11\xdc\xfc(N9\xa6\x82<\x0eW\xf9=O\x80\xb99\u007fp4'p\u009f\xaa\uf4c0\xf6\xbf\xd2\x1a\xde\xfeN#@\x9fJ#C=\xadj\xff\x87\xc8w&\x8d4\xe8l\xba@\xa3\xdey\xa1\x8f_\x9eK\xa3d\xf3_\x1a\xc4\xf9<\xb0\xe5\x02\xac\xedb\x9a\xd5\xfcK\x1c\xc2e*\xfcJ\x9au\xf6\xabi\xd2\xd0k\xbc\xfez\x9a\xbb\x8c\xdfH\xa3\xe4\u007f3\x8d]On\x89\x1fݦ\xe2\xef\xc8\x00\x14\xee\xcc0>\xe6\xcb\x10\x95\xf3g\xc8\v\vdHy\nfD\x88\x852\xfaZ\x8f\f\x00\xa0HF\xe2eQ\xfcT,#b*\x9ea\xb2|WF\\\xaeD\x06\xe0W2\xa3\x9b\xb6f`w\xf7d`\xd9\xf7f\xa0\xe9\xfb2\xac\x92f\xf4\x99\x8d\f3\x94\a3\xec\xf8C\x19\xe8\xf2\xe1\f\n\x80\x8fd \x9cG3\xacz?\x96A\xee\xf0xFl\xf7\x89\x8c\x84\x85'3\"\xa3R\xf8[:\x83\xd9\xc3\f<\xbdL\x06v\\6C(*\x97шX>#\xa2}*#\x00\xf9t\x06U\xf6g0\x94g3\x98\xa3\xad\x90aN\xff\\\x06U\xe3\x8a\x19T\xed+e\x80v\x953\x90\xe1\xf3\xb8Q\x95\f\xe1\xe6\x05\xf6\xeaŌ\xb8J\xd5\f\xc2\xdaK\x19\xbc\x16\xe5\xe5\x8c\x18\xc9+*\x8fj\x19\x89\xf8\xd53\fA52\x84\xb2\x9a\x19\x80\u05eb\x19q\xeaZ\x19>\x86\xf8\x1a\x9by=\xa3k\xf7y\x9e\xe7\xbe?\xe3\x9a\xef\xeb\xfa\x8c;\anm\x01$~a\x8e\xe4\x85\x16\xe5\b\x94ZL\xe1YB\xab\xb1T\xfb\xbf,\x87!\xc0\xf2\x1c\xba\xd1\x159\xe2NVB]W\xa1+\xabs4\x1f\x9a\x03P\xb9\x96\x8c[Gʬ\xcfQo\xbf!\apac\x92q\x9brtG\xf5\xcf\x1c\xe6\xe6\x1c:\x90-Tҭ\xec\xc5/\xec\xfa6\x90g;\xca_\xa9\x85;r\x10\xf4\xfcF9\xde\t\xee\xee\xcaa8\xb9\x1bµ\x87\xd4؛C\x8c\xf9{Nᶩ\xc3S\x87\xa5\x8fJOٗ\xa3ɼ?r\xe8\xf5\xf6\xe7(x:\x90#\xae\xe8`\x0e\x12+\x87 E\x87\xc9\xf1#9\x82\x18\x8e\xb2_\xc7r\x98\x9d<\xae\xd7\x13\xda\xfb\x93\xa0Ω\x1c8\xf5\xd39L)\xfe\x99\x83t\xf3_9\xf0gg\xd0ճ\x10\xa9s9\x9a\xcc?\x9f\xd4\xf9\v4\x87\x17s\xb0|\xe0R\x0e`\xd4e^\xaePV\xae\x92\x92\xd7\xd4\f]צo\xe4p\xbd\xcd\xcd\x1c8\xc1\xbfu\xd8\xff\xe4\xc0\xf1\xff\x9bT\xe9[9\b\xb2nchw()ws\x14+\xdc\xcba4r\x1f\x04|@\xc3\xf1P+~\x04\xa9\xfc/\x87\xf31\x8fs$\xc7\xf1D;\xf1\x14.$\x97A4-\xb7Ai\x99\xc7\x00_\x9f\xd7\x00\xb9\xc8g\x80@\xe47P\x9c\v\x18\x88d\v\x1a\xa4\xa9B\x06\x99\x04+l\xa0\x05,b\xa0V>c\x80\xd9)\x8a\x1f\x9f5\xa07\xc5\f\xe4Xq~|\u0380\xad\x1a\x06*j\t\x03E\xb1\xa4\x81V\xe8\x05\x03\"\x82\x17\r\xb4\x84\xa5\f\x88__2p\xf5Oi\x03\xcco\x19\x03\x16%\xbfl\x10\xad,k@\x1a\xe8\x15\x03·W\r\x88{\xca\xf1\xc6\xf2\x06\xb1i)\x06\xf1\x9c\x15\xd0\xed\x8a\x06\xa6\x9a_çJ\x06\bJe\x03\x82\xd0\xd7\r\xa2To\x18D\xf1\xab\x18Ȍ\xaa\x1cЛ\x06Ɠ\xd5\xf0Xu\xfdT\xc3@0\xf1\x96A6(\xd64pf\xf7m\x83j\xf1;\x06\x01y\xb5\f\x9co\xae\xadd|\xd7\x00\x0e\xd61\x88P\xd55\xc0o\xbdg\x10!\xacg\xa0\xa7\xac\x8f\x1e4\xe0O\r\rЉF\x06\b\xd4\xfb\x1cWc\xf6\xea\x03\x03|~\x13\x12\xb6\xa9A%\xf2C\x03\x96&43`\xa9Xs\x83\xcc\xef\xb40 \xc3\xd4Ҡ\xca\xfd\x91A\xb4\xa6\x95A\x12=\xad\r\xe2\tې\x8emѯv\x06\x82\xb0\xf6\x068\xb4\x0e\x06\xacP\xe8h\xa0-\xee\xc4!t6\x10\xd9vQ!\xe8j\x00\xd4\xeaf \x88\xean`\xec\xddC\xa9Փ}\xef\xa5?\xf76H0\xd1\xc7 6\xf5c\x03=\xa4\x013۟\x1aT\xf2\xfb\xf2\xeb~\xfc\xba\xbf\x81\xdb\xd6R\r\xccU\f\x00\xd1\x06\x1aD\xc6\a\xa1L3H\xde\xff3\x03\xb0\xcb`\x03\xf6h\x18\xb8(=݀\x85LCq\xdf0\x03\xf5\xe3s\x03-\xe5p\x83\xf8\xc5\x11\x06\xa4\xfc3\f\x88a2\rj\xedF\xb2ۣ\f2\xdd4:I\xbf,\x03\xd0]\xb6!o\x97\xf4\x941\xc9o\xc7&\xff\x19G\x96\x8c\x87\xacO\xe0\x87/\f\x88\x11\xbe4\xc0\x1e}e\xc0\x8a\x9c\xaf\xa9c\xdf\x18\x90\x01\xfb\xd6\x00,7Q\xe9<\xc9\x00\x8c\xf0\x9d*\xded\x83\x98\xfe\xef\rL\xe9\xff`\x80m\xc9a\xad\x06\x03\xf0\xb0рD\x8fI9c6`\xfd\xaeŀP\xdaj\xc0\xde\"\x9b\x01\xf9\x0e\xbb\x01\x93\xca\x0e\x83\xb8@\xa7\x81\xf0\xdae@\xfa\xcam\x10\xbf\xee\x81~y\xc9U\x9f\x01sK~\x03\xf1r\x00\x84\f\x1a$V\r\x19$\\\n\x1b\xc4=E\f\x12~DA\xd0\x18\t\x1a瀧\xf02\x95\xfd\xfd\xd1\x00\v;\r\xd5\xfcd\x80K\x98\xce\xce\xcf\x00\xa5g\x1a0;0K\xa5e\xb6\x81fk\x0e\x98>׀\xfc\xdc<60\x1f\"\xbb \xf1X\">D_\x16\x19x\xd8\xd5b\xf4c\t\x1aYj`ph\x90U\x90\xcbI\xfd\x15\x06\xf5m+I\xbeU\x06\x1c\x92\xbb\x1aʱ\x86\x8c_\v\x1dYG\x93\xb8\xde \xb9\xd4\rT\xbf\x8d\x06\xb8\x87M\x06X\xf7\x9f\r\x98\x8e\xd9l@ز\xc5P\xa4\xad\x9c~;$}xF\xcaV\xe1簄o4$W>m3\xd0lo\xa7&\xfdj\x00\xea\xdea\xc0N\xc9ߔ\xc3;I\xf9]\xec\xdbn\u07b3Ǡ\x96\u007f/L\xd1\xefj]\xf6\x19\x18\xcf\xfc\x91\xa0k\x82l\xfb\r\x125\x1e\xc0\xc0\x0fB\x11\x0e)\xe3\x0e\x1b\xe0\x85\x8f\xd0\xf2\x1d5\x00\xac\x1d3\x88o;\x8e\xdbO@\x89N\xfe\xaf\xab\xa7\f\x8cTN\x1b0\xf7\xf3'\x9f\xf8\v\xad\x9fQ\xb3\u007fV\xaf\xe7@\xdc\xf3j\xd3/\x18\xb0\x80\xf8\xa2\xb2\xee\x12$\xed2yvŀp\xe3\xaaJ\xfa5\x15\xd8\xebd\xd2\r\x83.V\xb9i`*\xfbo\x03\u0588\xfd\xa3\xda\xfc\xafA\xe3\x84[\xc9\x1bo\xd3\xd8\xdd\xe1\xe5\xae\x01>\xf9\x9e\xd6~\x9f\xcc{`\x00\x0eyH\x9b\xf6\bV\xf6?\xc8\xcec\xca\xe6\x13\x88\xfdS(m.#Nd5\xe2\x87&A%\x1f\x9bd\x8d\xe0'\x94\x8cO9о&\xc9W\xf43\xc1\xb5\xf47q. \x15$\x19@\xc6\x0fD\x93\x83L\x1a\v\xa7\x81h\x9f\x99`R\x06S؆\xa8\x9c\xa6\xf3ۡ\xa6|\x89x2e\x18\u007f\xfc\x9c\x14\x1cn\x82\u058ePBe\x98\x00\xb32)\x99#M\xd0\xfaQ&\xac\xeb\x1bmRg\x9fe\x82Q\xccV~\x8d1\x11\xbb\x8e51\x0e\x1eg\x12\xeb6^\xd91\xc1D\xd3\xfc\x85H藬\xfe+\xea\xd8\xd7I\x9a|c\x02\xec\xfc\xd6$\x11\xc8D\x13\xd3\v\x93Lt$\xdfQF&\x9b\x90\xf0\xf8\x9e\xf4\xf9\xc1\xa4\xa7\x88昰\xbd\xc6`\x92\f\xa8\xd1$\xa6\xd1db:\xd2\f=\xb1P|\xadT/\x1b\xc4\xcenbN\xd6!}r&\xabr\xc9'7i\xe511E\xee5\xc1(\xfaL\x82\n\xfc&\xe2\x92\x00\x15#hb|\x1e2q\xcdC\xd8\x04\xcf\x141\U00048ae8I\x96\a\xc6Х\xb8*\xd8\x14\x93\x065SI\xe0\x1fU[\xa6\xa1\xfb?\xa1\x9cn\xc2\xf2\x88\x19\xaa\xb83M\x02ff\x91p\xb3MLq\xcd1q5\xe8\\U\x8fy\xfay\xbeI\\\xe4\x02S\xa1ީ\xa3夸\x94\x85&\x8d\x1b\x16\x99\x18\xdd,f\xc3KL\x92\nY\nY\\fbnr\xb9vr\x85\thl\xa5\x89S\x0e\xabL\xccz\xac6!\xd6XCv\xac5\xc9\xd9z\xeb \x84\xebM\x12\xc8n\xa0e٨\x86o\x93I\xf2\xb0?\x9bx\xd4\xc5f%\xdd\x16\xb5-[M\xccq\xfc\x02\x1em\x83y\xdbn\x02\xca\xf9\xd5\xc4\x03\x19vP\xca~\x83\xd2\xed4q\xfed\x97\x89~uw\xd2\x10\xec1I\x06n\xaf\t\xfb6~7\x11v\xef31\x8d\xf6\x87Iç\xfd\xfa\xd8\x01J\xcfA\x13\x92o\x87L\x98&:\xacr|\xc4D\\u\xd4D\xd0~\xccDPt\x1c,\xcd,\x06\xf3'3|\xf9t\xfc4\x83\xaa4\xd3L\x9c=\x8b\xaa<[\xfb6G\xf92\xd7,fl\x9e\x19hm\xbe\x19\bj\x01:\xb1\x90\x1f\x16\xa1\xb2ŬyIR\u007f\x96\x92\xe0ˠ\x91\xcb\xcd\xe2\x89V\x98\x05]\xaf4\vR^\x05\xcdY-$[c\x86/YkVH\xb3\xceL\xf7\xbd\xdeLӻ\x81\xaa\xb4\xd1L\xf0\xbc\x89z\xf2sR\xa56\x9b\x05\xb4n1\v\x88\xdbj&p\xfc\xc5,\xb8p\x1bua{\xb2\xe2_\xa9\";`\xf5~3\xd3\xff\xef4\x8bW\xd9E\x13\xbcی\x97Z\x99ŷ\xef5\x13\x8c\xfen\x16\x10\xba\x0f#\xfe\x03\xe5~\xb3\u009e\x03\x94\x98\x83l\xe7\x90\x19P\xec0?\x1d!q\x8f\x92\f\xc7̊^\x8e\x9b\xb9\xdcF\x85\xec$\xf5\xe1\x94\x19X鴙{\xf5\xcd\x1a*\x9a9\xb7H2\x9fE\xdb\xe7\x92\xc3>o\x06\xb2\xbc`Vdy\xd1\flz\xc9\xcc`\x11佢zr\x95}\xb9f\x16\x90t\x1dzqC\r\xc4M|\xf7\xb7\x19\xc0\xe9\x1f3\xd0ٿ\xec\xf3\xad\xe40o\x9b\t\x91\xee(c\xeeB\xbf\xef\xa9\x1d\xbf\xcf\x11= \x05\x1f\x9a\xe1\xfb\x1eA\x10\xffS\xd3\xfb\xd8\f\xfc\xf3\xc4,\xc8\xe9)5#\x97E\xba\x91\xdbBl\x95\xc7Bz\xe4\xb5\x10\x02\xe5\xb3PQ\xf2[\x14\x86\x14\xb0\x10\xf5\x14\xb4\xc0*\x15\xb2\xc0V\x15\xd6NJX\xf0N\x0e\v\\mQ\v\xd5\xe8Y\v`G1\x8b\xba\xce\xe2\x16\x9a\x82\xe7,p`\xcf[\xe0\tKX\xc4x\x94\xb4\xa0S/X\x12\xf2\xf8\"zV\xcaB\xe3\xf0\x92\xf6\xa4\xb4E\x80D\x19\x8b`\x85\x97-P\xb0\xb2\x16\xbc*قE7\x16\u009ar\x16!|y\x8b\xf8\xb3\x14\v\x95\xb4\x82E\x04\xae\"\x9b}\xcd\x02KP\xc9B\xa7[\xd9\x02iy\xddBk\xf7\x06*\xabb\x81{\xaaj\x81\vz\xd3\x02;PM\xbb_\xddB\xafH\n\xbce\x81e\xafi!;߶\xc0\x92\xbcc\x11C_\xcb\x02M\xa9\xcd\u07be\xabMԱ\xd0d\xd4U2\xbdg\x11\xfd\xa9\xa7\xbfַ\xd0\x184\xb0`\rICV\xd1H\xbf}\xdf\x02/\xd8X?~`\x11W\xd7\x04eS\v\x8cև\x16N\x8f7\xe3\x9d\xcd-\"\a-,L\x02\xb6\xb4`\xf1\xdeG\x16\xb5\x11\xad,bZZ\x93\x04m,b`\xdbZ\x98Zjg\xc1\x8a\x81\xf6\x16Q\xbe\x0e\x16\xe4\x1e;Z\b\xd1:Yh\x18:[\x10\x9ft\xe1\xa5+\x87ٍ\x97\ueb34\x87\x05Ɍ\x9e\x16Ht/\vB\xeaސ\x96>\xfc\xf01>|\xa2\xc4\xfd\xd4\"\xc1e_\vv7\xf6\x13i\xe8o\x91\x1cP\xaaE\xd5l\x80\x85\xd6x\xa0>0Ȃ|\u007f\x9a\x05\xda\xf8\x99E,\xc7`\vva\x80,\xe9\x16\xccC\x0f\xb5\xc0S\r\x83h}n\x81\xc6\f\xb7pK\xa2\x05`)\x03\xb7gZ\xc4я\xb4`\xf5\xc4(mk4\xebȂ\xb8e[\xb8\\g\x8c\x05\x19\x8b\xb1\xe8\xfe8\x92`\xbc\x05I\xeb\t\x16N\x06~A\x1e|i\x11\a\xfc\x95E\"\x8d\xaf\xf1\xff7\x16ؙo\xf1\xd5D\v\x94~\x92j\xdew\x10\xdd\xc9\x16,\xb5\xb1`\xde\xe1\a\xedG\x8e\x05\x93\x13\x06\v\x12\xa6(M\xb8\xdb̆,\x16\xa4@\xad`\xa4\xcd\"\x99);\xf8\xe7\xc0\xb0\x9d\x1c\xaf\xcb\xc2\xd9\t7\xef\xf6\xa0G^\x8c\xde'\x04\xf7[\xc4W\x04Pq\x90\xec\vQ\xd6\xc3ڏ\b\x852j\xc1\x84F\xccBC\x1e\xb7\x10\x9fN\x01\xf5\xa7Z0\xd5\xf6\xa3\x85)\xaei\x16Y\xe7\xf1\x13\x885\xdd¥\xa73(b3-X\xad>\x8b\xf2;ۂT\xd4\x1c\xb68\xd7B$9\xcf\"\xd0s>\xa9\xbc\xc0\"\xb1\xf6B2q\x11\xe5l1ƹ\x046b)~_f\x81\xf1^\xaeva\x85\x85\x90a\xa5\x05\v\xc7VY\x90\xf2^mQ(\xb6Ƣ\xb6v\xad\x05\xa6r\x9d\x05\vCף\xc3\x1b,\x00\xe6\x1bI\xb0M \xfc\xcfhp\xb3E\x12s[,p\xbd[)\xe4\xbfP\x14\xb7i\xc3\xdbѧ_I\xfb\x1d\xb0N\xbf\x89\xcdHO\xd9i!\x96\xdb\xc5\av[\xb89k\x8fJ\xf6^\v\xf1\xd4\xef\x16\x9e\xeb\xa6d\xfe\xc3\x02o\xbc\x9f2y\x80\x97\x83\x16d\xf7\x0e\xf1\xb7\xc3\x16\x8d\xb6\x8eh\x95G-\xf4C\xc7,\x98\x138n\x01\x929\x01\x19;\xa9f\xe8\x94%\x99/\xb50Y\xfb\xa7\x05\xf3t\u007fY\xb8\x8b\xfe\x8c*\xffY\x12\xf0\x9c\xf6\xf2<\xfd\xc1\x050\xfd\"\xb9v\xc9\xc2H\xe3\xb2^\xaf\x90oW-\x82S\xaeYt\x95\xd0u\x98\xa6\x1b4\xb57-\x8a\x11\xfe\xb6ps\xd5?\x16̄\xfdK\x8d\xbae\xc1۔n\xb3\xffw\xd4\xfeܵp\xb9\xd6=T|?\xc9\xcb\a\x16콰(J\u007f\x84\xae\xfdG\x95{l\xc1\x19\x8fO OO\xd5V\xe6\xb2\xc2\xf9\xe4\xb6b6%\x8f\x95\x04\xc9k\xd5\n\xf2Y\xd1\xfd\xfcV\x02\xe1\x02V\xc6S\x05\xad\xe8a!+\x83\x8f\xc2V\x8c\xbd\x88\x15\xb4}\xc6\n+X\xd4\xca\x03Q\xad\x84rŬ\x90\xf8\xe2Vr\xf69+\xc6\xfe\xbc\x15\x89\xba\x12V\x12\xb8\xa4\x95L{\xc1*\x12\xf6\xa2U\xa8T\xca\n\x03\xfc\x12k-\xcdZ\xcbXuO\xa2\x95ֽ,\xebyE\x9f\u007f\xd5\n\xf5,g\xa5ܔ\xb7\n\x19R\xacT\xd2\nV\xeaeE+\xd4\xf45\xabУ\x92\xf6\xa02\t\xf1\xbaU9\xf5\x86\x15\xaaY\x85t\xa8j\xe5&\f\x8e\xb7\x9a\x15\xda[\xddJ\xf5\xaaa\x05\x99߲B9kZ\xa9\xbeo[E#߱Rdja`\xb5\xadP\xdcw\xadP\xb4:Vhk]\xab\xaa\xe1{V\xe8g=+T\xae\xbe\x15J\xd9\xc0*\x9a\xd7Њh\xd1\n\x1dz\x9f}jl\x85z}`\x85\x066\xb1\x8a\xca5\xb5B+?\xb4b\a\x86\x12\xa59~ja\x85Ƶ$\xfd?\xb2ByZ)\xa1Z[!dm\xacХ\xb6 [;p\xa1\xbd\x15\vQɋ\x8eV\u0557NVQ\xa3\xce\x18T\x17\xab\xbe\t\xd9J\xa5\xe9f\x85\x0eu'\xadz\xb0\xe6\x9eV\x8ao/\x15\xa8\xdeh\xa2OR\xdc>\xb6BD?\x01K>%\xc7\xfbR\xaa\xfaQ\xe4\xfa\xabȥ\xaaX\rPy\x1aH\x01\x18d\x85\xb9J\xb3\xe2\x95T\x1c\xc6`+\xb2*C\xac\xaaf\xe9\xf8q\xa8UQ\xf10P\xf6\xf3\xe4\xc7\xe1V \x9e\x11V`\x90\f+\x17-fZ\x810G\xf22\xca*^f\xb4\x15\xf35Y\x18B\xb6\x95\x88}\f\xfb1\xd6*\xe8b\x9cvr\xbcU\x17\xa2O\xb0\"\xd7\xf8\x85\nۗV\x81\xfc_Y\xe1R\xbe\xe6o\xdfp\x04\xdf⧉\xaa\x8f\x93\xf4\xfa\x9d\x15\xb0r\xb2U\xdc\xd4\xf7V\xa6\a~\xb0\x8aoαJ\x02\xc0`%\xc02R\bMV\xec\xb60[\x05\x1aX\xac@\xe2V6`\xb3½٭\b\x1c\x1cVdT\x9ch\xd5ee\n\xd7mŤ\xafNJ%#^\x8e\xccg\xc5\xf4\xbaߊ7!\x83\x94A\x1d{\xc8*X3lE\\\x19A\x1f\xa3*\xf71+v\xecǕ\x9cS\xac\xcc/N\xb5\xe2H\x90\x1f!\xa7\xd3\xd0˟\xac\x04\xae\xd3)\x993 \xb43\xad\x922\x9a\x85\x1bf\xb3\xffs\xac\n\xad\xe7Z\t?\xe7\xe9u\xbe\x15 w\x81\x15\x89\x85\x85xr\x11\xad\xc6b\x8a\xd2\x12^\x96Z%P[f\x955B˭\xb0\xb5+\xac\x92\xf0Zi\x95Xa\x95\x15k\xc6W\x93;k\xc0\xe5\xb5ʇu:\xac\xf5Vƅ\x1b\xa8\x8a\x1b\xad\xb2[r\x13\x89\xfe3*\xde\fA\xd8¯\xb6Z5#\xf6\x8b\x15\x11\xed6+6;n\x87V\xffj\xe5\x11\x8a;\xac\xba!Ê\xd5U;\xad\x82\nw\x91\xf1\xbbѿ=|z/\x15\xffw\xf0l\x9f\x15\x8b\xa7\xfe\x10\xaa\xa5\xa6\xec'\xfb\x0f\x80\a\a\xf9\xec!\xf6\xf10\x06rĊ\xf9\x92\xa3\xb8ᘊ\xe9q\xab\x00\xc0\x13V\x01O'\xf1\xcb)\b\xfai+N\t\xfb\x13\xdd\xfc\v\x86\xe0\fm\xd4Y\x15\xe4sV\xba\xf3\xf3\x94\x98\vl\xfebR\xad/Y\x91\xed\xbf\xcc\xcf\xe9)Wh\xf1\xae&\xd5\xe2\x9a\x15\xbb\xa5\xae[\x15\xe8\xde \x8boB,\xfe\xb6rb\xe1\x1f\xf6\xff_\x8e\xfa\x96\xca\xe8m\x1a\xf1;ʐ\xbbV\xc9\xd4\xdcSQ\xb8\x0f\xc3\xf4@;\xf9\x90\x9d{\x84\x01\xfc\x87\xf1=\xc6\xffOh՞Z\xe5\\\x88\\6\xe8En\x1b\x93LylX{\x99\xd7\xc6\xfcf>\x9b\x90(\xbf\r\xfd+`\xc3Z\xa3\x826\x84~\x85l\xa8\xa5\xb0\x8d\x93\x14El\x9c\xfdy\xc6F\xd2\x14\xb51\x1b\xf3\xac\x8d}+f\x83H\x16\xb7I\x87\x9f\xb3\xb1\xfb\xcf\xdbDZJ\xd8\xe4L\x8a\x926\x86\x856Ʊ/ڠ\xbd\xa5l\xd2\xe3\x97ث\xd26\x19D\x19\x94/ې\xc9(k\xc3\xda\x1a\x1b\xb7\x1b\xbfjcN\xbc\x1cn)\xcfn\xa7\xd8\xe0\x89+\xd8\x18\xe9W\xb4\x89\x86\xbc\x86\x81U\xb2\x89JT\xb6a\xbb\xcc\xebh\xea\r\x9b(z\x15\x1b\x91\u007fU\x1b_Ϩ\x1f\xabٸZ\xb9\xba\r\xbc\xa9a\xd3\x04\xc1[6n#\xa9\xc9\xef߶\xe1@7\x1b\xe8_\xcb\x06\xbd\xaam\xe3J\xd7w\x95Nul\x9aj\xafk\x13s\xf6\x9e\rf\xb8\x9e\x8d\nWߦ'07PJ5\xb4\xc1n5\xb2Ѡ\xbc\x8f\x016\xb6\x89l\u007f\x80A4\xb1A\xea\x9b\xda\xf4`p\x1b\xf6:4\xb3!E\xd0\x1ccnaCڢ\xa5\r\x1bI?Қ[ٰ\x9c\xb3\xb5\r\xdb\x04\xdb\xd8\xc4µ\x05\x97\xda\xd9T}\xdb\xdbhe;\xe80;\xa2\xbaN\xa0Wg\xb2\xa6\x8b\r\xf1sW\x1b\x96\xb3u\xd3\a\xba\xdbD {\xd8t\x1dVO0\xab\x97\x8dP\xac7(\xddGG\xf4\xb1\r\x1a\xf6\x89\r\xe6\xf6St\xa3\xaf\rz\xd2\x0f\r\xf5'mS\x93\x9d\x1a\x00\xba\rd\xbb\x83\x94Ai6\xe6H?\xb3Q\x89\x06\xdb`\x15\x87\xd8hk\xd2mX\xf83\x94\x14\x18\x06\"~nc\x14<\x99ԏ\x91Iv\x8d\";GC~\xb20\x84l0`\x8c\x8d/:\xe6e\x9c\rہǃ\x90\x13T\a\xbe\xd0\xeb\x976I\x19|\xa5t\xf8\x9a5~\x93h\x1f\xdb\xf4m\x8a\x8b&\xb2\xef\x93P\xc5w\xa0\xe4d\x94\xdf\xdb8\xb9\x88\xee\xe7\xb0\u007f\x06\xad\xda\xc8\xcaL\xaa}f\x90\xceb\x93ē\xd5\x06\xa3lSZ\xd8\xd9C\x87\r\xde\xceI\xea\xbalb\x00\xdd\xfc\xe0\xb11\x1d\xe3e\x13>\x1b\x17\xca\xf9\xa9\xa8\x01\x1b\xa1h\x90\xf5\x84\x94la\xfe\x1a\xb1\xf1\x15jQ\x1b\x92$1\x1b\x122q\x1b\xf3\x9dS\xf8q*e\xe7G\x8cp\x9a\r\xee\x90j?]\x891C\xab\x99\ty\x98Ey\x98M\x12\xcfa\xc3sm\xccKͳ\x11\xe6η!\xbf\xb7\xc0V\xb0k\xda\xe0\xd4Qi)\vQ\xf1\"\xc8\xecb\xea\xe4\x12^\x96\xea#\xcbԆ-\xb7\x15\xe8\x906*5;e\x85\x8d\x8bll\x12]\xac\x02\rW\x83\xdakTZ\xd7\xda\nv\xcf\x1a\x9d=,-e\x1d\xed\xdaz\x1b\xc0\xdb\x06[\xe1\x04\xd2\xe5/\x1bm|]\xfe&\xb4\xff\xb3\r\x93'\x9bmؗ\xb0\xc5\x06\x97\xb6\x95\x84\xfe\xc5V\xb0w\xea\xe0\u052c\xb4\x94m\xe8\xe5v\xaa\xe4\xaf\xff\xfbz\a\f\xcao6,t\xdci\x93\xd4\xee.\x1b|\xcan\x1bV*\xed!\xd5\xf7\x92ſ۰\xe6a\x1f4\xe4\x0f\x1bC\x87\xfd\x94\xe7\x036@\xe5\x836\xfa\xc0CIu:Ls~ĆL\xcdQ\xb5CǨ\x95\xc7myZ\xa4\x9c\xb0I\n\xe3$L\xf6)\x1b\xc0\xfai\x1b\xf7\x05\xfc\xc9J\xffb\x13gl\xea\a\xcfژ\xbb:g\x93\x99\x84\xf3\xa0\xc3\x05\x9b☋\xe8\xdd%\x1b\xbd\xffe6t\xc5&SFWm\x84\xe7\xd7X\xdf\xf5\xa4Fܰ\xe5k*\xabP\xd5\n\xffmC\xd4\xf3\x8fM\x92j\xff\xda\x04*ܲ\x01\x16ܦ\x86ܱq\x01\xe5]\xe9\xfe=\x9b\x00\x8b\xfb6\xa2\xf6\a\xa4\xfcC\x1b\xc0\xd3#\x9b \xa0\xfflxՔ\x8d\xb9\xd6'6\x04-OmLs\xe7\xb2K\\\x93ۮ.<\x8f]\xc4!\xaf\x9d\xce.\x9f]\xcf\xfb\xcco\x17\vW\xc0\x8e\x83\xa3\n\xda\xe9q\n\xd9\xd9fa;\"\xdb\"v4\xfd\x8c\x1dh\xbd\xa8]\xb3\xae\xcfڅ\xc7\xc5\xec\\\xf9]\xdc\xce͋v\xa6a\x9e\xb7#\x1a(a\x87\x97(i\x87G{\xc1\x8eu\xa8v\xf6\xb7\x94\xb6\xf2\x92]\xf0Li;\xe9Z\x86O\xbcl\xa7A.\x9bl\xed\x15\xbb\xe0\xccW\xed\b\xa2\xca\xd99\x87^\xdeN\x98\x9ab\xc7\xca\xdc\nvl3\xa8h\xc7\xcb\x19\xed$z%;\x93\x97\x95\xedX8\xfc\xba\x9d\xd6\xe4\r;\xfdN\x15;\xc3\xc0\xaav\xf0\xe6M;\xbct5;NǪ\x9el\xbd\x86\x9dr\xf3\x96\x1d^\xaa\xa6]4\xecm;\xe7{߱ӒԲkj\xa3\xb6\x1do\x83x\xd7\x0e\xf8Q\xc7.ӱuI\x9f\xf7씰zvHo}P\xa4\x01\xc6\xd6\x10\xff7\xb2#\xbd\xf7\xbe\x1d\xa7\xe56V2|\x00Z7\xe1oM혋\xf9\xd0.b\xd4\xccN\xd1knG<\xd4\xc2\xce\x18\xba%\xf8\xfa\x91\x1d\x11P+;\x03\x91\xd6v\x95\xf36vh_[;\xe6\x03\xda\xd9y\xa0\xd7\xc7v\xccw~\x02\xc1\xfc\x14#ꫤ\xeb\xa7d\xe9oǤI\xaa\x1d\xa1\xe9\x00\xbb\x18\xf7\x81(\aa@il\xf03\xf6t\xb0]\f\xcb\x10;\fK\xba\x9dfc\xa8]\xd4t\x98\x1d\n\xfeyr\xc4\xc3\xed\xf2\x12\r27C\x99\x99\xa9\x8d\x8e\xb4\x8b&\x8e\xb2\x8bJ\x8dF3Y*\x9aٔ\xfc1\x18\xc4X;\xa6\x17\x95\xde\xe3ѫ\tv\xa8\xe3\x17*\xd2_ڡ\x82_Qo\xbe\xe6\xd3\xdf(=\xbf\xd5J'\xaa\x96MBS\xdf鯓)\xf7\xdfC\b~H\xca]\x8eJ\xab\x81\xd2i\xa4\xf8\x9b(\xcaf\xd5\x02K\xf2n\xab\xaa\x83\r\xf2d\xa7\x14:P\xa3\x13\x1cpQ\x80\xdc\x14>\x0f\xa9\xecU\x91\xf1Q\x82\xfc\xca\xf6\x00\xba\x17$\xa5C\xa4{\xd8\x0e;\x1d\x81\xe4E\xed\xd8Ǐ2n\x97D\xed\x14;\xd68N\xb53\xdf\xf2\xa3]vOMC\xf9\x13n\x9bnG\x14;Î\xa5\xdf3\xf5\xbeYz\x9d\xad\xd79vx\xa8\xb9\xfaq\x1e\uf78f\x1a\x16\xe8\x97\v\xf5\xba\xc8N\xbb\xbf\xd8.\xab\x92\x97ع\xd2y\xa9^\x97)\xb3\x96+aV\xe8u\xa5\xbd`\xd7\xec\x01r\x8e\xe4*;\xcf\xf9^\xad\xf5\xad\xd1\xeb\xda\x04\x89\xd23S\xd6\xd9\xf3\xb6\xceLYo'\xf0\xd9`\x97ue\x1b\xedD\x19\x9b\xec|\x97\xb1ڗ\xcd\x10\xe8-(\xb7\xdaq\xaa\x8d\x1d\xc9\xd1m\x90\x9e\xed4Q\xbfڑ\xa1܁\x1b~\xb3\xcb\xca\xef\x9d\xca\xc6]v\xeeӰs2k\x8f\x9d\x89\x93\xbdv\xf1\xb0\xbfӞ\xee\xb3#+\xf4\x87\x9dY\xea\xfdP\x80\x03Tƃv\x01M\x87\xd0\xc7\xc3\"\xeeGT\a\x8fR\xb4\x8e\xd9\vv\xcb\xc8J\x1b\x9e\x91r\x9c\xb5\x9cPƟ\xa458e'\x14=mG\x14\xf2'9\xff\x17\xac\xf3\x19;\xa3\xb9\xb3\x14\xe9s\x18\xd2y\xbb\xae\xe8\xbf\xc0\x9e_T^\\\xb2\xc35^\x86\x9e_\x81:^\xb5\xcb!c\xd7t\xa4\xd7\xed\x80\xfe7\xec\x92E\xbaIk\xf67e\xf2\x1f;pƿ\t+\x92\x96\x99r\xcb\xce \xef6\xe5\xfe\x8e\x9d+\xe1\xeeҖ\u07b3\xcb|\xd8};\x91\xf3\x03H\xf8C;6\x1b\xf9\u0601\xd8\xf3\x13\x8aϧ\x0e\t*\xfaB\x1a\xfa\xb1\xcb\xfd\x95\x10\xa9\xe0\xe6\x00\xdc0СKn\x1c0\x92i\xac\xf13\aQ\xcc`\x87\xa6Ň8\x04\x97\xa6s\x9cC\x1dr\xa0\xe30V\xfa9*\x1bN\xd1\x18A\xa9\xca \x013\xd1\xf8H\a\x80\xff(\xde>\x9a\xbc\xcfr\xc0\x01d;\xb0\xf4h\x8c\x03\x1bQ\xc6j_Ʃ\"\x8c\aq'`\xc4_@.\xbft\xd0r~\xe5\x10\xf4\xf95\xbb\xf3\r\x18\xfa-{>\xd1\x01\x838Ɂ\x00\xeb;\xb2p\xb2\x8a\xd6\xf7\x0e\xfa\xa2\x1f\x1c\\\f\x97\xe3\x10\x93bp\xf0|/\xa3\xf2ȤW\xb3C<\x8d\x05\x82lU\xd2\xd9؊\x9d\xa2\xe4\xc0oNt\xd0\xe5\xe0\xc2@\xb7\x83\xe1\xb4ǁcm\x1c\xccv\xf8\x1c\f7\xfc\x0e\xcc^\x060\xb2\xa0\x83;\x91C\x0eq6a\x87B\xeb\bY\x11u(\xea\x8f9\xc4\xe3\xc6\x1d\x8c\xa6\xa680\v6\xd5\xc1L\xe4\x8f\x0e.\xb7\x9f\xe6\x90\xe0\xe4'\a\x1c\xeat\aC\xc2\x19\x0eN\xfc\xcf\xd4\xde\xcfr`?\xe1l\a\xe68\xe6\x90Fs\xf5\xa6y\xa0\xf0|Pv\x01%x!{\xbbȁ\xb4\xe5b\xca\xe7\x12\x90{\xa9\x03\xce~\x99\x83\xc7\xce,w\x10\xb6\xafp\x00X\xact\xc8ΝU\xe0\xffj\ar\xe8k\x1c\xb2\xf6a-\xf9\xbe.9\xb6\xf5\x0e\x80\xad\r4d\x1b\xc9\xd0M\x0e\x01P?\x83F\x9b\x1d\x98\xbe\xd8\xe2\x10H\xb8\xd5!Q\xf0/\x0e8\x8dm\x0e1\xd0\xdb)\xed\xbfҰ\xeePj\xfc\xe6 \nډ\av\xb1\x91\xddT\xf9=\x0e\x06\xbb{\x1dL\xa7:x\xa4\xeb>\x87\xe6\xb2\xfep\x14\xe8\x98%\xe7E\xee\xa7\xfe\x1e\xa0\xfe\x1fL\xda\xc2C4\xa3\x87)\xcfG`\xa6\x8e\x82E\xc7@\xc1\xe3\xf8\xff\x84\x03\xab\\N:0\x17\u007fJ\xdb<\xcd6\xfft\xe8\xcb\xfc\x1d\b^\xcf8\xe8\x9a\xcf:$\xa7r\x0e}>OB^\x80\x84]dO.\xc1#\\VWp\x05\x02v\xd5!H隃\x80\xf1:\xcd\xcb\r5\x977\x1d\x98\x8b\xf9\x9b\x1c\xf8ǡ\x87\x9d\xd2\x18߂\x13\xba\xed\xa0\x93\xbdC\x8e\xdfE\x13\xf7\x1c\x9a\x98\xbd\xef@.\xe1\x01\x84\xe2\xa1\xca\xf2#\aS-\xffA\xee\x1f;d\xba\xe8\t\x89\xf4ԁ\x1cv.'\x8e\xb4\xcd\xed\xc4\x18\xf28\x13\xc6.\xafS\xb8\x99\xcf\xc9\xed\xa1\xf9\xf1\xa9\x80\x13\xc3*\xe8\x04\xef\n9a\xc6\v;\xb5\xed\"\xc9\u007f\x9eq\xe2h\x8d\xa2N\xa6\x1e\x9eur\xa5z1\xa7Щ\xb8St\xe79',\xe0\xf3N\x01\xaa%\x9c\x18nI\xa7t\xfa\x05\xa7\xf0\xe4\xc5d]\xa5\xd8\xe4KN\xbe\x94\xd8\xc9\x11\x95q\x8aH\xbf\xec\x94e\x00e\x9d\fn^q\n\x1b_u\xd2\xe0\x94sb\x92\xa3\xbcS\xf1\u007f\x8aS\xc0{\x05')Qщѿ\xe6TkY\xc9\t\x04UY\u007f~\xdd)3\xe1o8i\x18\xaa\xa0\xb1\xaaNh\xea\x9b\xf8P\xcd\xc9\xd4[u'\xac}\rg2\xad\xeaD\x96\xa1\xa6\x93\xc6\xe3m'\x94\xe7\x1d\xa7\x80\x9fZN\xb0\xa6\xb6\x93\a\x15\xbc\xeb\x14\xf5\xaa\xe3\xc4F\xbd\xbaN\xca\xf4{N\xba\xe8zzS}R\xa6\x81S\xb5\xaf\xa1\x93\xae\xbb\x91\x13\x13$\xef;\x89D\x1b;!\v\x1f8E\xc0\x9aplM\xf5\xb7\x0fyk3'\xc3\xeb\xe6NZ\xd6\x16`FKm\xef#\xde\xd4ʉ\x1d\x1b\xac\xad\x8d\x93k\x03\xdb:aE\xda9\x99\xe6n\xef\x84\x1d\xe9\xe0\xa4\xc4vt\x8aLur\x12\x8fv\x86\x98tq\"\xea\xecʎts\"(\xe8N\xd2\xf4 \x15z\x82½\x9c@E\xbd\x9dt\xc3}\x9c\x88-?v>\x93\xc0 i#2\x86gde\xa4|\xe2\x84q\xfd\x94r\xd0\xd7\t\xcb\xd6\xcf\xc9e\x00\xfd\x9d\x98MIu\x024\x0epbG\xfa@'\x8c\xe0 \xa7\x04)iN\xf1v\x9f\x81\x03\x83\x9d\xcc1\x0eq\"\b\xbd\xc0I\x17\xb3\x10\xb4]\xc4\xe1,FǗ85l\xc4s\xcbP\xe3rX\xa3\x15\xfa\xd0J\xfc\xb2*ɐ\xd50bk\x9cpekul\xebȑ\xf5\xaai\x1b@\x80\x8d(7%\x1f\xfc\x99Vd\xb3V\xbb\x05}ي{~\xe1\xe3ۜ\x043\u06dd\x98\xc8\xf9\xd5\t\x0f\xbb\x83\xc2\xf8\x1b?\xed\xe4e\x17:\xba۩\x89\xfe=\xb0({\x9d\x9a\f\xf8\x1d\xbf\xees2`\xe4e?u\xf9\x00Fs\xd0\xc9E]\x87\x9c\xc0\xaf\x87\x9d\x9c$>\xa2z\u007f\x14\xa49\xc6'\x8e;e\x0e\xfe\x04L\xecIU\xd7SԈ\xd3NIS\xfe\xe9Է-\xe2\x963NDag\xb5\x85sʱ\xf3j_/8q6\xc3E\xa7\"\xf4Kp\x03\x97i\x84\xaf\x80\xadW\x9dX\xfbz\x8d\xba|\xddɼ\xf7\rX\xb8\x9bN°\xbf\x9drx\xc6?N\x01-\xff:e\xa1\xc3-\xd0\xf36\x86w\xc7ɅnwI\xac{\xd0\xcb\xfb\xb8\xe1\x81S\x8e4y\xe8$z}\xe4,\xdcz\xf4\x90\xf4\xf1\xd9\xc3\x13\x11\xa3\x93sL\x8f\x9d⥟\x80\xa2O\xa9ع\\2\xce\xdc.¸<.]\x88\xe3\xa2z\xe4s\xc1\x94\xe5w!\x9f]\xc0\x05^\x16taW\xbfK\xea*\xec\x12\xbcW\xc4\x05\xfe?\xe3Jxآ.\x84SϺ\x80\xbd\x8b\xb9\x10!\x14w\xa1\xb1\xe7\\\xec\xdb\xf3.\xe4>J\xb8\x04:\x95t!\xa7\xf5\x82\x8b\x0e\xefE\x17\x8ei)\xe5\x82\xd5|\xc9\x05\x95*\x8d;˸\xb0T\xe1e\xb4\\\xd6\x05\x8c\xf1\x8a\x8bٮW\xd9B9\x17\xa0Ly\x17\xa8\x93\xe2\x12\x92Up\xa9(Uta\x9e\xe35\x17\xb0\\%\x97й\xb2\v \xf4u\x17߯\xe8\"\xba\xad\xe2*\xd8*u\x80\xbc2\xb1\xaa\xab@\xebQ\x92F|\xd3U\xa0\xc3\xe0\xd4\xd4a)\xd5\\0\xdd\xd5]\x98\xbb\xa8\xe1\x12&\xbf\xe5\x12\x10S\xd3%\xccx\x9bm\xbf\xe3\xc2\xdb\xc7j\xb9\xc4^\xd7\x16ʼ\xeb\xa2\n\xd5q\x11\xac\xd6E\xef\xde\xc3\r\xf5\xc0\x86\xfa\x18Y\x03\x97\x88JC\x97\xb0\xbe\x91\v\xc6\xff}\x97\xe0\xa1\xc6.:D\x17\xa7\xff\x9a\xb8\x80⚺(\xb8\x1f\xb2\xddf\xbc4wa\x91O\v\x17^\x8e\xe1\xa2\x01\xfd\x88\xbf\xb5r!\xe5\xdd\xda\xc5\xf8\xa6\x8d\x8b*\xd2օ\xc5\b\xed\\\xa2\x14\xed]X\x85\xea\xd2\x05^\x1d]<\b\x9c\x15tv\tB\xec\xe2\x02Z\xee\xea\x02\x02\xed撬Ow\x97\xa8W\x0f\x17\xbdKO\xe5w/HLo\xb4\x93\x9e\xd2Dž\x00\xf1c\xfd\xf1\x13\xfc\xf8\xa9K\xf4\xba/\x05\xad\x1f\xe8\xd0\xdf\xc5Dt*\xa5e\x00\x1a\x1d\xe8B\xf2c\x10\xe5*ͅ\xf4\xcag.E*\x83]\xb2=d\b\xb8\x91Nn\x0fu!qJZ}\x8e߇\xbb\xe0\xadG\xb8\x18\x9ed\x80\x0f\x99\xa0\xf7H\x97b\x92Q.y\xb1\xf8h\x17\xa0c\x96\x8b\xfa\x93\xad\xe3\x1a\xa3\xea1V\x85o\x9c\x8b\x86o\xb709\xbf[:V\xc0\xadӷ\x05\xdd8h\xa8\x90\x9b\x01fa7\x9c@\x117z\xf3\x8c\x9bswE\xdd\"\xd7ϺE\v\x8a\xb9I\x99\xe2n\xc9y?\xe7fN\xf7y7T\xbc\x84;OӔ\x92\xee\xfcm\xf1\xc6~\xb7$\xca^t\x03ߖr\v\xa2}\xc9\r\x04]\xda\xcd׀\x95q\x8b/z٭\xa4/\xebF\x9a\xf3\x157\xec\xe0\xabn\xae\f.\xe7Fڴ\xbc\x1bH1\xc5MXT\xc1-\x10\xa4\xa2\x9b\x1c~\xcdM\x01\xa8\x84o+\xbbE _w\x03k\xbd\xe1fJ\xae\x8a\x1b\x81OU\xb7\x92\xeaM7c\x97jn\x81\"\xd5\u074c\xe8j\xb8Aз\xdc\xe2\xe3j\xba\xa1;o\xbba\xb3\xdfq\xd3m\xd5\x02\x19k\xbb5~ħ:n\xc0\xa4\xban.\xfb}/\xf1xjJ=7\xf2\x05\xf5\xdd\xd8}\xd0\xc0]\xb0m\xea\x84Ԅcn\xe8\x16\xdf\xd8\xc8\rl\xf6\xbe\x1b\"\xdf؍\xc8\xef\x037_=\x9c VZJS\xa5\xf4\x87\xfc\xb2\x99\x1b\x8e\xbf\xb9[\x81e\v~\xddҍÿ?rc긕\x9b)\x91\xd6nq\"mtTm݀\x1e\xed\xb4\xc2\xf6\xe0E\a\xb7\x88_Gr\xa4\x93\x12\xbb\xb3\x1bo\xebwcz\xb5\xab[\x8c_7w\x02\nt\xe77=܄[=\xddb[z\xb9\x11(\xf4vS\xab\xfb\xb8\xe9\xfe>vs\xbd\xc7'n\xe4i?\x057\xfa\xbai\xba\xfa\xb9\xa9\x9b\xfdݚ\xbdMe\a\x06\xa0\xe1\x81nz\xf6An\x18\xe147\x16X}\xe6\xe6\xfaTmg\x88\x1b\t\x8fte\xe0P7<\xd407A\xed\xe7z\x1d\xee\x86\v\x18\xe1\x06 \xcc\xd0\xf1e\xba\x19w\x8ft\v\xa4\x1cEi\x1f\xed\xd6\xd7ghdz\xf5\xf3\x187\xe7$Ʋ\xc1qn\t\xc7ƻi\x8f&\xb8\x01\xa7\xbepý\u007f\xa9\xc2\xf1\x95\x9ba\xc3\xd7n5\xb0\xdf@Ŀu\xc34OD\xab\x93\xdc<\x0e\u038d\xb9\xed\xc9n1\xfb߳\x9a\x1f\xc8\xd3\x1c\xdeap3P4B\x91Ln\xe6\x1b\xcd$\xabEykuc\xfa\xc0\x061\xb6\xbbaL\x1cnYX\xe4Dk.7\xb6j\xba\xdd\xac\xe7e\x03/\x1b\xdd\x12:l\x82\xc2\xff\xcc\xfb6\xbbe\xe7\xe4\x16\x90{\xab\x9b{f~!\xaf\xb7\xa9\xddٮ\xf2\xf2\xab\xf2k\x87\x9bo8\xfd\xcd-3\x03;\xddX\x86\xb6\xcb-8i7\x87\xb5\a\x94\xddK\xbd\xf8\x9d\xb4\xda\a}\xfeCe{\xbfJ\xf3\x01\x10\xe0 zt\b\xf5\x1dV5;\x92\xe4\xe7Q7V\x0e\x1f\x83a<\x9e4~'\x94V'\xd9\xfc)7\x13\x00\xa7\xdd\xf4\xc3\u007f\xba\xe9\x97\xffrs\x1e\xec\x8cڵ\xb3\xb4\x84\xe7ܘ\x998ρ^\xd0\xce\\ts]\xd7%\x9a\xb2˪DW\xdcH\xad]\x85۹Fٽ\x0e\xde\xdcp3v\xb9I\xf2\xfe\xed\xd6\xf5\xab\xfc\xf8//\xb7\x92\xce\xe16Fy\x87\x86\xf6.,\xd3=\xb7x\xe3\xfbnL\x9c=H\x8e\xf8\xa1[\x10\xfd#7_\xbdH>=fW\x9f\xb8\xf1n\x85\xa7n\xf5չ\xe7ar\xf4y\x0f\x94\xbe\x84\aP\xb3\xa4\a\xcb2^\xf0\xc0Ͻ\xe8\x01\x15Ky\xb0\x8c\xd5CZ\x96\xf6\xa8U)\xe3aN\xf4e\x8f8\xe1\xb2\x1e*\xda+\xac\xe1U6Z\xce\x03\xc5/\xef\x11S\x9d\xe2\x11RU\xf0\x00\xcdT\xf4p\xeb?/\x95<\xf4\xa8\x95=\x84ۯ{\xa0\x96oh\x9bU<\xd8WQ\x95\x04x\xd3#\\\xa8\xe6\xa1\nU\xf7\x80\xf45\xf0\x88\x1a4\xf1\x88!m\xea\xe1\x82\xce\x0f=H\n5\xf3\x10(5\xf7\x00y\xb5P\xae\xb6\xf4\x10\xb6|\x04\xf2\xb6\xf2\xc0b\xb4\xf6\x88\x99mせm\xeb\xe1\xcc@;\x0f\xa6\x8d\xda+\x89;Pb:z\xf2w\xc0\x11\xe3\x1e\tc:+\xa9\xba赫\x88C\xc2\xd1w\xf3\xc0\x8bu\xf7\x00\xce\xf6\xf0ऱ\x9e\x1eQ\x84^$Bo\xb4\xdf\xc7\xc3Ŋ\x1f{4\x96\xfbģqԧ\x1e\x02\xb0\xbe\x1e\x99R\xeb\xe7\x11\xbf\xdd\xdfCۜ\xea\xa1w\x18\x00\x96\f\xf4PE\ay\x10\x13\xa7\xe1\xcb\xcf\xc8\xe1\xc1\xbc\f\xf1PA\xd3=\t\x87>\x147\f\xf3\x00\xdb\u007f\xae\x1d\x1f\xeeANs\x84\a\x87\xc4闙\x1eDM#=\xb4\r\xa3\x92\xb2=Z\u007f\xcf\xf2\x882g{h\xee\xc6x\xb8\xdfj,i:\x8e\xe3\x1c\xef!\xe0\x9f\xe0\x91\xa5|_xxH\x9c\a\xe6\xee+\x1d\xfd\xd7\x1e\x86D\xdfx\x90C\xfc\x96\x15L\xa40OR\xcd\xfa\xce\x03\v4\x99\x1d\xfd\xde\x03\x88\xf7\x83\a\xe1P\x0e\xc4\xc0ࡏ0\xaaX\x9b<\x84\x14f\x8f\x98\x05\x8bʃ\xd5#\xe0Ǧ\xa2i\xf7h\x06̡:\xe0\x04_\\\xd4v\xb7>\xe3\xf1\x00iy=\x92\xb5\xf2a\xd4~\x0f\x93[\x01\x0fQMЃLM\xc8#\b/\xecA\x8a&\x02\xb9\x8dz\x90^\x8ay\x984\x8a'5y\x8aG\x9c\xcaT\n\xf2\x8f\x1ez\xd1i\x1e$\x10~\xd2\xeeM'\afx\x90M\x9a\xe9A\x9c:\xcb#>v\xb6\ai\x859\x1e\x9aҹ\x1e\xa6\xc5\xe6Q\x88\xe7{\x00~\x16x\x90\xa1Z葔\xcd\"(\xebb\xadz\x89G\xcd\xebR\xfdb\x99G\x90\xefrH\xc0\n\x11\x94\x95\xa0\xdb*\x8ch5jX\xe3A\xcec\xad\xf2m\x9dGR\x14\xeb=HQl\xf0\xc0\xdfn\xf40\xe3\xb3Ƀ\x10\xf8gj\xf9fԺ\xc5èd\xab\xca\xf1/\xda\xf26\xda\xdf\xed\x14\xe0_=L\xb8z\xe8=\u007f\xf3\x00\x02\xee\x84\xd1\xd8\xe5\x91t\xc9n\x8f\x84#{h+\xf6*\x03~\xf7 \xcc\xdeG\x93\xfa\x87G\xf2\x1a\xfb=\f\xde\x0e\xc0X\x1ed\x97\x0ey\xd4\a\x1cf\x8f\x8f\xc0\xf2\x1c\xa5\xf1?F\x8d=\xae\x96焇ٌ\x93ЙS*\x0e\xa7=\b\xfe\xff\xa4\xb9\xfc\xcb\x03,|\x86v\xe6\xacG\xf7{x\x18\f\x9fO\xf4R\\\xa5~}\xd1\x03/vI\xdd\xc7e\x0f\xa0\xcf\x15\xf6\xe4*\xb9}\xcd#\x90庇\x19\x8c\x1bI>\xdd\xf4H6\xe1ou(\xff@\xe4\xffUռ\xe5\xd1<\xccmՖ;\x1e\x89\xdb\ueca3\xf7<\x92\xbe\xb8\xcf\x0e> \x81\x1e\xc2-<\x82(\xfd\xe7A\xb8\xf2\xd8#!\xeb\x135\xe6O=\xc4\x13\xb9\xbcL5\xe4\xf6\u009f\xe6\xf1\x8a\x11\xcb\xeb\xd5N\xe5\xf3\"\x86\xce\xef\x85\xc1.\xe0E\x04\\\xd0\v\xa6\x17\xf2\xc2\xf5\x16\xf6\"\x96*\xe2E\xec\xfd\x8c\x97\x00\xa3\xa8\x17\xf6\xe7Y/X_\xcc\vR\x16\xf7\xea\x19\x00^D\xd6\xcf{Ł\x95\xf02)Q\xd2Kiy\xc1+Z\xf5\xa27\x11o\x96\xf2\xaa\xe7\u007f\xc9\vY(\xede`P\x86M\xbe\xecE\xe0W\xd6\xcb\xe0\xd2+@\xe8U\xaf\x04X\xe5\xbc\b\x93\xcb{\x11\xa1\xa6x\x15\x1cU\xf0\x8ajW\xf4\x8a\"\xbc慍\xaa\xe4%\x0e\xae\xec\xa5\xdfx\xdd\xcb\xd8\xd2\v9\xa9\xe2%[\xabz\xe9\x16\xdf\xf4B\xfb\xaay\x11ZW\xf7¡\xd7\xf0\xd2\x1e\xbd\xa5\xa3\xab\x89\x01\xbc\xede\x18\xf9\x8eWps-/\xe2\x9d\xda^=b\x9c\xf4\xa8\x93\x1c_]/\xe3\x91\xf7\xbc\t\xbd\xac\xe7\x85s\xa9綾m\xe0U\x83\xd2\xd0\xcb \xa6\x91\x17@\xfa}\xaf\x82\xef\xc6^\xba\xd8\x0f\xbc\xe2&\x9bxŕ4\xf5\"\xce\xf8\xd0\v<\xd7\f}h\ue17fo\xc1\x9fZz\x05\x98~\xe4\xa5Mm\xe5\x15\x05n\xed\x15\xc9l\xe3\x85}n륨\xb5ӱ\xb7\xe7\xd7\x1d\xbcb\xd6:\x92\xaf\x9d\xbc@\xf1\x9d\xbd\f`\xbaxq\x9a\x9c\x17\xd2\xde\xcd\v\xff\xda\xdd˰\xa4\x87\x97n\xac\xa7V\xdb\xcb\v\x80ڛl\xe8\xe3%D\xfc\xd8K\xbc\xfa\x89\x97\xf8\xf4S\xaf\xa0\xe3\xbe^\x81\xe1\xfd\xbc\xb4\x18\xfd\xd9v*\xa9?\xc0\v72\xd0K\xe45\xc8+r\x9f慓\xf4\x025\r\xf6¥\f\xc1/\xe9^\xfa\xab\xa1I\xd2\x0f\xf3\x12p}N\xd6\x0e\xf7\u00ad\x8dP\x9afx\tm2\xbdԚ\x91\x18\xe1(rh\xb4\x0e)\xcb+><\xdbK]\x1d\xe3U7:6)v\xe3\x92\xff\x8c\xf7bno\x82W\x00\xfd\x17^\xac\xd1\xfb\xd2+\x8e\xf3+\xed\xfe\xd7^\xc9A~\x93|\xe0[/\x1c\xd3D\xaf\x847\x93\xbc8\xf0|D\xcaw^\xfa\xb2\xc9\xec\xf2\xf7\xbc\xe9\a/\xa6Dr\xbcbH\r^$Č\xf8`\xf2\x02\x1c\x9b)t\x16/=\x8aUIn\xf32\xf8\xb6\x8bR&\x1c%ov\xaa\xe4\xbb\xf4\xeaV\xe1\xf6x\x11X{\xbd\x80\xfb\xbe\x04\xbb\xb2Ge\xa7\xf8\xbdH6\x06\xbc0\x95A/lmH\x1f\t{\tu\"|&\ua957\x88y\x19P\xc7\xd1\xc7)l}\xaaW2\xfe?z\t9\xa6A\x1e\u007fB9\xdd+o0\x9cA{4S\xfb<ˋu,\xb3٩9^\xb1\xa0s\xa9\xf4\xf3\u052ċ(,\xf0\xd2\\/\xf4\xc2 /\x82\x8a.\xf6\xc2\xdd-\xe1e\xa9\xf2o\x19\x18\xb2܋(cE\x92\x13+A\xf3\xf4\x94U\xac}5\x89\xbd\x86\r\xaf\xe5\xa7u\xa2\xbd\xeb\xbdX\xf2\xbd\xc1+\xcex\xa3\x17ˁ7%E\xe2g/\\\xe0\xe6\xe4\xe7-hk\xab\x17~\xe1\x17\u07bc\xcd\v\x88\xb0\xdd+\x16\xfeW/b\xc5\x1d^\x18\xfa\u07fcx1\xd4N/\xb6\x92\xee\xa2\\\xef\x86\xc2\xef\xf1\x02\t\xee\xf5\x16\xec4dl\xb6\xf8K\xb5\x95\xfb\xa8\xb6\u007fx\x05\x02\xef\xa7\b\x1c\x00\x95\x0f\xb2\x9aCނ\xcdx\x90\xf8a%\xfb\x11r\xf3(\f\xea1/p\xf1qU\xdb\x13z\xcfI/\xe7\x8aOy\xb1\x1a\xfd4,\xeb\x9f^D\r\u007f)%\xcfxŏ\x9f\xf5\xd2מS\x95:\x8f\x9e\\\xe0`/\xb2ۗ(\u0097\x93\x12~E%\xfc*\t{\x8d\xd2|\x1d\x92r\x83O\xde\xfc_\xb7\xff\xa6\xe8\xfcCR\xfd\xab\x02{KE\xe4\xb6J\xe1\x1d\xb2\xea\xaeJ\xff=\xb6x\x9f\x12\xfb\x80\x12\xfb\x90\xe2\xf4\x88-\xfc\x87\x8e>\xe6sO FOI\xbf\\>\x91\xc5\xdc>\x91\xcb<>\xbc\xd9\xdf\a\xb1\xc8烂\xe7\xf7q\x92\xd2G\x11,\xe8\x13\x0e\x15\xf2\x89D\x14\xf6aDE|*W\xcf\xf8\x84pE}h\xf1Y\x1fd\xa7\x18\xef)\xeeK\x88\xd3s>\xd2\xf2y\x1f\x98]\xc2'\x02S\xd2\a:\xbf\xe0\x838\xbc\xe8\x03\x1fK\xa1;/\xf9\xc8\xf6\xd2>\xe9|\x19\xfe\xf4\xb2\x0fL*\xeb#\a^\xf1a!\x0f\xcar\xbe\xbc\xed\xd2R\xca\xfb`)S|p\xda\x15|\xf4\xc4\x15}\x82\x1e^\xf3\x89ɫ\xe4\xc3\n\x90\xca>\b\xe2\xeb>\x86\xc5o\xf8 )U\xa4\xa7U}:A\xf7\xa6\x0f\x02\\͇\x15\x13\xd5}\xc4\x195|\x98ex\xcbǭ\x015}\xdc\x17\xe9\x13\x11y\xc7Gk_\xcbG\x90[[\xab\u007f\x97\xad\xd5\xf1\xd1I\xd5\xf5!\xaa~ϧ\x00\xaf\x9e4[\x1f\x14i\xe0\x13?\xd1\xd0'~\xa2\x91\x8f\x13\x95>\"\xd5\xc6>ڛ\x0f|\tDф\xc3h\xcaˇ>A\xc0\xcd|\x9c\x17k\x0e\x82\xb4\xf0I\x80\xd0҇\xdd\x1d>\xc6U\xad|L\x8e\xb6\xf6\x11\x8b\xb6\xf1\x01\x8e\xb4\xf5\xc1\xbb\xb6\xf3\x89'm\xef\xd3\xc5\x10\x1d|&\xd1\x06*\x11\a\xf9`AӴ_\x9f\xf9\x10H\f\xf6\xc1J\f!\x93\xd2\xf5ǡd\xe10^>\xf7a\xad\xd4p\xc8\xc3\bJL\x86\x0f\xd3\x12\x99d\xd4H\x9f\x98\xd5Q>B\xaa\xd1J\xb5,\xf08[\xd90Ƈ<\xfcXmb\x1c\b=>9\x82\t\xfa\xf5\x17\xac\xf0K\x1f0\xedW*\x0f_\xfb\xb8\x97\xe0\x1b\x1d\xf8\xb7>\x85\xc5\x13\xa91\x93\xf8\xd4w \xefd\x9f\xa0\xcb\xef\xf1\xff\x0f\xca\xc8\x1c\x1f\xdfa̋\x91\x836q(f\x1f\xf2 \x16\x9f\xd8]\xabO\xc1\x95\x8d2g\xe7\x8f\x0e6\xe0$\xe7\\l\xd5Mm\xf4@\xff\xbc\xa4\xa0\x0f\xaa\xe3g;\x01\x9f\x00\xb3 4=\xe4\xd3w\x17C\xcc\">]\x1f\x17\xf5a\x89H\xcc'\xc8 \x8e;\xa7\xb0\xad\xa9>X\xc8\x1f}X\x10<\xcd\a\xb7\xf4\x93\x0fK\xeb\xa7\xfb\x88]f\x80\xc23\xd9\xc7Y\xbc\xcc\xc6\xc0\xe7(=\xe7\xfa\x90\\\x99\xe7\x93\xf8j>dq\x01z\xb0\x90\r/B\x93\x8b\xd9\xe4\x12ѫ\xa5\x1c\xd52\xa5\xdcrܰ\xc2'\xd1\xc1J\x94\xabP\xae\xf6\x117\xaf\x81\x19\\K\x01\\\xe7\xe3\x0e\x8b\xf5>\x04\xb4\x1b|8~b#\x04g\x13M\xd2Ϥ\xddf55[|\x12\xc1m\xf5\xe9\x01\x15\xbf\xe0\xe36\xf4p\xbb\x0f\xaf\xeeW۵\x83-\xfc\xe6Ct\xbc\x93\r\xec\xf2\xc9Ε\xdd>b\xc7=\x90½\x94\xf3\xdf}\x92\xda\xdf\xe7\x93\x15\x01\u007f\xf8\x805\xf7\x832\a|\\TtЧ\x8btH\xb6\xc3>\xb8\x9a#>@\xab\xa3\xbe\x82]\xe9d\x8e\xf9\xe8\xf7\x8e\xb3\xda\x13\xa8\xf0\xa4\xb6x\n\x1d=\r\x12\xfd\xa9\xf5\xfe\xe5\x03J=\x03*\x9d%\x05\xce\xf9\xb0;\xe7<\x1e\xbe\x80\x87.\xa2\xbc\x84.]Fy\x857]\xf5\tξ\xe6\xc3\x1b:\xae\xfb\x04\xd9\xdc\xf0I<~\x93\xbe\xe2or\xf4\x1f\x1f\xf7Q\xfcK\xb9\xbb\x85\xe6n\xa3\xbc\xe3â\xaf\xbb\xaax\xf7|@\xb6\xf7!\xdf\x0f|\x00\"\x0f}\fR\x1e\xa9\xad\xfc\xcfG_\xfdX\x8d\xcf\x13\x88\xf1S\x1f\x13-\xb9\xfc\x92\x13\xcb\xed\x97\x1e\xe7\xf1K_\xf2\xfa\xa9\xe2\xf9\xfc\f\"\xf3\xfb\xe1N\v\xf8\x89\xb6\n\xfais\n\xf9a@\n\xfb\x11y\x14\xf1\xb3C\xcf\xf8\x05_\x14\xf5\xeb\xa9\x00~P\xbc\x98\x1f\x00\xad\xb8\x1f\xb1\xcas~\xcem<\xef窑\x12~\bhI\xbf\x88\xf1\v\xfa\xe4\x8b~lP-\xe5\x87j\xbc䇾\x94\xf6\xf3\x8d\x84e\xfc\x8c\xb8^\xf6\xcbH\xcajӯ\xf8\x93|}U\xbf)\xe7\x87I-\xef'jHaC\x15\xfcb\xda+\xfa\xb1\x82\xfe5?H\\\xc9\x0f\xc6V\xf6\x93t\xaf\xfb\x11漁\xfa\xab\xf8\xa1\xf0U9\x967YI5?\xe6!\xfd\"\xfd5\xfcb\n\xde\xf2#\xa5R\xd3O\xf5}\xdbO%}\xc7/\xfc\xaf\xe5\xc7,\xa4\x9fz\xf7\xae\x1f{?\xea\x80\xe8u\xfdd\xf6{~\xda\xc3z~,\xd5\xf1c\xa9\x0e\x9bn\xc8\xe17B\x9b\xef\xe3\xa9\xc6\xec\xfe\a\xda\xe1&zm\xea\x87<|\xe8\x17Yi\xc6\x1e7G\xf7Z\x90\xf8-\xd9\xf4GړVJ\xa9ָ\xa7\x8d\x9fq~[<\xde\xce\x0fqj\xcf\xc6;\xf8\xf1\xb2F\x94\x9d\xfct\xae\x9d\xfd̺v\xd1kW?]~7?\xf1Jw\x15\x95\x1e\xfa}O\xbf:\xf9^\xfa@o\xfd\xa1\x8f^?\xf6Sd?\xd1ϟjE}\xd9\xf9~\xbc\xf4\xf7#k\x94ꗰj\x80\x9fk0\a\xeau\x90\x1f[\x83\xd3\xfc\x92 \xfaL\x9f\x1f\xec\x87\xdd\x1b\x82/\xd3\xfdbӇ\xfa\x19\xe0\x0f\xf3\xf3\x00r\xbf\xe6T\xfdX\xe32\xc2/\xd0#\xc3/\x18/\x13R0\x92?\x8c\xf2#\xab7ڏ\x8cB\x96\x1fj\x9d\xedG\xc6n\f87\x96\x022\xce/\xd3n\xe3\xfd\xa2\xf0\x13 q_\xf8\x05\xc8|\xe9\x97T\xf8W~\xc0\xa9\xaf!?\xdf\xf8\x11*|\xeb\x87\x13\x9d\xe8\a\x8c\x99\xe4G\xc0\xfd\x9d\xca\xeed\x0e\xeb{\x15\x92\x1f\x12\x03\xcb\x18\x9e\x91\x92\xc3\xc6\f\xbc\xd9\xc8\x1aL\x90\x10\xb3\x1f\v&,~\xb8>\x946?\xce\\\xb7\xfb\xb9\x0e\xc3\xe1/\xd86cHꄴ\x14':\xe5\xf2\x13\xa5\xb8U\xd3=~\xcc\xeayU\xdb|~\x18S\xbf\x9fӂ\x01\xbd;\xe8g\x04\x1f\xf2s\x1dA\xd8\xcf\x104\xe2\x17\xb0\x1a\xf5\xab3\x8c\xa9\xcc\xc4\xfd\xc0\xb7S\xfcbϧ\xfa%\xc8\xfe\x91=\x9d\xe6Ǥ\xf5O~\xe6\x1d\xa6\x8363\xfcH\x16\xcf\xe4e\x16\xc4o\xb6\x1a\xa690WsQ\xd3<\xe5\xdf|?\x03\xd0\x05`\xe0B\xfdv\x91?o\xc7\x11)\x8bI\x9e%~\xb8\x99\xa5\xe8\xdf2?R\xb9\xcbم\x15~\xa4\xbaWBBV\xf9\x05\x96\xadf\u007f\xd7\xf8\xf5D\x00?\xdf\xda\xe8\a\xe6[\xaf4\xd8@9\xd8\b\xc2o\xd2F\u007f\xf6+x\xdc\xec\xd7\xe3\xc6A\xe5\xad\xd4\xca_\xd4>l\xf3\xd3Yn\x87\xac\xfd\xea\x97\x1d%;\xb4\x86\xdf\xfc\x88\x0fv\xd2\n\xec\xa2\x04\xee\xf6\vf\xdcC\xb3\xbb\xd7\xcf<\xce\xef\xe8\xf1>\xde\xf8\x87\xf6j\xbf\x1fn\xef\x80_Ò\x83\xd4\xe4C\xa4\xf3a?\xa0\xe9\x11?R\xb5G\xf9\xe81\xd4s\x9cD9Az\x9d$\xbdN\xd1R\x9ef%\u007f\x828\u007f\xf1\xa93T\xaf\xb3\x18\xc29\f\xe1<\xfe\xbf\xc0\xdf/\xb2\xe7\x97\xd0\xf3\xcb\xec\xd5\x15\xfet\x95\xb5^cS\xd7\xfd\b\xb5n\xf0rSI\xf47\x99\xf3\x8f~\xfc\x17T\xbeEɿ\xed\x17\xb7{\x87\xb5\xdc\xf5s{\xce=\xbf\xc4?\xf7\xfdLQ<\xf0s\x16\xe9\xa1\xca\xf1#\xde\xfc\x9f\xfe\xfc\x98\x15=\xe1\xe5\xa9_\xa3\xf8\\\x01\xc1\x18\xb9\x038c<\xa0\xaf\xdf\b\xd0l\xe5\v@L\xf2\a\xf4\xde\x02\x01\x9epW0\x80\xa1\x15\n\x88K-\x1c\x10\xf9-\x82\xff\x9f\t\b\xdc)\x1a`B\xf0\xd9\x00\xa2\xefb\x01q\x95\xc5\x03\xdc\x04\xf0\\@\xfc\xc4\xf3\x01\xa8\\\x89\x00\xccC\xc9\x00f!^\b\x88,\xbf\x18\x80\u007f)Ŷ_\nH\x94S:\xa0\xdb\xff\x03\xb2U\xed\xe5\x00\xadf\xd9\x00\x9c\xc0+\x01Q\x96W\x03\x14\x86r\x81\x04\xfe+/EJ\x00\x94\xaf\x10\x00\b\xae\x18\xc0R\xb3\xd7\x02\\tU\t\xbd\xae\xac\x9f^\x0f`Q\xf9\x1b\x01\x80\xe9*\x01n\xfe\xaf\x1a\xe0\x0e\x9b7\xd9N\xb5\x80\xb0\xb5z\x00&\xa8F\x00\xa1\xc7[\x01HDM\x8e\xf4m\xfe\xf6N@\xd8R+@\x9b\\;\xa0\xce\xe0\xdd\x00\xf9R'\x00\xdcW\x17\x84\u007f\x8f\xa4\xac\xa7C\xaa\x1f\x006n\xc0K\xc3\x00N\xa8k\x14@\n\xfc}~\xd98\x80\xb7o\xa0l\x12`\xb4\xd2T\x19\xf7!\xefl\x16\xe0\x1eG\xd6\xdd\"@-k\x19\x90\x05\xf8\x1f\x050\xf3\xde*\xa0\xdbT[\a`\xe8ۀ\x1em\x038\xc0\xad]@\xa2\x8f\xf6B\xc4\x0eRtd}\x9d\x02\x88\xf0;\a\x10\x90w\t\x10?v\r\xe8&\x9an\xbc\xab{\x80\xe2\xdb#\x80x\xbc'*\xee\x15` \xd7;\xc0\x85\x19}\x02t\x80\x1f\xeb\xf5\x93\x80ڐO\x03\xb2\x17\xa2/)\xde/\xc0\xc4b\xff\x00\xf35\xa9\xa8k@\x00\xc9ׁ\x10\xabA\x01\x84\x16i\x01\xe4r?S!\x18\f9\x1c\x12\x80\xe6\xa7\ahy\x86\x92W\xc3\x02z\xb8x@&\x1e\x86\a\xe8wF\x04\x80\x842\xb4\xf3\x99\xa4\xfcȀ\xc0\x99Q\x01\x98\xa7\xd1\x01D\\Y\x01N\x8fdCe\xc6(\xe5Ǫʌ#\xcd\xc7'Uf\x82\xaa\xcc\x17Џ/Q~E\xe1\xff\x1a\x9d\xfc\x86\xb2\xff-F3\x91\xfa0I\x15\xe8;ՙ\xc9Й\xefљ\x1fT\x15r\xa0\x18\x06\x95\x1b#\x85\xdcD\x014\xab\xe8Y\xd8\x19\xab\n\x9e-\xc9s;\xc5\xdd\x01r:9T\x17\xa9㦐y\xf8\xa4W\x9f\xf4\xe9\xd5\x0f\x11\n@<\x82\xca\xff\x90HH8)\x04\x11\nAT\xbe\x8c\x91`q\xf2k\x8a\xb2z\xaa\x8a\xc0\x8fd\xd94\xb2\xfa'\xe5\xcatpe\x06\xfb4SI=+@\xbc6;\xc0\x9c\xf5\x9c@\xc1\x8e\xa32\xe4M\x1bs\xa1\x93\xf3\x02\xc0\xbf\xf3\xb5\x8e\x05z\xdb\u0080@\x93E\x01A+\x8b\x03\x98\xf5\\\x12\xc0&\xef\xa54\x06\xcb\x02t\xe9\xcb\x03\xba\xdd1 .ve@L\xfe\xaa\x80\xac\xbeXM\x1b\xb2&@8\xb26\x00\x04\xb3.\x80I\x8c\xf5\x01$\xd46\x88\xd2d\xa4l\f`ZvS\x00\b\xfb\xe7\x80`\xd8\xcd*\xc0[T\x80\xb7\xea\xf5\x17\u07b4M;\xba]\xaf\xbf\xeau\x87>\xf6[\x00>hg@\x02\xa8]J\x84\xddz\xd3\x1e\xfd\xbcW\xfb\xf6\xbb\x8e~\x9f\xfe\xfe\aF\xbf\x1fC:\x80\xc1\x1cd\xb7\x0fq\x10\x87ه#ڥ\xa3\xfa\xd81\xbd\x1e\x0f\x00\x83\x9e\xe0\xa8N\xf2\xd3)^N\a\xf8\x02\x0e^\xfe\n\xe0\x05\ng\x02\xe2\xaeΪ>\x9cC\x8b\xe7\x03\xc0x\x17\xb4k\x17\x03XSu)\xa9\x1c\x97\x03\b~\xae\x04\xe06\xafBʯ\x05\xe8\xe9\xaf\a\xf8\xae\xc6\x00\xdc\xd5Mv\xe3o^\xfea=\xff\xf2\xb7[\x01._\x854\xdf\tpq\xfc]0\xf1\x1e\xd5\xe2~\x00^\xf7\x01G\xfc0 \x01\xf7\xa3\x00\xe6\x01\xffC?\x1f\a\x10\x02<သ\x06\x90\xfd\xca\x15\x84\x96\xe4\x0e\xa2wy\x82\u0529\xbcA=#'\bU\xcb\x1f$\xdc+\x10\x04Q\v\x069'[((m\x14\x0e&7?\x06u%\x8e^\x8b\x06%\x89\xf1l\x10\xbaR,(\xee\xa2xP\xba\xff\\\x10\xb4|>(3Z%\x82\xf9:$\xa8]2\x98Х\x17\x822\xd3\xfdb\x10\xae\xa3T\x10ٱ\x97\x82\xb2ιtP\xc8V&\x88\xae\xbf\xccn\x94\r\xc2R\xbf\x12\xc4\xcc\xe7\xab\xfc\xad\\\x90~\xa0|\x90Y\x8f\x14\xedz\x85 m^E4\xf1ZP\xccZ\xa5 \xe6\xa7*\a\xc1\xef׃\xc9\xe3T\x83P\x87*A=\x15Gz\xf6f0\u007f\a\xf9P\x8dmV\x0f\x8a[\xab\x11\xd4\x14\xd4[A\x90\xb9f\x10\xd6\xe5m\xed\xc2;A\xac\x82\xac\x15\x14\xcf];\xc8x\xfd\xdd \x92\x02u\xd8FݠX\xc7\xf7\x82\x88Z\xea\x05i\x10\xeb\a\x89N\x1b\x80>\r\x83\x98\xd1m\x14D\xb8\x18\x84\vn\x1c\x04\x8f?\bBԛ\x04\xc1\xf8\xa6\x18ӇA\xe0\xa1fA\tR\x9akM-\x820N-\x83\".\x1f\x81\x94\xad\x82\x98\x1an\x1dD\xe8\xd3&\x88)\xa5\xb6\xac\xbe]P\xed\\\xfb \xb3x\x1d\x82\x10ӎA\x80\x96N`b\xe7\xa0hB\x97 \x14\xbe\x8f\x83\x8cw?\xa1\xa8|\x8a\xbe\xf7\r\x12g\xf7K>\xdb?\x88\xdcPj\x90K\x1f\x06$8\"_\x0f$\xdf\a\x05\xa9\x92i\xe8\xd6gA\xa8\xe4`%\xfa\x90 T0\x1d4\x1cJQ\x1f\x062|Α\r\x0fB\xe5F\x04\xa1k\x19\xca\xc0LV=2\b\xbd\x19\x15\x14%\x1a\x1d\x84\xdad\x81\x06ِ\xf01\xa4\xc1\xd8dG\xc7Qi\xc6C\xe2'\xa8&}\x81\a\xbeTi\xfc\n\xac\xfd\x9a\x12\xfe\x8d\bط\xe8\xceD*\xc3$j\xc0w\x94\xecɔ\xb9\xefU\x82\u007fP\xb9̡\xd4\x18\xc83#$\xc9D\x890+\xed,\x10\t+\xe8iC\x93vJ\x8e\x83\xdcw\x92\xfb.\x8a\x87\x9bB\xe0Ir߫\x14\xf6\x05\xb9j\xd2ϖ\x02\xecU09\xdc\x10y\x19\x0e\x8a\a\x8a\x04%\xef\x13\x85\xa0\xc5\xd0b<(\xab}\xa6\x04\xe1j\xa7R\xd5~T\x02O\vJ \xfbSP\xd2\x15Ӄ\xe2\xe4f\xe8 g\x06\x91B\x9b\x15\x04\xbc\x9a\xcd&\xe6\x04\x01_\xe6*W\xe7\x05e\xceu>\b\xb7 H?\xb1\x90\xed,\n\n\xc8^L)X\xc2\xcbR\xb2zY\x10xiy\x10\xf6tE\x10\xc9ʕA\xa4\xbfV\xb1\xb9\xd5A\xd9\xec\xb5&(\xaee-Eb\x1d\xfb\xb3\x9e\xb7o\xa0\"l\f\nV\xd9\x14\xe4:\x1b\x8cv3eeK\x90\x9b\x99\xb7\xb2\xe9_\xc0\xfcmA\x99\a\xd9\x1e\xe4\xeb\x8a\xf1aGP\xc1\xcaoxzg\x90i\xbc]A\x02\xc9\xddAF5{\x82\xdc\xec\xb37(~\xeewv}_P\xd6B\xfd\x81\xaf\xf6\a%\xc1t \xc8\xdd'\a)\x80\x87\x82\x04Q\x87\x83L\xdc\x1c\t\x02\x1d\x1c\x05\xa1\x8f\xa9\x91>N\x19?\x01\x8e\x9ddwO\xf1r\x1a\x02\xff'%\xe6/\x10\xfbL\x90A\xc3Y\xaa\xf79\x92\xe3|\x90\v\b.\x04yD\xc5\xc5 3{\x97H\xea\xcbA\x02\x9c+A\xc6!W\x93\x83\xbe\x06\xf5\xb9N#p\x03\x14\xb8ɱ\xfdM\xaa\xffC\x95\xffW\x1e\xcc\xceN\xb9E\xb2\xdf\x0e\"\x82\xbcÎ\xdd\x05\x01\xee\xa9\xed\xbc\x1f\x04\xd0\u007f\x10䲣\x87A]\xb0\xff\x88r\xf1\x1f\x84\xf1q\x90\x88\xe8I\x100\xf0)\xb5-W(\xa1\x86\xb9Cj\xc8\xf2\x84 \nyC\"\x9b\xf9Bؽ\x11\xd2\x03\xc6C\xe4F\xc1\x10\x82\xedB!Ҥp\b\x13\x8cEB \xcd3!\x04)EC\\\x12\xf9lH0e\xb1\x10\x82D\x94υ`\xec\x9e\x0fA#J\x84\x00\xafJ\xf2\xcb\x17B\xf0\xe8/\x86\xe0\xe7J\x85$e\xf5\x926S:\xc4%\xbfe\xf4\xf3\xcb!\x02\xff\xb2\xac蕐\xfa\xa4WCԱr!d(ʇDMRB\x02j+\x84\xf4T\xd5\x10\x10\xe5k\xda\xc7J!\xe8w\xe5\x102\n\xaf\x87\x84\xfbo\x84\x80!\xab\x84 \x11UyǛ!X\x99j\xdar\xf5$\xcdj\x84 \x0eo\x85\xc0\x98\x9a!\xa6B\xdf\x0eQ*\xde\t\x89\xf5\xad\x15\u0092\xe4\xda!\xc9(\xbc\xcb\xe1\xd6\tQ\xdc\xeb\x860}\xf5\x1eiQ/D\xe7S?$\xf9\xcb\x06\xac\xbba(\x99\x16n\x04\x9e\xbc\x8fJ\x1b\x87 '\x1f\x84ԃ7\t\x89\xc5k\x1a\x92\xa9\x9a\x0fC\x00\x1b\xcdBD\x80͕|-B\xa2\u007f-C\xf0\x88\xac\xbb\x15H\xd4:$\xd1K\x9b\x10V\x89\xb5\r\x89P\xb6\v\t\x04i\x1fb\xa2\xaaC\bY\xec\x8e!\xda\xd8N!\x846\x9d9\xe8.$T\xd7\x10O\x00\xef\x16\x02\xc2\xe8\x1e\xa2\x13\xe8\x11\x02N\xe8\xc9a\xf7b\xaf{\x87\xe8\x13\xfb\x844m\x1a\xc2\v7Bb\xba?eu}C0|\xfdH\x9f\xfe|<5D\xe30\x00\x84\x18\xa8T\x1e\x14R\xb3\x9c\x16\x82#\xfc\x1f\xbd\x06\x87DO\x86\x80^\xe9!N\xd5\f\x05i\x87\x85\b\x86?\x0f\x89\x8a\x0f\a\x15F\xb0\xe1\x8c\x10483\x04\xd39\x124\x1b\x15\x12\xeb8\x1a\x94\xc9B\xdb\xd9!ڝ1!ؒ\xb1\x90\x9cq!13\xe3\xd1\xea\x04v\xfc\x8b\x10\x94\xfbK\xd6\xfdUH\xec\xd7\xd7!تo0\xe8oC\xb0P\x13\x95ғС\xefB\xb0\x05\x93\x95\x86ߓ3?\x90x9!\x18\x0f\x83*\x83Q\x87b\xc2\xc0\xccI\xc1\xb4\x90bV\xea\xb4-\x04\x1ba\x0fA\xed\x1d\xd0F'J\x17\xb4\u052dz\xeeQ\xbd\xf0\x82\xfd>ʑ_[\nPf\x82Ԉ\x10E6\fM\x8d@\xee\xa2\x14\x88\x98*I\\\xa5y\x8a\xca\xe1T\xf6\xffG\x15\xc7i*M?\x91R\xd3\xc1\xbc\x19\xffc\xde\xcc$Wg\x85\xc4\xcd\xcf\x0e\x01=\xce\xe1e./\xf3\xc0\xb8\xf9!\x9cȶ\x00\xf4\\\x18\xc2\xfcҢ\x10\xd28\x8bC\xb0\x9bKBpQK!\xfa\xcb\xc8\xd9\xe5\xb0\v+B\xe25W\x86$9\xb1\x8a\x84]\x1db\xaeaM\biõ!\xac\xda[\x17\x92\xc4\xe2\xfa\x10\xcc\xe7\x86\x10\x0f\x86٨\x94ل\x8a\u007f\x06)7\x87dc\xec\x16\xf4lk\b\x19\xe7_Bȹn\x83\xfclWN\xff\xaa\xb6b\a\xbe\xfd-$q\xc5ΐ\xf8\xd0\x11)\xbb(\xfc\xbb\xa1\xd3{B\f\x1a\xf7\xb2\x96\xdfCX\x15\xb2\x0f\xd6\xe4\x0f\x15\xc3\xfdl\xe8\x00\xc6t\x90O\x1f\xd2\xca\x0eC`\x8f\x84\x80\xa9\x8f\xf2rL\xc7x<\x84\x99\x88\x13\xfa\xf1d\b\xcb4O\x85\xb0$\xfa\xb4J֟\xb4\xf2\u007f\xf1r\x06m\x9c\rqQĹ\x10P\xd5\xf9\x10^\xbd}!\x84\xb5\x8f\x17CpZ\x97\xf8\xc0ev\xe7JH\x96\xa7\\\r1\xf9~M5\xf9zH\xd2]7\xf0\xdbMV\xf27\x1a\xf8'\x84\xf9\x82\u007fC\xe2\xe5o\xb1\xde\xdbЯ;!q\xa1wCt\xc0\xf7\xa0\xa0\xf7C\x9c\x11x\xc0\xee<\xa4\t\u007f\x04\x91\xfaO\x9bz\f\x19OMy\x12bR\xe0i\b\x93z\xb9\xc2\xd2\\\xee\xb0hO\x9e0\xba\x9a7L-ȇ\x9f\xf2\x87\xf9|\x810\xfdm\xc1\xb0.C-\x14\xa6O)\xac\xb7\x17\t3%\xf4L\x98n\xa8h\x18\xb8\xf9ٰ\x04N\xc5\xc22SP<\x8c\xc4\xf0saQ\xbd\xe7\xc3\\\xbeW\"L\xb0R\x92\xad\xbf\xa0\xed\xbd\x18\x16\xee\x97\n\x03\xa1\xbe\x14\x06^-\xcdK\x99DK؏\x11\x16\xc3]6\x8c\xdc\xc7+a\xaaݫz-\x17\xe6\xf1\x16\xe5ô|)a\x02\xb4\nan&\xa8\xc8\xe6^\xe3ӕ\xc2<\xf2\xb8r\x18\x00\xfdu^\xde\bCت\x84\x99L\xab\x1a\x16S\xfa&\u007f\xab\x16\x06\xbe\xa8\xceO5xy\x8b\x03\xac\x19֔\xfd\xdba\x1c1\x8e\xb2\x16h\\;\xcc<\xc0\xbbaaf\x1d\xedl\xdd0\xe6#\xdeSR\xd6\vÒ\xd4\xe7\xb7\rº?\xa1!\xeai\xc4o\xdf\x0fc\x06\xa1qX\x98\xfc\x01(\xda$\f\x10\xd14\f\v\xf5a\x18AD\xb3\xb0\xa0\xf4\xe6aI\xab\xb5\xe03-A\xb7\x8f\xc20\x91\xad\xc2T\xc8\xd6a\x05Nm\xd0J\xdb0T\xbd\x1d*n\x1f\xe6\x11\x86\x1d\x92\xf7t\fs\x1f\xa3\xb2\xbas\x186\xa5K\x98\x96\xaekX\xecA70\xb0;\xfa\xd7\x03-\xf6\fsr\xb8\x17[\xee\x1dV7\x88\x06?\x0ec\xd1\xc3'a\xc4j\x9fⱾ\xe1|-F\xa5\xa7\xf4\vS\xbc\xfb\x87\xe1FR\xc3\\\x1d0\x00\xf7\fDՃ\xc2\t(\x97&\xc5ga\xc8\xf5\xe00s\x8cC\xc2\f\x04љ\xa1$а0a\xe9\xe7aĀ\xc3\xc3\x04##\xd0팰؋\xcc0\xd0\xffH\b\xef(|5:,S\x15Yaj~v\x18\x01ʘ\xb0L\x10\x8e\r# \x18\x17.\xd8!uxꄄ\x13\f\xe7\xef\x961(#eBX2\x01_\x84\x81\xba\xbe\fcU)\xe5\xe6\xeb0^;\x15&`\xff\x96#\x9b\x18\x16c8)\x8cɲ\xef\xc2L\xd3O\x86\xa4|\x8f\xce\xfd\x80!\xe7\x90\xfa\x86\xb0xZ\xa3RÄg\xcd\xec\x89%\x8c\xb3R\xadh\xc4\x16֣\xc4Ì\xa2\x1ca\xf8\\g\x18^\xc1\x85\xea\xdda,\x12\xf0\xa0JoX\\\x8e/\\\xb0m\xea\xd0\xf4a\xa9)\xfe0\xb3?\x810\x02\x99`Xf\xdeB\xa4e8\x8c\x95U\x9102\x1fѰz\xadXX\xacT<,\xd1\xde\x14vwj\x98\xb6\xef\xc700\xc1\xb40\x9c\xfcOԿ\xe9a8\xc7\x19\xda\xd2̰8\xb3Y\x10\xbe\xd9aF\xd0sX\xcd\xdc0Q\xc1\xbc\xb0\xa4u\xe6S\x19\x16\x84\x19D.\f\x8b\x9bZ\x14F\x0ebq\xe2\xc1\x8cQ\xa9)K\u0089\xd0 +-ei\x18\xd6~\x99HSB\xaa\x96\xd3>\xad\b\x03\x14\xac\x84\xb1[E\x03\xb3Zub\rX\xbe\x96\x12\xb5.\f\x10\xb1\x1eҺ!\x8c\xb0}c\x18g\x01l\"\xb9\u007f\x0e\xd3/n\x0e\v|\xd8\x12\x16h\xb0\x95\xcc\xfc%\xcc9\xd9m\xa0\xf6\xf602忒Y;`\x18~c+;y\xd9\x15\x86\xa3\xda\x1df\x9edO8\u007f\xf7\xc1\x89!\xed\r\x17h:Z6;\xfd\x1e.\xd25{\x80\xbc\x959\xf1a_\xb8pۄԍ\x90\xe3\xe0\xfe\b3\x1d\xb3?,a\xf9\x81\xb0\xf8\x97\x83\xa0\xe3\xa10S\xf9\x87\x93<:\xc21\x1c\rs1\xdb1\xc8\xd6q\x15\xa7\x13a,\x8e?\tQ8\x15f\xfcs:\xcc0\xef\xcf0B\x9d\xbf\xc2\b@Π\xa9\xb3`ٹ0\x9c\xdeyh\xcc\x05ZËIkx)L\xbc{9\x8c\x05jWT\xf8\xaf\x86\x81\x80\xae\x81\f\xd7u\xc47\xd4\x14\xde\f\xf3\xe5\xfd*\xc3\xff\x84\x13\x00)u\xa0\xbcq#\\\xa0y\xc6\xd0\xc4s\xb7\u0085\xbaf\x0fK\x1b\x9d\xf8\xf7v\x18p\xf4\x8e\xea\xf7]Z\xc0{\xfc\xf6>E\xf4\x81j\xc1C\x8a\xe1#8\xa6\xff\xb4\xb5Ǡ\xc1\x930'\xa1\x9f\xea5W\x84.$w\x84\x1e/OD\x8ep\xcd\x1b\x81<\xe6\xe3%\u007f\x04g\xc5E\xf8\xee\x8d\b\x02\xaaB\x11\xac&/\x1c\xd1\x00\xadH\x84\x1e\xef\x99\b_E\x15\x81h<\xcbK\xb1\bmi\xf1\x88(\xfas\x91\x84){>\u0088\xbdDD\x84\xa6d\x04\x93X/\xe8}/F`TJE`9^\x8a\x88\xa1*\x1d)\xd4>{TFBkS\xcaDp\xb8\xddˑ\x82\xadRG\xa5&\x94\xb8l\x04\x16\xed\x15}\xfc\xd5\b\xa6)\xcaEDH\xcbGh\xd4R\"\x00\xb5\x15\"p?\x15#\x02\xc6_\x8b\x80z\x95x\xa9\x1c\xe1Z\xd3H\x9e>)oD\x18\x80T\x89\x88=\xa8\x1aA\xe8\xfafD4\xb0Z\x84\xf0\xa4z\x84\xfe\xadFD$\xf0-v\xa1&\xbb\xf6v\xa4p\xab\u052c\xd4!\x89\xbf\x94w\x942\xb5\"\x02\xa4kG$\x8f\xf6n\x04Z\\'\x82\xb5\xbeu#\u0099\xf7\"\xb4!\xf5\"bJ\xeb\xebX\x1a\xb0\xdb\r#P\xdeF\x11\xe4MI\xd5\xc6\x11\x82\xb8\x0f\xf0x\x13\x94M#\xe0\xfc\x87\x11\x9d\x9bk\xc6[\x9bG4\v\xd6\"B\x15j\x19\xd1\xe0\xf2\xa3\b\xd7'\xb7\x8a`\x99@\xeb\b^O\xd1&\x82M\x0emѕv\x11U\xaa\xf6\x111\xc3\x1d\"z\x9ax\x04\xd1T\xa7\x88\x18\x84\xceI9\xe8\x12\xc1\xc6\u052e\x11\x06\b\xdd\xf4\xda=\x02wԃ\xf5\xf6\x8c \xe8\xec\xc5\x1azGx\xfag\x1f\f\xee\xe3H\xbe\x16\xa3\x13\xbe2ѯ\xf1\t9\xfb4\x02_\xd47\xc24a\xbf\b\x04\xbd\u007f\x84\x80'5\x82\xf7r\r \x91\x06F\xe8t\aE\x105\xa4E`??\x8b\xe8Z\xcf\xc1\x11Q\xc2!\x11\xd5\xd8\xf4\xffǣ\xa1\x91\x84ꥧ'\f\xea\xb0H\x81\xf6\xd9Y\xa3\xb3S>\x8f`\xae\x11T\x1d\x11\x11?\x9c\x11A\xee!\xf3\u007f\xf7\x8eD\x85\xa3\"\xb0\xf2\xa3\xf1!+\x82\xad\x99\xd9\x11\xe2\xbe1Iҍ\x15\x99\x1f\x17\xc1\xdaS\x1d\xca\x04<\xf0ED_L\x05\x1d\xfb*\x02\xa0\xf05Z\xfd&\x02\x13\xfam\x04^~\"\x1e\x9e\xa4\xd2\xfc]D\x90\xcf䈸\xe0\xef\xc1\x96\x1f\"t\xa99\x11=`\x1c\xf7\x1bY\x87)\x02\x17h\x86\xb2Yp\xbf5\x02\x17i#A\xed\x11\xf1\xa6\x8e\b\xbd'\t\xe7\x8a\xc0\v\xba#p-\x1e\xda\x02oD\x9c\x9f/B_懵\b\xb0\x8e\xa02%\x84އUL\":\xc0hD\xfcM,I\x908y?%B\xb72\x15\xc4\xf8QUe\x9a\n\xffO\xf8v:\xd5s\x86\xea\xc7\xcc\b\x1cҬ\b\x9c\xc9\xec\b\xdc͜\b<\xdd\\4=O\x85|~\x84^aA\x84\xc6x!4uQ\x04&yq\x04\x06~I\x04F}iR*\x96E\xb0k1\"f\u007f\x85\x92re\x84~cU\x846~u\x846x\r;\xb66\"\x8eh\x1d\x05u=\xed\xe2\x06X\xba\x8d\xc9Z7\xf1Οi$6\x93\x94[h\xf4\xb6\x8ah\xfcB=\xdd\x06\x06mO*ӯj\x94w$\xd5\xf9\xb7\b\xad\xf6N\n\xfc.\x98\x94\xdd\x18\xd6\x1eX\xaa\xbd\xb4a\xbf\xd3`\xedSi\xf9C\xe9\xb9\x1f\xc6\xe7\x80ڍ\x83T\xf9CT\xf9ô0Gh\x92\x8e*\t\x8f\xb1\xabǡ\x9a'\xa8\xbd'U\xa7OQ\x8bOS\xe3\xff\xa4v\xfcE\xdd;\x03ΝU\x85<\xa7\"\u007f\x1e\x04\xcdH\xb9\x10\x11\u007fz\x11\x83\xbd\x84;/G\xe0N\xafD\x04\xf8\\\xa5\x0f\xb9\x86߯+\xf1o\x80\xb57#̌\xff\x1d\xc9\xd7!=#\xe5\x1f\xb6\xf6/\xc9{K\u007f\xbc\x1d\xe1V\xc7;\x14\xf1\xbb\x11b\xe0{\x11\xb8\xdb\xfbp'\x0f\"@<\x0f\xd1\xc6#\xf2\xee?\xb2\xe7\xb1R\xeb\tk}\n\xcd\xcc\x15\xc5\xc2\xd8\xdc\xd1\xfc\xbd\xa57y\xa2 E^^\xf2EE{\xf2G\xe9J\vD\xa9\xfc\x05\xa3h\xbeP\x14\xa3)\x1c\x95%\xc6E\xa2\x1aa>\x13%k\x8aF\xd9\xe7g\xa3h\xbbXT\x8e\x81(\x1e\x95\x9d\x93\xcfE\x91Lz>*\x99\xa5\x12Q\xcap\xc9(\f\xcc\vQ\xbcx#\n\aY\x8aϾ\x14\x15J\x96\x8eRD\xcaD1ڗ\xa3\xe2\xb3\xcaFI\x82W\xa2x\xe7FT\xccz\xb9(\x81`yޘ\x12Ed_!*(\xb9b\x14f㵨(C\xa5\xa8n\b\xac\x1c\xc5i8Q\u074b\x11\xa5\x1f\xac\x82\x81U\x8d\"\x19\xf8&n\xa9\x16M\x98\xdc\xf4\xc1\xe9)գ\\\x84P#\xca$\xd4[Qq\xc95\xa3bCގ\"\x92\u007f'*9\xe9Z\xecDm\xd2\xea\xdd(\x97EՉ҆ԍ\xc2AFah\xebE\xe1\xe2\xeaG\x11L5\x88\x8a(5\x8c2\x8am\x14\x05\xd4~?*\xb6\xbbq\x14ǹ|\x00\n4\xe1\xedM\xb5\xc6\x0f\xa3\xc8&4\x8b\x12\xfc5g\xf3-\xf8x\xcbh\xa1D\xc87H\x80\xf4Gф\xa1\x1b\x96\x9e\xd2*1<ِ\x11\xe5\xce\xf26dM[\xf4\xab\x1d\xc6\xdc^\xb9߁<\xe9\x18\x15I\xee\x84\xf1v\x8e2.\xee\x82\x1b\xbbF\xc5\xfcv\x8b\xc2\xfeu\xe7\xa5GT\xccEϨhY\xaf(LYo0\xa2OTR\xfc\x1fGix>\x89\n\x90\xff4\x9a0\x18}\xa5\xe8\x17\xd5m\xfe\xfd\xa3\"өhm@\x94\xd6i`\x14@vP\xb4P\xeba\xa9\xa3\xb2\a\xa7\xa6\xa4E\x13QO\x02Mf\xa7|\x16\x15\xe708J\x971$\x8a8'\x1d254*qͰ(\xe7n>\x8f2c1<\nu\x19\x11%\xe6Ɉb\x15^f\x94\xba9\x12Ս\x8ab\xa5\xfd\xe8\xa8\xee\xe6\xc7\x00\xb21\x801\x90\x91\xb1*\x85\xe3\xa2b\xb0\xc6C\\'D\xf16\u007f\x95\x8f/\xa3\\\xa8\xf9\x95~\xfe:\n\xeb\xfcMT]\xf8\xb7xr\xa2\x92c\x12x\xff\x1d\xca\xc9\xfa\xdd\xf7\x90\xf0\x1f8\xf6\x9c(1\xbd\x01\x8f\x19\xa3\f\x06LQ\xfa\x1c3:h\x812X\xa3\x05Z\x8f\xceN@I[\xb4h\xdb\xf4a\xd9\xe9Y\xa9\x83\xe4\x14\x17;8\xec \x81\x9cQĈ\xae(2\x02\xee(a\x8b'\n@\xec\x05\xdd|Q\xd8q?\x18\x15\x00W\x82\xf8?$\xfc\nG\xe1Q#Q伢\xa0wL{\x15\a\xa1\xa6P\x01\xa6F\xb1-Q\xe95-\xca\\\xe4OQ̆O\x8f\"\xd31\x03\xb5\xcfT\r\x9b\x15E\xe2~vT֝\xce\xc1\xc8\xe6F\vu\x1b\x9d=XN\xc1\x9c\x17\xe5Y\n\xf3\xd5\xea,\x80b/\xd4g\x17i;\x8b\xa3\x9cIZ\x12\xe5*\xf8\xa5Q\x18\xc0e\xe8\xcc\xf2(6\"\xac\x88r\x12~e\x14\x10}U\x14xwu\x14\xa8z\r\xa8\xb5\x16\x95\xafS1X\x1f\xe5ތ\rQ\xe4\x066\x82?\x9bP\xfe\x1c\x15/\xbb9\n_\xb5\x85#ߊ*~\x81ll\x8br\xfd\xc6\xf6(N\x17\xfd\x15\x84܁\xf27<\xba\x134\xdb\x15\x15,\xbf[5{O\x14\xd1\xe5^}\xf6\xf7(w\f\xecC\x8d\u007fD\x15\a\xef\x8f\"\xb8=\x10\x15\az\x90=8\x04A:\f\v|\x84<>J\xbbp,\xca<\xedq5y'\xa2\xc8מ\x84֞\x8a&\xf3ڧ\xa3ܼ\xfb'\xf4\xfc/h\xe3\x19z\x88\xb3Q\xf8\xe5sQ:\xf4\xf3Q\x99T\xb8\xa0]\xbe\x88\x86/E1\x15p\x19\x95^Q\xd6\\\x8d\n\xba\xbe\x16U@p=\n\x18}C\x1f\xbc\xa9\xfd\xfa\x9b\xb4\xfb'\n\xa4\xf6o\x14\xbe\xf4V\x94\xc0\xeb\xb6^\xef\x80Xw\xa3X\x9c\u007f/\n\xa7}\x1f=}\xa0\x8d=\x84\x1a?\x8a\xf2\xf4\xa1\xff\xf4\xb9\xc7Q\xc0\x84'\xe8\xe4Sx\xba\\1\xe8C\xee\x18\xcce\x9e\x98hWޘ\xfc\x9e/&\xec\xcb\x1f\x03u\vĤ\xf6\x821\x82\x80B1\xd1\xf4\xc21z\x8f\"1\xa9\xe8\x99\x18\x13\x96Ec\x00\x0f\xcf\xc6d\xd9|\xb1\x18\xbbS<\xc6p\xf9\xb9\x98$\xf2\x9f\x8fa\x8fc\t\xd4Y2\x86\x15h/\xc4\xe0a_\x8cI\xbfK\xc5DL_\x8aq\xf7@\xe9X\x92+e\xf8\xe0\xcbZk\xd9\x18\xb1\xc8+1\xd1\xfbWc\"\x16\xe5b4\xeb\xe5c\xc4')1P\xb0\x02\x06S1F\xedx-FTQ)\xc6h1&r\xf2zL\f\xdb\x1b1\xaaW\x15\xf6\xa7j\x8c\x00\xe0͘(B\xb5\x18\x8dPu\xf4\xb2\x06\x1ex+F\xdf[S\xa9\xf1v\x8c\xde\xfd\x9d\x18\xe4\xad\x16\xfaV[\x1b~7\x06\x06ԉ\xc1\xb1ԍ\x89\xe8\xbc\xc7\x1b\xeb\xc5ē֏\xc1\x936\x88\xc1\xa75\x8c\xd1y6\x8aѷ\xbd\x1f\x13\xff\xd7X\x87\xf8A\x8c\x9e\xb2IL\xa4\xb3iL|ч B\xb3\x18\xbdT\xf3\x98ؕ\x16\xb1\x84\xbdj\tb\u007f\x14\xa3 \xb4\x8a\x89\xe6\xb5ƃmb\xb0\xf5mc\xb4\x81\xedbb\xfc\xda\xc7`\xad;\xc4\xe0\x1c:\xc6\xe8::\xc5\xe83:\xc7h\xe8\xba\xc4h~\xbb\xea\xf0\xba\xc5DĻ\xa3\xd5\x1e\xe0aOi\xbb\x17\x88\xd7;\x06{\xda'&\xf6\xe2\xe3\x18\f\xdf'1\xb1q\x9f\xc6\xc4\x02\xf4\x8dAu\xfb\xc5h\xb1\xfaǸU\x1f\x8c\x1b\x80;\x06\xc6\xf4\xd5\xfd\xa8:\rR\xfa\x99\xca\xc2`\xca\xe9\x90\x18\xd5(]\xafCcT\xafa\x14\x83\xcfA\x85\xe1`߈\x18Md\x86\nu\xa6\xd64\x92\x02轸Hh\xbd8U\xb5>oh\x10\a&h\b\xda6\x8a\xc3/\xbc\x1fg\xa64\x0e\xec\xf9\x01\x1ak\x82\xb2i\x1c9\xab\x0fQS\xb3\xb8\x98\x8f\xe6q$\xc9Z\xc4\xe1F[\xc6\xe9R>ⷭ\xe2\x9crm\x1d\xe72\xc26 a\xdb8\x97.\xb7\x8b\x8b\xa1j\x0f\xe6t\xc0\xff\x1dQv\xc2]\x9d\xf1\u007f\x978\x14\xb0k\x9c\x96\xb5\x9b^\xbb\xc7\xc5/\xf5\x88\v,\xe8\x19\xc7\x01\xaf\xbd\xe2X\xda\xd0;\x0eS\xd5'\x0e\xb3\xff1\t\xf7\t\xda\xf8\x94u\xf5U\xba\xf5\x03\xa1\xfaǙ K\x8d\xd3^\x0e\xd0\xeb\xc08\xd4tP\x9c\xd83\x8d\xb5\u007fƶ\x06ǹ\x11?\x0e;\x9eN\xb2\r\x8d\xf3@\xb8aq8\xf4\xcf\xe3p\x80\xacfD\\\xd4*#.\x9e \x93]\x1b\x19\xe7;\xfa\xc1\xf7\xd1q\x98\xb6,eNv\\`Ҙx£\x8f\x8d\xcbK\xb6\xc7\xe1\xf9\xf1\xda\xc2\x04>\xfaE\x9c\xb6\xe3K\xb6\xf1U\x1c\xe6\xefk\x10\xe5\x1b\x8e\xf5[\xa5\xd7D\xd0k\x12{\xff\x1d\x06>Y\a\xfa=\t\xf6\x83\xd2!\a\xa42\xf0N#\xc6`\xd2f\xcd\xe8\xaa\x05\x1d\xb2Ɖ\x18mq\xaeڰ\xc7u\v\xa2~vR\xbc\\z\x9b[\xaf\x1e\xbd\xcd\x1b\xc7\xd6\x1a_\\\xd2o~\xedI\x00\x9f\x82zkHo\r\xf3\xd6H\x1c\xe9\xd5(e3F\xcb\x10\xe7\x97S\xe2\x00\xabS\xe3\xdc$\xf4#\x06;-\x8e\x18\xe2'2n:F2#\x9e7\xe1\x8af\xa2\x9c\x85r6\xca9(碜\x87r>\xca\x05(\x17\xa2\\\x84r1\xca%(\x97\xa2\\\x86r9\xca\x15(W\xa2\\\x85r5\xca5(ע\\\x87r=\xca\r(7\xa2܄\xf2g\x94\x9bQnA\xb9\x15\xe5/(\xb7\xa1\u070e\xf2W\x94;P\xfe\x86r'\xca](w\xa3܃r/\xca\xdfQ\xeeC\xf9\a\xca\xfd(\x0f\xa0<\x88\xf2\x10\xca\xc3(\x8f\xa0<\x8a\xf2\x18\xca\xe3(O\xa0<\x89\xf2\x14\xca\xd3(\xffD\xf9\x17\xca3(Ϣ<\x87\xf2<\xca\v(/\xa2\xbc\x84\xf22\xca+(\xaf\xa2\xbc\x86\xf2:\xca\x1b(o\xa2\xfc\x1b\xe5?(\xffEy\v\xe5m\x94wP\xdeEy\x0f\xe5}\x94\x0fP>D\xf9\b\xe5\u007f(\x1f\xa3|\x822ה<\xe9Y\xb9\x13Ÿ%OF\xd6s\x89b\xdc\xf3Srg\x94H\xfc\x93YrJ\x9e\xb4q/Lɝ\xf6\xe2\x94\xab\xb3\x94\xe3\xba$\xee\x19\xdfU\xfe\xcd언'\xd1\xe9\xee\xf2aT\x8f\xc4\b\x12,\xe8\x89˸^\x89\x81\fH\xef\x8d\x0f\x99}\xa6\xe4\x1f l\xf8?\x9a\xce:\xc0\x8d\x9b\x89\xe2\xfd\x0e\x9223333\xb7i\x03\xc5\x14\x92\xa2\xe5\xddH^/(\x1e9\xb7N\x99\x99\x99sP\x0esRfffffn?\xcd\x1b\xdd?\xf3{\xef͊\xbc\xb6\xef\xec&\xcd\x11\xc2\xf2H\xc4\xf1Qb\xed\xd1l+\xee\x18\xa0<\x96GV*0V\xf1%\xfe U\xb6\xcd\"\x12kc\xb6\x85\x1b\x05\x94\x9aG\x14\x06\xc6\xd6\x18q\x99p\x16\xd7al\x8a\xd1.\x03ʜ[\xcd\x02\xc6Z\xcc\xd8(G\xc36\x1a\x8c\x96#\xa0t|e\xab\tcnj\xeb\xc8\xfd\xf9\xba\x80\xb2\x1c\xd7\xeeт\xb1c\xc7u\xe6|\xae\xe3`\xe3\xe3\xc5\xda\x13\xd8V܉@y\x12\x8f\xa8\x9c\fcOa\x14\xeeT\xa0<\x8d[\xc5\xe90\xf6\fFӝ\t\x94gq\xaby6\x8c=\x87'\xf6\x9b=\x17\xb6q\x1e\xa3\xe5\xce\a\xca\v\xf8\xcaօ0\xf6\"\xbe\xd2ߚ\x8ba\x1b\x97\x8c\xeb0Y\xcd]\n\x94\x97\x8dk\xf7\xb8\x1c\xc6^1\xae\xd3#.\xaf\x84\x8d\xaf\x12k\xaff[q\xd7\x00\xe5\xb5<\xa2r\x1d\x8c\xbd\x9e/\xf1\xf7\xe4\x06\xb6\xcd\xe2F\xb1\xf6&\xb6\x85\x1b\a\x94\xdd<\xa2聱\xbd\xb8\xd2\xf5\x01\xe5\xcd\xdcj\xde\x02co\xc5\xe8Fy\x1bl\xe3vF\xab\xbc\x83/i\xdd\tc\xc7\xf3%\xfe,\x13`\x1b\x13ǵ\xfb\xa3L\xe2ZN\x1eזզ\xb0\xb4S\xc7u\xf01\xa6\xb1\x89\xa7\xc3\xd8\x19\xdeT\xdcL\xae\xe5,\u007fie6K;\xc7w\x9b\x85\x9b\v\x94w\xf9\xacY\xdc\rc\xef\xf1\xa6p\xf7r-\xef\xf3#\x8a\xfbY\xda\a|\x8d\xcb\a}\x10?ė\xbb\x87\xb9\x96\x8f\xf8\xa0\xf9(K\xfb\x18\x8fo\x94\x8f\xb3i<\xe1k\xcb=ɵ|\xca_\xd3z\x9a\xa5}f\\\xfb\xa8\x9a{\x96k\xf9ܸ\xb6Q\xb5\xe7Y\xda\x17|\xad\xb8\x17\xb9\x96/\xf9\xb8\xf22K\xfb\x8a\xafE\xf9\xaa\x0f\x8a\xd7X\xda\xd7}m\xba7\xb8\x96o\xfa\xb8\xf9\x16K\xfb\xf6\xb8\x8eQ~\xe5w\xd84\xde\xf5\xb5\xe5\xde\xe3Z\xbe\xef\xafi}\xc0\xd2~8\xae}L\xcd}ĵ\xfcx\\ۘ\xda',\xed\xa7\xe3:\xc6\xf8\x97\xe5g@\xf99g\xf1\x170\xf6Ko*\xee+\xae\xe5\xd7~D\xe5\x1b\x96\xf6[_\v\xf7\x1d\xd7\xf2{\x1f\x17?\xb0\xb4?\xfa\x1a\x97?q\xb5?\xfb\xdat\xbfp-\u007f\xf5\x974\u007fci\u007f\xf7\xf3\xfam\xfe\xc1\xa6\xf1\xa7\xaf-\xf7\x17\xd7\xf2o\u007fM\xeb\x1f\x96\xf6_\u007f\x8d\xbf\xd1\xff\xb1i\xcc\xd3\xdd\x1e\xf9\x9f \\˶\ueda8\xd6\xce\xd2vtwD~\xb3\x9dl\xe2\x010v\xa07\x157/\xd7r>\u007fie~\x96v\x01\xdf\xf57xAo\x9a\xc5B\xbe\x16na\xae\xe5\"\xfe\x9abQ\x96v1_\xe3rq\x1f\xc4K\xb0\xb4K\xf2\xd5n)\xae\xe5\xd2>n.\xc3\xd2.\xcbs5\xca\xe5\xd84\x96\xefnw5\xb7\x02\xd7r\xc5\xee6W[\x89\xa5]\xb9\xbb\xc3\xf9\xad\xad\xc2\xc6\xff\xe2\xc4Ʈ\xe6Mŭε\\\xc3_ZY\x93\xa5]\xcbw\xfdSpm\xa0\\\xc7g\xcdb]\x18\xbb\x9e7\x85[\x9fk\xb9\x81\x1fQl\xc8\xd2n\xe4k\\n\xec\x83x\x13\x96vS\x1e\xe46\xe3Zn\xee\xe3\xe6\x16,\xed\x96\xf7+\x9f\xedM\xb38\aƞ\xebM\xe1\xce\xe3Z\x9e\xef/-.`i/\xf45./\xf2A|1K{\t\x0fr\x97r-/\xf3q\xf3r\x96\xf6\n\x9e\xa5Q^ɦq\x95\xdfbZsW\x03\xe55~\xf7i\xedZ\x18{\x9d?u\xea7x=l|\x83X{#ۊ\xbb\t(\xc7\xf1\x88J7\x8c\xed\xe1K\xfcn{\xd96\x8b>\xb1\xf6f\xb6Ey\v_Z\xdc\ncoc\xc4\xe5\xed\x9c\xc5w\xc0\xd8;1̍\a\xca\t\xdcjN\x84\xb1\x930U\xa3\x9c\fۘ\xc2h\xb9\xa9@9\x8d\xaflM\x87\xb13\xf8J\xffn5\x13\xb61\xab\xbb\xdd\x1fo6\xd7rNw[Z\x9b\xcb\xd2\xde\xd5\xdd\xe1\xcf\xe2\xee\x06\xca{8\x8b\uf171\xf7ySq\xf7s-\x1f\xf0#*\x0f\xb2\xb4\x0f\xf9\xae\u007fkx\x18(\x1f\xf1Y\xb3x\x14\xc6>\xe6M\xe1\x1e\xe7Z>\xe1G\x14O\xb2\xb4O\xf9\x1a\x97O\xfb ~\x86\xa5}\x96\a\xb9縖\xcf\xfb\xb8\xf9\x02K\xfb\"\xcf\xd2(_b\xd3x\xd9ז{\x85k\xf9\xaa\xbf\xa6\xf5\x1aK\xfb\xba\xbf\xc6\x1f\xea\r6\x8d7\xbb\xdbuͽŵ|\xbb\xbbM\xd7\xdeai\xdf\xed\xee\xd0\xfeL\xef\x01\xe5\xfb\x9c\xc5\x1f\xc0\xd8\x0f\xbd\xa9\xb8\x8f\xb8\x96\x1f\xfb\x11\x95OX\xdaO}ן\xe93\xa0\xfc\xdcg\xcd\xe2\v\x18\xfb\xa57\x85\xfb\x8ak\xf9\xb5\x1fQ|\xc3\xd2~\xebk\xec\xbe\xe3Z~\xef\xe3\xf8\a\x96\xf6G\x1e\xea~\xe2Z\xfe\xec\xe3\xe6/,\xed\xaf\x96v~_c\xb7\x00\xd7rA\x1f\xc7\v\xb1\xb4\v\xf3P\xb7\b\xd7rQ\x1f7\x17ci\x17\xe7\xb9\x1a\xe5\x12l\x1aK\xf6th\xff\xf0.\x05\x94K\xf7\xb4{,\xd3ө\xf9A]\x16a\xbc\x9cX\xbb<ۊ[\x01(W\xe4K++\xc1ؕ\xf9\x12\xffخ\",W\xe5\xb8Y\xac&֮ζpk\x00\xe5\x9a<\xb2X\vƮ͈\xdd:@\xb9.\xb7\xe2\xf5`\xec\xfa\x98\xc4m\x00\x94\x1br\xab\xb9\x11\x8c\xdd\x18\x137\xcaM`\x1b\x9b\xf6tf\xbc\xe1\xcdz:\x98\x9b3*n\v\xa0ܲ\xa7\xddc+\x18\xbb5_\xe97\xb8\r\xdbf\xb1\xadX\xbb\x1d\xdb\xc2m\x0f\x94;\xf0\x88bG\x18\xbb\x13#.w\xe6,\xde\x05\xc6\xee\x8a\xd1n7\xa0ܝ[\xcd=`잘\xb1Q\xee\x05\xdbػ\xa7Ô57\b(\xf7\xe9i\xf7\xd8\x17\xc6\x0e\xee\xe9\xf4\x88\xdd\x10a9\x14q\x88\xbbY\xda\x1e\x1e\xe4z\xb9\x96}>n\xde\xcc\xd2\xde³4\xca[\xd94n\xf3\xb5\xe5n\xe7Z\xde\xe1\xafi\xdd\xc9Ҏ\xf7\xd7\xf0g\x186\x8d\x89=\xedU\xff\x11\x86k9\xb9\xa7\xadZ\x9b\xc2\xd2N\xed\xe9\xa8\xfag\xe54\xa0\x9c\xceY<\x03\xc6\xce\xf4\xa6\xe2fq-g\xfb\x11\x959,\xed\\\xdf\xe5\x8f0\xde\xf8\x8f00\xf6\x1eo\xfcG\x18\xae\xe5}\xfe\xd2\xe2~\x96\xf6\x01_\xfdG\x18\x1f\xc4\x0f\xb1\xb4\x0f\xf3 \xf7\b\xd7\xf2Q\x1f7\x1fci\x1f\xe7Y\x1a\xe5\x13l\x1aO\xfa\xdarOq-\x9f\xf6״\x9eai\x9f\xf5\xd7\xf8\xc3<Ǧ\xf1\xbc?\x99\xbf'/\x00\xe5\x8b\xfe\x80ck/\xc1ؗ{:=b\xf7\x8a\xb0|\x15q\xfc\x9aX\xfb:ۊ{\x03(\xdf䑕\xb7`\xecی\xa2|\x87\xb3\xe2]\x18\xfb\x1e\xc3\xff\xbc\xe1,\xfe\x00\xc6~\xc8h\x96\x1fq\xd6\xfc\x18\xc6~\xc2\xd3\xfb#|\n\xdb\xf8\x8c\xd1r\x9f\x03\xe5\x17|e\xebK\x18\xfb\x15_\xe9\x0f\xf25l\xe3\x1b\xff\xea\xf0'\xf9\x16(\xbf\U000ef631\xb5\xefa\xec\x0f\xfe\xf5\xc6'\xf8\x116\xfeI\xac\xfd\x99m\xc5\xfd\x02\x94\xbf\xf2\x88\xcao0\xf6w\xbe\xc4ߛ?\xd86\x8b?\x19E\xf9\x17`\xfff\xf8\x9fE< \xfe\x17W\x94\xff\xb1i\xce\xd3\xcb\xc6\xfe\xaf\x97G7\xca6\xd8F;\xa3\xe5:\x80\xb2\xb3\xd7_\xd9\x1a\x00c\a\xf2\x95\xfe\f\xf3\xc26\xe6\xebm\xa7\x9a\x9b\x9fk\xb9@o\x1b\xd5\x16di\x17\xea\xed \xbf\xfd\x85\xd9ċ\xc0\xd8E\xbd\xa9\xb8Ÿ\x96\x8b\xfbK+K\xb0\xb4K\xfa\xae\xdf\xf7R\xde4\x8b\xa5a\xec2\xde\x14nY\xae\xe5r\xfe\xd2by\x96v\x05_\xe3rE\x1f\xc4+\xb1\xb4+\xf3 \xb7\n\xd7rU\x1f7WciW\xe7Y\x1a\xe5\x1al\x1ak\xfa\xdarkq-\xd7\xf6״\xd6ai\xd7\xf5\xd7\xf8c\xacǦ\xb1\xbe7\xfe\x18\x1b\x00\xe5\x86>\xa3\xdaF0v\xe3\xdeN\xe2\xa3l\x02\x1bo*\xd6nƶ\xe26\a\xca-xDeK\x18\xbb\x15\xa3p[\x03\xe56\xdc*\xb6\x85\xb1\xdb1\xe2r{\xce\xe2\x1d`쎌\xa6\xdb\t(w\xe6Vs\x17\x18\xbb+\xa3\xe5v\x03\xcaݹ\xd5\xda\x03\xc6\xee\xc9\xfb\xf0\xfb\xdf\v\xb6\xb1wo\xc7X\xff\x1e:\b(\xf7\xe9m\xf7\xd8\x17\xc6\x0e\xee\xed\x1c\xcb\xef\xa2C\xd86\x8b\xa1b\xed0\xb6\x85\xdb\x0f(\xf7\xe7\x11\xc5\x010\xf6@F\xec\x0e\x02ʃ\xb9\x15\x0f\x87\xb1\x87`\x12w(P\x1eƭ\xe6\xe10v\x04&n\x94#a\x1bG0Z\xeeH\xa0<\x8a\xafl\x1d\rc\x8f\xe1+\xfdޏ\x85mTz;\xaa~\xef\n(\xab\xbd\xed\x1e\x11\x8c\x8d{;\xab\xfc\xf3`\x94\xb0\xd4\x1c7\v#\xd6\xd6\xd8\x16.\x01\xca:\x8f,R\x18\x9b1b\x97\x03e\xc1\xad\xd8\xc2\xd8ј\xa4lp\xd6$\x18\xeb0c\xa3l\xc26\xc60Z\xae\v(K\xbe\xb2Ղ\xb1c\xf9J\xbf\xf9\xe3`\x1b\xc7\xf7v4\x1a\x95\xf2\x84\xdev\x8f\x13{;\x1b\r\xbf͓8l\x16'3\nw\nP\x9eʗ\x14\xa7\xc1\xd8\xd3\x19\xb1;\x03(\xcf\xe4V|\x16\x8c=\x1b\x83\xdd9@y.\xb7\x9a\xe7\xc1\xd8\xf31\u007f\xa3\xbc\x00\xb6q!\xa3\xe5.\x02ʋ\xf9\xca\xd6%0\xf6R\xbe\xd2\xef\xf22\xd8\xc6\xe5\xfe\xc5\xda\xf0?\x9c\x80\xf2J\xffznT\xae\x82\xb1W3\xfc\xcf'Ίka\xecu\x8c\xd8]\x0f\x947p+\xbe\x11\xc6\xde\xc4h\xbaq@\xd9ͭf\x0f\x8c\xed\xf5\xef\f\xbc\xbb>\xd8\xc6͌\x96\xbb\x05(o\xe5+[\xb7\xc1\xd8\xdb\xf9J\xbf\xbb;`\x1bw\xfa\xe7\xb0\u007f\x02\x8c\a\xca\t\xfein*\x13a\xec$\xff4\xe7\x1b?\x99m\xb3\x98\"\xd6Ne[\xb8i@9\x9dG\x143`\xecLF\xecf\x01\xe5ln\xc5s`\xec\\L\xe2\xee\x02ʻ\xb9ռ\a\xc6ދ\x89\x1b\xe5}\xb0\x8d\xfb\x19-\xf7\x00P>\xc8W\xb6\x1e\x82\xb1\x0f\xf3\x95~\xef\x8f\xc06\x1e\xf5\x8fz\xc5=Ƶ|\xbc\xb7\xadQy\x82\xa5}\xd2?\xee~\xdbOy\xd3,\x9e\x86\xb1\xcfxS\xb8g\xb9\x96\xcf\xf9K\x8b\xe7Y\xda\x17|\x8d\xcb\x17}\x10\xbf\xc4Ҿ̃\xdc+\\\xcbW}\xdc|\x8d\xa5}\x9dg\xf1\x9f\x94\xd84\xde\xf4\xb5\xe5\xde\xe2Z\xbe\xed\xafi\xbd\xc3Ҿ\xeb\xaf\xf1{{\x8fM\xe3\xfd\xde\xf6\xa4\xe6>\xe0Z~\xd8ۖ\xd4>bi?\xee\xedH\xfc\xcf\xc8O\x80\xf2S\xce\xe2\xcf`\xec\xe7\x1e\xfe\xd5\xf6\x05P~\xe9[\xcd\xe2+\x18\xfb\xb57\x85\xfb\x86k\xf9\xad\x9f\xac\xf8\x8e\xa5\xfd\x9e\xafq?p-\u007f\xf4q\xf3'\x96\xf6g\x1e\xd4(\u007fa\xd3\xf8\xd5ז\xfb\x8dk\xf9\xbb\xbf\xa6\xf5\aK\xfb\xa7\xbf\xc6\xef\xf5/6\x8d\xbf{\xdbG\xd7\xdc?\\\xcb\u007f{\xdbF\xd7\xfeci\xe7\xe9\xeb\x18\xed\xf7\xfa?\xa0l\xeb\xf3Y\xdc\x0ec;<\xfc^;\x81r\x80o5\x8b\x810v^o\n7\x1f\xd7r\xfe\xbe\xb6\xd1\xc5\x02,\xed\x82|\x8d[\x88k\xb9\xb0\x8f\x9b\x8b\xb0\xb4\x8b\xf2\xa0F\xb9\x18\x9b\xc6⾶\xdc\x12\\\xcb%\xfd5\xad\xa5Xڥ\xfd5~\xaf˰i,\xdbב\xf8\av9\xa0\\\xbe\xaf\xddc\x05\x18\xbbb_g\xc2\x0f\xeeJ\xc2re\xc4\xf1*b\xed\xaaL\xbf\xdb\xd58n\x16\xab\x8b\xb5k\xb0-ܚ@\xb9\x16OX\xac\rc\xd7\xc1\x95n]\xa0\\\x8f[\xcd\xf5a\xec\x06\x18\xdd(7\x84ml\xc4h\xb9\x8d\x81r\x13\xbe\xb2\xb5)\x8cݬ\xaf#\xf7\x1b\xde\x1c(\xb7\xe8k\xf7\xd8\x12\xc6n\xd5י\xf3\x86\xb7\x16\x96\xdb \x8e\xb7\x15k\xb7c\xfa\ro\xcfq\xb3\u0601Q\xb8\x1d\x81r'\x9e\xa8\xd8\x19\xc6\xee\x82+\xca]9k\xee\x06cw\xc7\xe8F\xb9\alcOF\xcb\xed\x05\x94{\xf3\x95\xadA0v\x1f\xbe\xd2?\xc0\xfb\xc26\x06{\xb4jn\bP\x0e\xf5W\xb6j\xc3`\xec~\xfeʖ\xdf\xf0\xfe\xc2\xf2\x00\xc4\xf1\x81b\xedAL\xbf\xe1\x839n\x16\xc3\xc5\xdaC\xd8\x16\xeeP\xa0<\x8c',\x0e\x87\xb1#p\xa5\x1b\t\x94Gp\xaby$\x8c=\xaa\xaf\xdd\u007f\xa0;\x9akyL_[Y;\x96\xa5\xad\xf4u\xf0\x879\x05\x94U\xce\xe2\b\xc6\xc6\x1e\xfc)\xc9g\xfeC\x92\xaf\xfe3\x12ײ\xe6\x87\x17\tK[\xf7\xb5\xe5R\xaee\xe6\xe3V\xce\xd2\x16~\xa8\u007f\x04,\x9b\xc6\xe8\xbev\u007f\xfe\x06ג\xfaڼfi\x9b}\x1d|\xf61@\xd9\xc5Y\\\xc2ؖ\x87\u007f9\x8c\x05\xca\xe3|\xabY\x1c\x0fcO\xf0\xa6p'r-O\xf2\x93\x15'\xb3\xb4\xa7\xf05\xeeT\xae\xe5i>n\x9e\xceҞ\xc1\x83\x1a\xe5\x99l\x1ag\xf9\xdargs-\xcf\xf1״\xceei\xcf\xf3\xd7\xf8\xbd\x9eϦqA\x1f\xff\xe7\xd7\vQ/B\xbd\xb8\xaf\xfd\xc0\xc2]\xd2\xd76\xb4vi_\xfb\x90\x9a\xbb\xac\xafc?\xbf\xfd\xcb\xfb\xda\xf7h\xd9+\xfa\xda\x0f\xa6ڕ}\x1d\x83\xfc\xa7ū\xfa\xda\a\xc5\xf6꾶a\xb5k\xfa:\xf6\xf1\x0f\xf7\xb5}\xedC[\x8d\xeb\xfa:vWMw}_\xfb\xb0\xc2\xde\xd0\xd76\xa2\xb8\xb1\xaf\xfd\x80\xa6\xbb\x89/j\x16\xe3\xfaڇW\\w_۠J\x0f\x96\xecE\xed\xeb\xeb\xd83\xf2?y\xfa\xdawo6n\xe1\x15\x9aŭ\xbcvao\xebk\x1b\u07bc\xbd\xafm\u007f{G_\xc7`\xff\x92\xba\xb3\xaf}d\xe1\xc6\xfbY[nB_\xfb>Y1\xb1\xaf}\x84\xff\x94\xd4\xd7~h\xb31\xd9oҴ\xa6x\x1d۩}m#+\xd3\xfa\xda\x06\x17ӱʌ\xbe\xf6\xc1Ik\xa6\xdfy\xe1f\xf1d\xb5xv_\xdbaŜ\xbe\xb6=\x9bs\xfb\xf8\x8f\x0e5\xee\xf2\xa7\xa8\xc5w\xf7\xb5\xedѺ\xa7\x8f\xff\xa4\x88\xbd\xd7\x1f\xbef\xef\xe3\xfd\x17\xf6~\xbe\xaa\xe2\x1e\xc0|\x0f\xb2)\xecC\xfe\x02\x13?\xdc\xd79\x92?\xc2<\x82֣}mC\xe2\xc7 \x1fG}\x02\xf5IԧP\x9fF}\x06\xf5Y\xd4\xe7P\x9fG}\x01\xf5EԗP_F}\x05\xf5U\xd4\xd7P_G}\x03\xf5MԷP\xdfF}\a\xf5]\xd4\xf7P\xdfG\xfd\x00\xf5CԏP?F\xfd\x04\xf5S\xd4\xcfP?G\xfd\x02\xf5K\u052fP\xbfF\xfd\x06\xf5[\xd4\xefP\xbfG\xfd\x01\xf5GԟP\u007fF\xfd\x05\xf5W\xd4\xdfP\u007fG\xfd\x03\xf5OԿP\xffF\xfd\a\xf5_\xd4yƷ\xe9\xca\xffƷ\xeb\x8an\x1b\xdf\xe1\xabn\a\xa8\x83\xb3\xbc\x93M\x9e\f\x00\xcc@\u03a2y\xb9\xa6\xf3q\x94\xea\xf9\x81l\x01@-\b\xd0B\x80[\x18\xb0\x8b\x00fQ\x1e\x96-\xc6U-Α\xa2%\xd8Вl\x88\x96\xc2\x1aziΒe\xb8V\x97\xe5Z_\x8e\xab[\x9e\xab]\x81\xabY\x91k\xbc\x12\x0f\x88\xf5\xca\xe3;\x19z\x15!\xad\x8a8_\r6OV\x17\x9a5\x10Gk\x02\xe9ZHS\xbd\xb60[G\xa8\xd6\x15\xd2zB\xb7\xbe\xd0n 4\x1bbx\xb6\x11\xa06F\xaah\x13X\xda\x14\x96h3YRo\x8e8\xd9\x02\xa8n\tԷ\x02\xdcր\xdd\x060\xdb\xfa\x03\xb5*\xdbyӪ\xe8\xed\xfdp\x0f\xbd\x83\x90vD\x9c\xef\x04\x9b';\v\xcd.\x88\xa3]\x81t7\xa4\xa9\xde]\x98\xed!T{\ni/\xa1\xdb[h\a\t\xcd>\x18\x9e\xed\v\xa8\xc1H\x15\r\x81\xa5\xa1\xb0D\xc3dI\xbd\x1f\xe2d\u007f\xa0z\x00P?\x10p\a\x01\xf6`\xc0\f\a\xe2C00և\x8e\x1f\x00\xeaÂ\xa0å\x93\x8f\x90 OF\x06a\x8e\x90Nt\xa40=J\x1a\xa9>:\x88\xec\x98 ԱAP%\b\xa7\x82\xb0\xd5 L$\xd3d\xb1P\x8d\x92\x86\"-\x01\x19\t\xfc\aٰ\x03\x9dH'\xa9\v\xab\xa9\xb0\x9e\t].\xb4\x85\xd0X\u007f\xf7\xe2b\xb4?r\\\xe8\x86\x0f=4\t\xc9!Λ\xb0y2Fh\xba\x10G%\x90\xb6\x90\xa6z\xac0;N\xa8\x8e\x17\xd2\tBw\xa2О$4'cxv\n\xa0NE\xaa\xe84X:\x1d\x96\xe8\fYR\x9f\x8989\v\xa8\x9e\r\xd4\xcf\x01ܹ\x80=\x0f0\xe7\xfb7\x85\xf8\x02>\x95\xbe\x90\x13\xad/\x02\xe8b\xce\xf2K\xd8\xe4ɥ\x80\xb9\x8c\xb3\xe8r\xae\xe9\x15\x1c\xa5\xfaJ \xbb\nPW\x03t\r\xe0\xae\x05\xecu\x80\xb9\x9e\x87e7pU7r\xa4\xe8&64\x8e\rQ7\xd6\xd0=\x9c%\xbd\\\xab}\\\xeb7su\xb7p\xb5\xb7r5\xb7\xf1S..n\xe7\x9b\xe2\x1f\xf8;\xf8n\xf2\x8d\xb83\b\x1a/\x9d|\x82\x04y21\b3I:\xd1da:E\x1a\xa9\x9e\x1aD6-\b5=\b\x9a\x11\x84\x9b\x19\x84\x9d\x15\x84\x99-\xd3ds\x84j\xae4\x14\xdd%\x01\xdd-\x01\xd1=a\a\xfa^\xe9$\xf7\t\xab\xf7\v\xeb\x0f\b݃B\xfb\x90\xd0<\xcco\x18\xf1#8\xb2~\x14\xa1֏\t\xe9q\xc4\xf9\x13\xb0y\xf2\xa4\xd0<\x858z\x1aH\x9fA\x9a\xeag\x85\xd9sB\xf5\xbc\x90^\x10\xba\x17\x85\xf6%\xa1y\x19óW\x00\xf5*RE\xaf\xc1\xd2\xeb\xb0DoȒ\xfaM\xc4\xc9[@\xf5m\xa0\xfe\x0e\xe0\xde\x05\xec{\x80y\xdf?\xe5\x8a\x0f\xfc\xa9\n\xfd\xa1O\xfc}\xfb\b\xa0\x8f9\xcb?a\x93'\x9f\x02\xe63\u03a2Ϲ\xa6_p\x94\xea/\x81\xec+@}\r\xd07\x80\xfb\x16\xb0\xdf\x01\xe6{\x1e\x96\xfd\xc0U\xfdȑ\xa2\x9f\xd8\xd0\xcfl\x88~\xc1\x1a\xfaWΒ߸V\u007f\xe7Z\xff\x83\xab\xfb\x93\xab\xfd\x8b\xab\xf9\xdb\u05ee\xca?~@WE\xff\xeb\xcf\xec\xa1\xff\x13\xd2<\x138\xce\xff7\x81m\x9e\xb4\tM;\xe2\xa8\x03H;\x91\xa6z\x800\x1b(T\xf3\ni>\xa1\x9b_h\x17\x10\x9a\x051<[\bP\v#U\xb4\b,-\nK\xb4\x98,\xa9\x17G\x9c,\x01T\x97\x04\xeaK\x01ni\xc0.\x03\x98e\x81x9\f\x8c\xf5\xf2\x13\x06\x80z\x85 hE\xe9\xe4+I\x90'+\aaV\x91N\xb4\xaa0]M\x1a\xa9^=\x88l\x8d ԚA\xd0ZA\xb8\xb5\x83\xb0\xeb\x04a֕i\xb2\xf5\x84j}i(\xda@\x02\xdaP\x02\xa2\x8d\xc2\x0e\xf4\xc6\xd2I6\x11V7\x15\xd67\x13\xbaͅv\v\xa1\xd9r\x82\xbf\x87\xf1V\xfe\xc8E\xac\xb7\xf6\xa1\x87\xdeFH\xdb\"η\x83͓\xed\x85f\a\xc4ю@\xba\x13\xd2T\xef,\xccv\x11\xaa]\x85\xb4\x9b\xd0\xed.\xb4{\b͞\x18\x9e\xed\x05\xa8\xbd\x91*\x1a\x04K\xfb\xc0\x12\xed+K\xea\xc1\x88\x93!@u(P\x1f\x06\xb8\xfd\x00\xbb?`\x0e\xf0\aj\x15\az\xd3*\xf4A~\xb8\x87>XH\xc3\x11\xe7\x87\xc0\xe6ɡBs\x18\xe2\xe8p \x1d\x814\xd5#\x85\xd9\x11Bu\xa4\x90\x8e\x12\xba\xa3\x85\xf6\x18\xa19\x16ó\n\xa0\x14REUX\x8a`\x89bYR\x8fB\x9ch\xa0j\x80z\rp\t`\xeb\x80I'\xb4\xe9f\xe6O\xd5ԹO\x9aZ\x17\x00Y\xce\xf2\xd1l\xf2\xa4\x01\x18\xe2,r\\\xd3&G\xa9\x1e\x03d]\x80*\x01j\x01n,`\x8f\x03\xcc\xf1<,;\x81\xab:\x91#E'\xb1\xa1\x93\xd9\x10\x9d\x825\xf4\xa9\x9c%\xa7q\xad\x9eε~\x06Ww&W{\x16Ws6\xbf\x86\xe2\xe2\x1c~\x96\xf9\x1f;\xe7\xf2ӓ\u007f\x0e\x9d\x17\x04\x9d/\x9d\xfc\x02\t\xf2\xe4\xc2 \xccE҉.\x16\xa6\x97H#\u0557\x06\x91]\x16\x84\xba<\b\xba\"\bwe\x10\xf6\xaa \xcc\xd52Mv\x8dP]+\rE\xd7I@\xd7K@tC\u0601\xbeQ:\xc9M\xc2\xea8a\xbd[\xe8z\x84\xb6Wh\xfa\xfc\x91\xbb\xe2\x9bqd}\vB\xado\x15\xd2m\x88\xf3\xdba\xf3\xe4\x0e\xa1\xb9\x13q4\x1eH' M\xf5Da6I\xa8&\vi\x8a\xd0M\x15\xdaiB3\x1dó\x19\x80\x9a\x89T\xd1,X\x9a\rK4G\x96\xd4s\x11'w\x01ջ\x81\xfa=\x80\xbb\x17\xb0\xf7\x01\xe6~>P\xed\x0165\xfd \x0f\xafi\xfd\x90\x90\x1eF\x9c?\x02\x9b'\x8f\n\xcdc\x88\xa3ǁ\xf4\t\xa4\xa9~R\x98=%TO\v\xe9\x19\xa1{Vh\x9f\x13\x9a\xe71<{\x01P/\"U\xf4\x12,\xbd\fK\xf4\x8a,\xa9_E\x9c\xbc\x06T_\a\xeao\x00\xeeM\xc0\xbe\x05\x98\xb7\xf9M\xa1\xf9\x0e\xbf\xa0\x9a\xfa]~\t\xfa\x97\xd1{Bz\x1fq\xfe\x01l\x9e|(4\x1f!\x8e>\x06\xd2O\x90\xa6\xfaSa\xf6\x99P}.\xa4/\x84\xeeK\xa1\xfdJh\xbe\xc6\xf0\xec\x1b@}\x8bT\xd1w\xb0\xf4=,\xd1\x0f\xb2\xa4\xfe\x11q\xf2\x13P\xfd\x19\xa8\xff\x02\xb8_\x01\xfb\x1b`~\xf7\a\x8a\x9b\u007fx\x137\xf5\x9f~\xb8\x87\xfeKH\u007f#\xce\xff\x81͓\u007f\x85\xe6?\xc4\xd1<\x13\x19\xe9\xff&r\x9a\xea6a\xd6.T\x1dB\xea\x14\xba\x01B;Ph\xe6\xc5\xf0l>@͏T\xd1\x02\xb0\xb4 ,\xd1B`\xae\x17F\x9c,\x02T\x17\x05\xea\x8b\x01nq\xc0.\x01\x98%'\xfa;T[ʛVM/\xed\x87{\xe8e\x84\xb4,\xe2|9\xd8\xe2d\x03\xa0\xba!P\xdf\bp\x1b\x03v\x13\xc0l:\xb1M\xd76\xf3\xa7\xaa\xe9\xcd}\xe2O\xb3\x05@[r\x96o\xc5&O\xb6\x06\xcc6\x9cE\xdbrM\xb7\xe3(\xd5\xdb\x03\xd9\x0e\x80\xda\x11\xa0\x9d\x00\xb73`w\x01̮<,ۍ\xabڝ#E{\xb0\xa1=\xd9\x10\xed\x855\xf4ޜ%\x83\xb8V\xf7\xe1Zߗ\xab\x1b\xcc\xd5\x0e\xe1j\x86N\xe4?\xd73l\"\xff\xb9\x1e\xbd\x9f?3\u007f\x17\xb2\xbf\x90\x0e@\x9c\x1f\b\x9b'\a\t\xcd\xc1\x88\xa3\xe1@z\b\xd2T\x1f*\xcc\x0e\x13\xaaÅ4B\xe8F\n\xed\x11Bs$\x86gG\x01\xeah\xa4\x8a\x8e\x81\xa5ca\x89*\xb2\xa4V\x88\x93*P\x8d\x80z\f\xb8Q\x80Հ1@\\\xc3\xc0X'\x13\a\x80\xba\x1e\x04\xa5\xd2\xc93\t\xf2$\x0f\xc2\x14҉\xac0\x1d-\x8dT7\x82\xc8(\b傠f\x10nL\x10\xb6+\bS\xca4YK\xa8\xc6JC\xd1q\x12\xd0\xf1\x12\x10\x9d\x10v\xa0O\x94Nr\x92\xb0z\xb2\xb0~\x8aН*\xb4\xa7\t\xcd\xe9|\xd8V\xe5\f\xb6\xad\x8a>\x93\xe7\xe1\xefP\xce\n\x82ΖN~\x8e\x04yrn\x10\xe6<\xe9D\xe7\v\xd3\v\xa4\x91\xea\v\x83\xc8.\nB]\x1c\x04]\x12\x84\xbb4\b{Y\x10\xe6r\x99&\xbbB\xa8\xae\x94\x86\xa2\xab$\xa0\xab% \xba&\xec@_+\x9d\xe4:a\xf5za\xfd\x06\xa1\xbbQho\x12\x9aq¸[&\x88u\xcfā\"to\xbf\xa2\xbe\xd0\xcco\x0eQ\x9e\xdcү̭\xa1\x19\xdd\x16Dz{\xe8\xa5\xfa\x8e~\x95\xddٯ\xd4\xf8~E\x13\xfa\x95\x9bد\xec\xa4~e&\x87\xe9\xb2)A\xa8\xa9\xa1\xa7hZ\x88hz\x88\x88f\xf4oH\xcf\f\xcddV\x10\xd5\xd9A\xd4\xe7\x04\xe1\xe6\x06a\xef\n\xc2\xdc\xcdw=.\xee\xe1\xc7\xc3\xfffu/\xe7\xfc\xab\xd6}A\xd0\xfd\xd2\xc9\x1f\x90 O\x1e\f\xc2<$\x9d\xe8aa\xfa\x884R\xfdh\x10\xd9cA\xa8ǃ\xa0'\x82pO\x06a\x9f\n\xc2<-\xd3d\xcf\bճ\xd2P\xf4\x9c\x04\xf4\xbc\x04D/\x84\x1d\xe8\x17\xa5\x93\xbc$\xac\xbe,\xac\xbf\"t\xaf\n\xedkB\xf3:\xbfA\xc5o\xe0\xc8\xfaM\x84Z\xbf%\xa4\xb7\x11\xe7\xef\xc0\xe6ɻB\xf3\x1e\xe2\xe8} \xfd\x00i\xaa?\x14f\x1f\t\xd5\xc7B\xfaD\xe8>\x15\xdaτ\xe6s\fϾ\x00ԗH\x15}\x05K_\xc3\x12}#K\xeao\x11'\xdf\x01\xd5\xef\x81\xfa\x0f\x80\xfb\x11\xb0?\x01\xe6g<\x8d\xe3\xe2\x17\xdcN\u007f\xbb~\xc5Ӂo\xe0o\xfd\x8a~\x0f\xcd\xfc\x8f\x10\xe5ɟ\xfd\xca\xfc\x15\x9a\xd1\xdfA\xa4\xff\x84^\xaa\xff\xedW\xd9\u007f\xfdJ\xcd3)(\xfa_\xbfrm\xfdʶ\xf7+\xd31I\xa6\xcb:\x83P\x03BO\xd1\xc0\x10Ѽ!\"\x9a/\xa8\\\xcf\x1f\x9a\xc9\x02AT\x17\f\xa2\xbeP\x10n\xe1 \xec\"A\x98E'\xf1\x9bW\xbc\xd8$<\x1ezqɵ^\"\bZR:\xf9R\x12\xe4\xc9\xd2A\x98e\xa4\x13-+L\x97\x93F\xaa\x97\x0f\"[!\b\xb5b\x10\xb4R\x10n\xe5 \xec*A\x98Ue\x9al5\xa1Z]\x1a\x8a\u0590\x80֔\x80h\xad\xb0\x03\xbd\xb6t\x92u\x84\xd5u\x85\xf5\xf5\x84n}\xa1\xdd@h6\x9c\xc4\u007f\nv#>r\xa17\xe6\xd0\xdf\xefM\x84\xb4)\xe2|3\xd8<\xd9\\h\xb6@\x1cm\t\xa4[!M\xf5\xd6\xc2l\x1b\xa1\xdaVH\xdb\t\xdd\xf6B\xbb\x83\xd0\xec\x88\xe1\xd9N\x80\xda\x19\xa9\xa2]`iWX\xa2\xdddI\xbd;\xe2d\x0f\xa0\xba'P\xdf\vp{\x03v\x10`\xf6atU\xf6\xe5\x81]\x15=\x98\x1f\x18\xfe\x1ehH\x104T:\xf90\t\xf2d\xbf \xcc\xfe҉\x0e\x10\xa6\aJ#\xd5\a\x05\x91\x1d\x1c\x84\x1a\x1e\x04\x1d\x12\x84;4\b{X\x10\xe6p\x99&\x1b!T#\xa5\xa1\xe8\b\t\xe8H\t\x88\x8e\n;\xd0GK'9FX=VX\xaf\b\x9d\x12ڪ\xd0D\xc28\x96\tb=\x8a\x9f\xfb\xf8\x8eH\xf7+2\xa1\x99\xd7B\x94'I\xbf2\xf5Ќ\xd2 \xfcG>\xe9\xa5:\xefWYѯ\x94\xedW4\xba_\xb9F\xbf\xb2\u052f\x8c\v\xd3e\xcd Ԙ\xd0S\xd4\x15\"*CD\xd4\xeaߐ\x1e\x1b\x9a\xc9qAT\x8f\x0f\xa2~B\x10\xee\xc4 \xecIA\x98\x93q\xf3\xe3S\xf0t\x89\xf5\xa9\x9c\xf3\xf7G\xa7\x05A\xa7K'?C\x82<93\bs\x96t\xa2\xb3\x85\xe99\xd2H\xf5\xb9Ad\xe7\x05\xa1\xce\x0f\x82.\b\xc2]\x18\x84\xbd(\bs\xb1L\x93]\"T\x97JC\xd1e\x12\xd0\xe5\x12\x10]\x11v\xa0\xaf\x94Nr\x95\xb0z\xb5\xb0~\x8d\xd0]+\xb4\xd7\t\xcd\xf5x\x83*n\xc0K\xb8\xd07\xe2\xb5\xef_\xb67\x05A㤓wK\x90'=A\x98^\xe9D}\xc2\xf4fi\xa4\xfa\x96 \xb2[\x83P\xb7\x05A\xb7\a\xe1\xee\b\xc2\xde\x19\x84\x19/\xd3d\x13\x84j\xa24\x14M\x92\x80&K@4%\xec@O\x95N2MX\x9d.\xac\xcf\x10\xba\x99B;Khf\xf3\x1bTs\x0e\x1f\xb9\xa9\xe7r\xe8?_\xde%\xa4\xbb\x11\xe7\xf7\xc0\xe6ɽBs\x1f\xe2\xe8~ }\x00i\xaa\x1f\x14f\x0f\t\xd5\xc3BzD\xe8\x1e\x15\xdaDŽ\xe6q\fϞ\x00ԓH\x15=\x05KO\xc3\x12=#K\xeag\x11'\xcf\x01\xd5\xe7\x81\xfa\v\x80{\x11\xb0/\x01\xe6e\xbcf\xe3\xe2\x15\b\xbbB\x10fE\x99&[I\xa8V\x96\x86\xa2U$\xa0U% Z-\xec@\xaf.\x9dd\rauMa}-\xa1[[h\xd7\x11\x9au\xf9\xb0\xad\xe6zl[M\xbd>\xcf\xc3\xdfsm\x10\x04m(\x9d|#\t\xf2d\xe3 \xcc&҉6\x15\xa6\x9bI#՛\a\x91m\x11\x84\xda2\b\xda*\b\xb7u\x10v\x9b ̶2M\xb6\x9dPm/\rE;H@;J@\xb4S\u0601\xdeY:\xc9.\xc2\xea\xae\xc2\xfanB\xb7\xbb\xd0\xee!4{\xf2a\xe3\xe6^l\xe3\xa6ޛ\xe7\xe1\xef\xc0\x06\x05A\xfbH'\xdfW\x82<\x19\x1c\x84\x19\"\x9dh\xa80\x1d&\x8dT\xef\x17D\xb6\u007f\x10\xea\x80 \xe8\xc0 \xdcAA\u0603\x830\xc3e\x9a\xec\x10\xa1:T\x1a\x8a\x0e\x93\x80\x0e\x97\x80hD\u0601\x1e)\x9d\xe4\ba\xf5Ha\xfd(\xa1;Zh\x8f\x11\x9acqgk\x15\x9c\xbd\xa6\x15\x1e4\xff4\xae\x06A\x91t\xf2X\x82<\x19\x15\x84\xd1҉\x8c0\xadI#\xd5I\x10Y=\b\x95\x06AY\x10.\x0f\xc2\x16A\x18+\xd3d\xa3\x85\xaa!\rE$\x019\t\x88\x9aa\az\x8ct\x92.a\xb5\x14\xd6[B7Vh\x8f\x13\x9a\xe3'\xf3_C;\x81\x8f\\\xd3'r\xe8Oz\x92\x90NF\x9c\x9f\x02\x9b'\xa7\n\xcdi\x88\xa3Ӂ\xf4\f\xa4\xa9>S\x98\x9d%Tg\v\xe9\x1c\xa1;Wh\xcf\x13\x9a\xf31<\xbb\x00P\x17\"Ut\x11,]\fKt\x89,\xa9/E\x9c\\\x06T/\a\xeaW\x00\xeeJ\xc0^\x05\x98\xab'\xb7\xe5\x95k&\xb7\xe7\x15}\xed\xe4\x0e_\xf5u\x00]\xcfY~\x03\x9b<\xb9\x1107q\x16\x8d\xe3\x9avs\x94\xea\x1e \xeb\x05T\x1f@7\x03\xee\x16\xc0\xde\n\x98\xdbxXv;Wu\aG\x8a\xeedC\xe3\xd9\x10M\xc0\x1az\"g\xc9$\xae\xd5\xc9\\\xebS\xb8\xba\xa9\\\xed4\xaef:\xd7x\x06\x0f\x88\xf5\xccɝ\f=KH\xb3\x11\xe7s`\xf3d\xae\xd0܅8\xba\x1bH\xefA\x9a\xea{\x85\xd9}Bu\xbf\x90\x1e\x10\xba\a\x85\xf6!\xa1y\x18óG\x00\xf5(RE\x8f\xc1\xd2\xe3\xb0DOȒ\xfaI\xc4\xc9S@\xf5i\xa0\xfe\f\xe0\x9e\x05\xecs\x80y\xde\x1f\xa8Uy\xc1\x9bVE\xbf\xe8\x87\xf37c/\t\xe9e\xc4\xf9+\xb0y\xf2\xaaм\x868z\x1dH\xdf@\x9a\xea7\x85\xd9[B\xf5\xb6\x90\xde\x11\xbaw\x85\xf6=\xa1y\x1fó\x0f\x00\xf5!RE\x1f\xc1\xd2ǰD\x9fȒ\xfaS\xc4\xc9g@\xf5s\xa0\xfe\x05\xe0\xbe\x04\xecW\x80\xf9\x1a\x88\xbf\xc1\xc0X\u007f;y\x00\xa8\xbf\v\x82\xbe\x97N\xfe\x83\x04y\xf2c\x10\xe6'\xe9D?\v\xd3_\xa4\x91\xea_\x83\xc8~\vB\xfd\x1e\x04\xfd\x11\x84\xfb3\b\xfbW\x10\xe6o\x99&\xfbG\xa8\xfe\x95\x86\xa2\xff$\xa0y\xa6 \xfa\x9f\x88\\\xb7MA'i\x17V;\x84\xf5N\xa1\x1b \xb4\x03\x85f\xde)\xedy\\\xcc7\xa5\xc3W=\xbf\x0f\xf9\x17\xb7\x05\x84\xb4 \xe2|!\xd8\x01p\x13\x01;\t0\x93\x81x\n\x06\xc6z\xaa\u007fz⛣iA\xd0t\xe9\xe43$ȓ\x99A\x98Y҉f\v\xd39\xd2H\xf5\xdc \xb2\xbb\x82Pw\aA\xf7\x04\xe1\xee\r\xc2\xde\x17\x84\xb9_\xa6\xc9\x1e\x10\xaa\a\xa5\xa1\xe8!\t\xe8a\t\x88\x1e\t;ЏJ'yLX}\\X\u007fB\xe8\x9e\x14ڧ\x84\xe6i\xbe\x87\xf13|\xbbc\xfd\xac\x0f\xf9ۡ\xe7\x84\xf4<\xe2\xfc\x05\xd8\x1ap\r\xc0\x12`\xdcT\xfe+bM~A5\xf5\x98\xa9\xfc\x17̴\xee\x12R\x898o\xc1\xe6\xc9X\xa19\x0eqt<\x90\x9e\x804\xd5'\n\xb3\x93\x84\xead!\x9d\"t\xa7\n\xediBs:\x86gg\x00\xeaL\xa4\x8a\u0382\xa5\xb3a\x89Α%\xf5\xb9\x88\x93\xf3\x80\xea\xf9@\xfd\x02\xc0]\b؋\x00s\xb1?Pܼě\xb8\xa9/\xf5\xc3\xf9\x9b\x95˄t9\xe2\xfc\n\xd8<\xb9Rh\xaeB\x1c]\r\xa4\xd7 M\xf5\xb5\xc2\xec:\xa1\xba^H7\bݍB{\x93Ќ\xc3\xf0\xac\x1bP=H\x15\xf5\xc2R\x1f,\xd1Ͳ\xa4\xbe\x05qr+P\xbd\r\xa8\xdf\x0e\xb8;\x00{'`\xc6\xf3\x1d\xaaM\xe0\xd3\xd5\xf4D~<\xfcSn\x92\x90&#Χ\xc0\xe6\xc9T\xa1\x99\x868\x9a\x0e\xa43\x90\xa6z\xa60\x9b%T\xb3\x854G\xe8\xe6\n\xed]Bs7\x86g\xf7\x00\xea^\xa4\x8a\ue0e5\xfba\x89\x1e\x90%\xf5\x83\x88\x93\x87\x80\xea\xc3@\xfd\x11\xc0=\n\xd8\xc7\x00\xf3\xb8\u007fۮ=\xe1OU\xd3O\xfağ\xe6)\x80\x9e\xe6,\u007f\x86M\x9e<\v\x98\xe78\x8b\x9e皾\xc0Q\xaa_\x04\xb2\x97\x00\xf52@\xaf\x00\xeeU\xc0\xbe\x06\x98\xd7yX\xf6\x06W\xf5&G\x8a\xdebCo\xb3!z\ak\xe8w9K\xde\xe3Z}\x9fk\xfd\x03\xae\xeeC\xae\xf6#\xae\xe6\xe3\xa9mQ哩\xedQE\u007f:\xb5\xc3W\xfd\x19@\x9fs\x96\u007f\xc1&O\xbe\x04\xccW\x9cE_sM\xbf\xe1(\xd5\xdf\x02\xd9w\x80\xfa\x1e\xa0\x1f\x00\xf7#`\u007f\x02\xcc\xcf<,\xfb\x85\xab\xfa\x95#E\xbf\xb1\xa1\xdf\xd9\x10\xfd\x815\xf4\x9f\x9c%\u007fq\xad\xfe͵\xfe\x0fW\xf7/W\xfb\x1fW3\xcf4_\xe3\xffM\xf3\x03b\xdd6\xad\x93\xa1ۅԁ8\xef\x84͓\x01B3\x10q4/\x90·4\xd5\xf3\v\xb3\x05\x84jA!-$t\v\v\xed\"B\xb3(\x86g\x8b\x01jq\xa4\x8a\x96\x80\xa5%a\x89\x96\x92%\xf5҈\x93e\x80\xea\xb2@}9\xc0-\x0f\xd8\x15\x00\xb3\xa2?P\xab\xb2\x927\xad\x8a^\xd9\x0f\xe7/BV\x11Ҫ\x88\xf3\xd5`\xf3du\xa1Y\x03q\xb4&\x90\xae\x854\xd5k\v\xb3u\x84j]!\xad't\xeb\v\xed\x06B\xb3!\x86g\x1b\x01jc\xa4\x8a6\x81\xa5Ma\x896\x93%\xf5戓-\x80\xea\x96@}+\xc0m\r\xd8m\x00\xb3-\x10o\x87\x81\xb1\xde~\xda\x00P\xef\x10\x04\xed(\x9d|'\t\xf2d\xe7 \xcc.҉v\x15\xa6\xbbI#ջ\a\x91\xed\x11\x84\xda3\b\xda+\b\xb7w\x10vP\x10f\x1f\x99&\xdbW\xa8\x06KC\xd1\x10\th\xa8\x04D\xc3\xc2\x0e\xf4~\xd2I\xf6\x17V\x0f\x10\xd6\x0f\x14\xba\x83\x84\xf6`\xa1\x19\xee\xef^\\\x1c\xe2\x8f\xec\u007f\xcf9ԇ\xfc{\xcfaB:\x1cq>\x026OF\n\xcd\x11\x88\xa3#\x81\xf4(\xa4\xa9>Z\x98\x1d#T\xc7\n\xa9\"tJh\xabB\x13ax\x16\x03j\x14RE\x1a\x96\f,QM\x96\xd4\t\xe2\xa4\x0eTS\xa0\x9e\x01.\al\x01\x18;\xad-\x8aG\xf3\xa9t\x83\x13\xad\t \xc7Y\xded\x93'c\x00\xd3\xc5YTrM[\x1c\xa5z,\x90\x1d\a\xa8\xe3\x01:\x01p'\x02\xf6$\xc0\x9c\xccòS\xb8\xaaS9Rt\x1a\x1b:\x9d\r\xd1\x19XC\x9f\xc9Yr\x16\xd7\xea\xd9\\\xeb\xe7pu\xe7r\xb5\xe7q5\xe7\xf3S...\xe0\x9b\xe2\x1f\xf8\v\xf9n\xf2\x8d\xb8(\b\xbaX:\xf9%\x12\xe4ɥA\x98ˤ\x13].L\xaf\x90F\xaa\xaf\f\"\xbb*\buu\x10tM\x10\xee\xda \xecuA\x98\xebe\x9a\xec\x06\xa1\xbaQ\x1a\x8an\x92\x80\xc6I@\xd4\x1dv\xa0{\xa4\x93\xf4\n\xab}\xc2\xfa\xcdBw\x8b\xd0\xde*4\xb7\xf1\x1bF|;\x8e\xac\xef@\xa8\xf5\x9dB\x1a\x8f8\x9f\x00\x9b'\x13\x85f\x12\xe2h2\x90NA\x9a\xea\xa9\xc2l\x9aPM\x17\xd2\f\xa1\x9b)\xb4\xb3\x84f6\x86gs\x005\x17\xa9\xa2\xbb`\xe9nX\xa2{dI}/\xe2\xe4>\xa0z?P\u007f\x00p\x0f\x02\xf6!\xc0<\xec\x9fr\xc5#\xfeT\x85~\xd4'\xfe\xbe=\x06\xd0\xe3\x9c\xe5O\xb0ɓ'\x01\xf3\x14g\xd1\xd3\\\xd3g8J\xf5\xb3@\xf6\x1c\xa0\x9e\a\xe8\x05\xc0\xbd\bؗ\x00\xf32\x0f\xcb^\xe1\xaa^\xe5H\xd1kl\xe8u6Do`\r\xfd&g\xc9[\\\xabos\xad\xbf\xc3ս\xcbվ\xc7ռ\xefkW\xe5\x03?\xa0\xab\xa2?\xf4g\xe6/B>\x12\xd2Lj\xf3O`\xf3\xe4S\xa1\xf9\fq\xf49\x90~\x814\xd5_\n\xb3\xaf\x84\xeak!}#t\xdf\n\xedwB\xf3=\x86g?\x00\xeaG\xa4\x8a~\x82\xa5\x9fa\x89~\x91%\xf5\xaf\x88\x93߀\xea\xef@\xfd\x0f\xc0\xfd\tؿ\x00\xf37\x10\xff\x83\x81\xb1\xfe\xd7?=\xf1E\xc8\u007fA\xd0<\xd3\xd1\xc9\xff7\x1dA\x9e\xb4\x05aڥ\x13u\b\xd3Ni\xa4z@\x10\xd9\xc0 ԼA\xd0|A\xb8\xf9\x83\xb0\v\x04a\x16\x94i\xb2\x85\x84jai(ZD\x02ZT\x02\xa2\xc5\xc2\x0e\xf4\xe2\xd2I\x96\x10V\x97\x14֗\x12\xba\xa5\x85v\x19\xa1Yv\xba\xbf\x87\xf1r\xd3\xfd\xed\x8e\xf5\xf2>\xe4/BV\x10Ҋ\x88\xf3\x95`\xf3de\xa1Y\x05q\xb4*\x90\xae\x864ի\v\xb35\x84jM!\xad%tk\v\xed:B\xb3.\x86g\xeb\x01j}\xa4\x8a6\x80\xa5\ra\x896\x92%\xf5ƈ\x93M\x80\xea\xa6@}3\xc0m\x0e\xd8-\x00\xb3\xa5?P\xab\xd8ʛV\xa1\xb7\xf6\xc3\xf9\x8b\x90m\x84\xb4-\xe2|;\xd8<\xd9^hv@\x1c\xed\b\xa4;!M\xf5\xce\xc2l\x17\xa1\xdaUH\xbb\t\xdd\xeeB\xbb\x87\xd0\xec\x89\xe1\xd9^\x80\xda\x1b\xa9\xa2A\xb0\xb4\x0f,Ѿ\xb2\xa4\x1e\x8c8\x19\x02T\x87\x02\xf5a\x80\xdb\x0f\xb0\xfb\x03\xe6\x80\xe9mQ\xf3@\u007f\xaa\xa6>\xc8'\xfe\x03\xcf\xc1\x00\r\xe7,?\x84M\x9e\x1c\n\x98\xc38\x8b\x0e皎\xe0(\xd5#\x81\xec\b@\x1d\t\xd0Q\x80;\x1a\xb0\xc7\x00\xe6X\x1e\x96U\xb8*ő\xa2*\x1b\x8a\xd8\x10\xc5XC\x8f\xe2,\xd1\\\xab\x86k\xbd\xc6\xd5%\\m\x9d\xabI\xfd\xa5\xfc\xf5\x03?\xcb\xfc\x8f\x9d\x9c\x9f\x9e\xfcs\xa8\b\x82\xact\xf2\xd1\x12\xe4I#\bC҉\x9c0mJ#\xd5c\x82Ⱥ\x82Pe\x10\xd4\n\u008d\r\xc2\x1e\x17\x849^\xa6\xc9N\x10\xaa\x13\xa5\xa1\xe8$\t\xe8d\t\x88N\t;ЧJ'9MX=]X?C\xe8\xce\x14ڳ\x84\xe6l\u007f\xe4\xae\xf8\x1c\x1cY\x9f\x8bP\xeb\xf3\x84t>\xe2\xfc\x02\xd8<\xb9Ph.B\x1c]\f\xa4\x97 M\xf5\xa5\xc2\xec2\xa1\xba\\HW\bݕB{\x95\xd0\\\x8d\xe1\xd95\x80\xba\x16\xa9\xa2\xeb`\xe9zX\xa2\x1bdI}#\xe2\xe4&\xa0:\x0e\xa8w\x03\xae\a\xb0\xbd\x80\xe9\xe3\x03\xd5nfSӷ\xf0p\xff9\xeeV!݆8\xbf\x1d6O\xee\x10\x9a;\x11G\xe3\x81t\x02\xd2TO\x14f\x93\x84j\xb2\x90\xa6\b\xddT\xa1\x9d&4\xd31<\x9b\x01\xa8\x99H\x15͂\xa5ٰDsdI=\x17qr\x17P\xbd\x1b\xa8\xdf\x03\xb8{\x01{\x1f`\xee\xe77\x85\xe6\x03\xfc\x82j\xea\a\xf9%\xe8_F\x0f\t\xe9a\xc4\xf9#\xb0y\xf2\xa8\xd0<\x868z\x1cH\x9f@\x9a\xea'\x85\xd9SB\xf5\xb4\x90\x9e\x11\xbag\x85\xf69\xa1y\x1eó\x17\x00\xf5\"RE/\xc1\xd2˰D\xafȒ\xfaU\xc4\xc9k@\xf5u\xa0\xfe\x06\xe0\xde\x04\xec[\x80y\xdb\x1f(n\xbe\xe3M\xdc\xd4\xef\xfa\xe1\xfcE\xc8{Bz\x1fq\xfe\x01l\x9e|(4\x1f!\x8e>\x06\xd2O\x90\xa6\xfaSa\xf6\x99P}.\xa4/\x84\xeeK\xa1\xfdJh\xbe\xc6\xf0\xec\x1b@}\x8bT\xd1w\xb0\xf4=,\xd1\x0f\xb2\xa4\xfe\x11q\xf2\x13P\xfd\x19\xa8\xff\x02\xb8_\x01\xfb\x1b`~\xe7;T\xfb\x83OW\xd3\u007f\xf2\xe3\xe1\x9fr\u007f\t\xe9o\xc4\xf9?\xb0y\xf2\xaf\xd0\xfc\x878\x9ag\x06#\xfd\xdf\fNS\xdd&\xccڅ\xaaCH\x9dB7@h\a\nͼ\x18\x9e\xcd\a\xa8\xf9\x91*Z\x00\x96\x16\x84%Z\b\xcc\xf5\u0088\x93E\x80\xea\xa2@}1\xc0-\x0e\xd8%\x00\xb3䌶\xa8\xb6Ԍ\xf6\xa8\xa6\x97\xf6\t\xff\xa51\x80\x96\xe5,_\x8eM\x9e,\x0f\x98\x158\x8bV䚮\xc4Q\xaaW\x06\xb2U\x00\xb5*@\xab\x01nu\xc0\xae\x01\x985yX\xb6\x16W\xb56G\x8a\xd6aC\xeb\xb2!Z\x0fk\xe8\xf59K6\xe0Zݐk}#\xaenc\xaev\x13\xaef\xd3\x19\xfc?\xb5\xdfl\x06\xffO\xed\xf5\xe6\xfe\xcc\xfc]\xc8\x16B\xda\x12q\xbe\x15l\x9el-4\xdb \x8e\xb6\x05\xd2퐦z{a\xb6\x83P\xed(\xa4\x9d\x84ng\xa1\xddEhv\xc5\xf0l7@\xed\x8eT\xd1\x1e\xb0\xb4',\xd1^\xb2\xa4\xde\x1bq2\b\xa8\xee\x03\xd4\xf7\x05\xdc`\xc0\x0e\x01\xccP \x1e\x86\x81\xb1\xdeo\xc6\x00P\xef\x1f\x04\x1d \x9d\xfc@\t\xf2\xe4\xa0 \xcc\xc1҉\x86\v\xd3C\xa4\x91\xeaC\x83\xc8\x0e\vB\x1d\x1e\x04\x8d\b\u008d\f\xc2\x1e\x11\x849R\xa6Ɏ\x12\xaa\xa3\xa5\xa1\xe8\x18\t\xe8X\t\x88*a\aZI'\xa9\n\xab\x91\xb0\x1e\v\xdd(\xa1\xd5Bc\xf8\xb0\xadJ\x8dm\xab\xa2\x13\x9e\x87\xbfC\xa9\aA\xa9t\xf2L\x82<Ƀ0\x85t\"+LGK#Ս 2\nB\xb9 \xa8\x19\x84\x1b\x13\x84\xed\n\u00942M\xd6\x12\xaa\xb1\xd2Pt\x9c\x04t\xbc\x04D'\x84\x1d\xe8\x13\xa5\x93\x9c$\xac\x9e,\xac\x9f\"t\xa7\n\xediBs\xba0>C&\x88\xf5\x993\x06\x8a\xd0g\xf5+:;4\xf3sB\x94'\xe7\xf6+s^hF\xe7\a\x91^\x10z\xa9\xbe\xb0_e\x17\xf5+uq\xbf\xa2K\xfa\x95\xbb\xb4_\xd9\xcb\xfa\x95\xb9\x11\xb9\x9e_:\xc9\x02\xc2\xea\x82\xc2\xfaBB\xb7\xb0\xd0.\"4\x8b\xce\xe4\u007f\nf\xb1\x99\xfcO\xc1\xe8\xc59\xf4\xf7{\t!-\x898_\n6O\x96\x16\x9ae\x10G\xcb\x02\xe9rHS\xbd\xbc0[A\xa8V\x14\xd2JB\xb7\xb2Ю\"4\xabbx\xb6\x1a\xa0VG\xaah\rXZ\x13\x96h-YR\xaf\x8d8Y\a\xa8\xae\v\xd4\xd7\x03\xdc\xfa\x80\xdd\x000\x1b2\xba*\x1b\xf1\xc0\xae\x8aޘ\x1f\x18\xfe\x1eh\x93 hS\xe9\xe4\x9bI\x90'\x9b\aa\xb6\x90N\xb4\xa50\xddJ\x1a\xa9\xde:\x88l\x9b ԶA\xd0vA\xb8탰;\x04av\x94i\xb2\x9d\x84jgi(\xdaE\x02\xdaU\x02\xa2\xdd\xc2\x0e\xf4\xee\xd2I\xf6\x10V\xf7\x14\xd6\xf7\x12\xba\xbd\x85v\x90\xd0\xec#\x8c\xf7\x95\tb=x\xe6@\x11zH\xbf\xa2\xa1\xa1\x99\x0f\vQ\x9e\xec\u05ef\xcc\xfe\xa1\x19\x1d\x10Dz`\xe8\xa5\xfa\xa0~\x95\x1dܯ\xd4\xf0~E\x87\xf4+wh\xbf\xb2\x87\xf5+sx\x98.\x1b\x11\x84\x1a\x19z\x8a\x8e\b\x11\x1d\x19\"\xa2\xa3\xfa7\xa4\x8f\x0e\xcd\xe4\x98 \xaa\xc7\x06Q\xaf\x04\xe1T\x10\xb6\x1a\x84\x89p\xf3\xe3\x18O\x97X\x8f✿?\xd2A\x90\x91N^\x93 O\x92 L]:Q*L3i\xa4:\x0f\"+\x82P6\b\x1a\x1d\x84k\x04a)\b\xe3d\x9a\xac)Tc\xa4\xa1\xa8K\x02*% j\x85\x1d\xe8\xb1\xd2I\x8e\x13V\x8f\x17\xd6O\x10\xba\x13\x85\xf6$\xa19\x99\x0f\xdb*N\xc1K\xb8Ч\xe2\xb5\xef_\xb6\xa7\x05A\xa7K'?C\x82<93\bs\x96t\xa2\xb3\x85\xe99\xd2H\xf5\xb9Ad\xe7\x05\xa1\xce\x0f\x82.\b\xc2]\x18\x84\xbd(\bs\xb1L\x93]\"T\x97JC\xd1e\x12\xd0\xe5\x12\x10]\x11v\xa0\xaf\x94Nr\x95\xb0z\xb5\xb0~\x8d\xd0]+\xb4\xd7\t\xcd\xf5\xfc\x06ռ\x81\x8f\xdc\xd47r\xe8?_\xde$\xa4q\x88\xf3n\xd8<\xe9\x11\x9a^\xc4Q\x1f\x90ތ4շ\b\xb3[\x85\xea6!\xdd.tw\b\xed\x9dB3\x1eó\t\x80\x9a\x88T\xd1$X\x9a\fK4E\x96\xd4S\x11'Ӏ\xeat\xa0>\x03p3\x01;\v0\xb3\U0005a34b9x\xee\xfa\x1f\xaas\xf1\xdc石w\xf5+\xba;4\xf3{B\x94'\xf7\xf6+s_hF\xf7\a\x91>\x10z\xa9~\xb0_e\x0f\xf5+\xf5p\xbf\xa2G\xfa\x95{\xb4_\xd9\xc7\xfa\x95y\x95\x86\xa2\xcf$\xa0\xcf% \xfa\"\xec@\u007f)\x9d\xe4+a\xf5ka\xfd\x1b\xa1\xfbVh\xbf\x13\x9a\xefq\xd8\xda\x0f\xb05\xfd#\xe6\xf1\x1f\xc5\u007f\n\x82~\x96N\xfe\x8b\x04y\xf2k\x10\xe67\xe9D\xbf\v\xd3?\xa4\x91\xea?\x83\xc8\xfe\nB\xfd\x1d\x04\xfd\x13\x84\xfb7\b\xfb_\x10f\x9eY\x98&\xfb\x9fP\xb5\xcdBCQ\xbb\x04\xd4!\x01Q\xa7\x88\\\x0f\x90N2PX\x9dWX\x9fO\xe8\xe6\x17\xda\x05\x84f\xc1Y\xfc\x06\xd5\\\x88m\xab\xa9\x17\xe6y\xf8{\xaeE\x82\xa0E\xa5\x93/&A\x9e,\x1e\x84YB:ђ\xc2t)i\xa4z\xe9 \xb2e\x82P\xcb\x06A\xcb\x05\xe1\x96\x0f®\x10\x84YQ\xa6\xc9V\x12\xaa\x95\xa5\xa1h\x15\thU\t\x88V\v;ЫK'YCX]SX_K\xe8\xd6\x16\xdau\x84f]>l\xdc\\\x8fm\xdc\xd4\xeb\xf3<\xfc\x1d\xd8\x06AІ\xd2\xc97\x92 O6\x0e\xc2l\"\x9dhSa\xba\x994R\xbdy\x10\xd9\x16A\xa8-\x83\xa0\xad\x82p[\aa\xb7\t\xc2l+\xd3d\xdb\t\xd5\xf6\xd2P\xb4\x83\x04\xb4\xa3\x04D;\x85\x1d蝥\x93\xec\"\xac\xee*\xac\xef&t\xbb\v\xed\x1eB\xb3'\xeelm/\x9c\xbd\xa6\xf7ƃ\xe6\x9fƃ\x82\xa0}\xa4\x93\xef+A\x9e\f\x0e\xc2\f\x91N4T\x98\x0e\x93F\xaa\xf7\v\"\xdb?\bu@\x10t`\x10\xee\xa0 \xec\xc1A\x98\xe12Mv\x88P\x1d*\rE\x87I@\x87K@4\"\xec@\x8f\x94Nr\x84\xb0z\xa4\xb0~\x94\xd0\x1d-\xb4\xc7\bͱ\xb3\xf8\x9fb\xac\xf0\x91kZq\xc8\u007f\xa1LH\x11\xe2<\x86͓QB\xa3\x11G\x06HkHS\x9d\b\xb3\xbaP\xa5Bʄ.\x17\xdaBh,\x86g\xa3\x01\xd5@\xaa\x88`\xc9\xc1\x125eI=\x06q\xd2\x05TK\xa0\xde\x02\xdcX\xc0\x1e\a\x98\xe3g\xb55*'\xccjoT\xf4\x89\xb3:|\xd5'\x01t2g\xf9)l\xf2\xe4T\xc0\x9c\xc6Yt:\xd7\xf4\f\x8eR}&\x90\x9d\x05\xa8\xb3\x01:\ap\xe7\x02\xf6<\xc0\x9c\xcfò\v\xb8\xaa\v9Rt\x11\x1b\xba\x98\r\xd1%XC_\xcaYr\x19\xd7\xea\xe5\\\xebWpuWr\xb5Wq5Ws\x8d\xaf\xe1\x01\xb1\xbevV'C_'\xa4\xeb\x11\xe77\xc0\xe6ɍBs\x13\xe2h\x1c\x90v#Mu\x8f0\xeb\x15\xaa>!\xdd,t\xb7\b\xed\xadBs\x1b\x86g\xb7\x03\xea\x0e\xa4\x8a\ue125\xf1\xb0D\x13dI=\x11q2\t\xa8N\x06\xeaS\x007\x15\xb0\xd3\x003\xdd\x1f\xa8U\x99\xe1M\xab\xa2g\xfa\xe1\xfc\xcd\xd8,!\xcdF\x9cρ͓\xb9Bs\x17\xe2\xe8n \xbd\ai\xaa\xef\x15f\xf7\t\xd5\xfdBz@\xe8\x1e\x14ڇ\x84\xe6a\f\xcf\x1e\x01ԣH\x15=\x06K\x8f\xc3\x12=!K\xea'\x11'O\x01է\x81\xfa3\x80{\x16\xb0\xcf\x01\xe6y ~\x01\x03c\xfd\xe2\xac\x01\xa0~)\bzY:\xf9+\x12\xe4ɫA\x98פ\x13\xbd.LߐF\xaa\xdf\f\"{+\b\xf5v\x10\xf4N\x10\xee\xdd \xec{A\x98\xf7e\x9a\xec\x03\xa1\xfaP\x1a\x8a>\x92\x80>\x96\x80蓰\x03\xfd\xa9t\x92τ\xd5υ\xf5/\x84\xeeK\xa1\xfdJh\xbe\xf6w/.\xbe\xf1G\xf6\xbf\xa7}\xebC\xfe\xc5\xed;!}\x8f8\xff\x016O~\x14\x9a\x9f\x10G?\x03\xe9/HS\xfd\xab0\xfbM\xa8~\x17\xd2\x1fB\xf7\xa7\xd0\xfe%4\u007fcx\xf6\x0f\xa0\xfeE\xaa\xe8?X\x9ag6[\xa2\xff\x81\xb9n\x9b\xcdq\xd2\x0eT;\x80z'\xe0\x06\x00v `\xe6\x9d\xdd\u0588\xe7\x9b\xedO\xa5\xe7\xe7D\xeb\x05\x00Z\x90\xb3|!6y\xb20`\x16\xe1,Z\x94k\xba\x18G\xa9^\x1cȖ\x00Ԓ\x00-\x05\xb8\xa5\x01\xbb\f`\x96\xe5a\xd9r\\\xd5\xf2\x1c)Z\x81\r\xadȆh%\xac\xa1W\xe6,Y\x85kuU\xae\xf5ո\xbaչ\xda5\xb8\x9a5\xfd\xa5\xad\xb8X˟\x96\xbf\x97Z{\xf6\x00P\xaf\x13\x04\xad+\x9d|=\t\xf2d\xfd \xcc\x06҉6\x14\xa6\x1bI#\xd5\x1b\a\x91m\x12\x84\xda4\b\xda,\b\xb7y\x10v\x8b ̖2M\xb6\x95Pm-\rE\xdbH@\xdbJ@\xb4]\u0601\xde^:\xc9\x0e\xc2\xea\x8e\xc2\xfaNB\xb7\xb3\xd0\xee\"4\xbb\xfa#\xb7\xe2\xddpd\xbd;B\xad\xf7\x10Ҟ\x88\xf3\xbd`\xf3do\xa1\x19\x848\xda\aH\xf7E\x9a\xea\xc1\xc2l\x88P\r\x15\xd20\xa1\xdbOh\xf7\x17\x9a\x030<;\x10P\a!Ut0,\r\x87%:D\x96ԇ\"N\x0e\x03\xaa\x87\x03\xf5\x11\x80\x1b\t\xd8#\x00s\xa4\u007f\xca\x15G\xf9S\x15\xfah\x9f\xf8\xfbv\f@\xc7r\x96W\xd8\xe4\x89\x02L\x95\xb3(\xe2\x9a\xc6\x1c\xa5z\x14\x90i@\x19\x80j\x80K\x00[\aL\xcaò\x8c\xab\xca9RT\xb0!ˆh4\xd6\xd0\r\xce\x12\xe2Zu\\\xebM\xaen\fW\xdb\xc5Ք\xbevUZ~@WE\x8f\xf5g\xe6o\x95\x8e\x13\xd2\xf1\x88\xf3\x13`\xf3\xe4D\xa19\tqt2\x90\x9e\x824է\n\xb3ӄ\xeat!\x9d!tg\n\xedYBs6\x86g\xe7\x00\xea\\\xa4\x8a\u0383\xa5\xf3a\x89.\x90%\xf5\x85\x88\x93\x8b\x80\xea\xc5@\xfd\x12\xc0]\n\xd8\xcb\x00s9\x10_\x81\x81\xb1\xbe\xd2?=\xf1\xcd\xd1UA\xd0\xd5\xd2ɯ\x91 O\xae\r\xc2\\'\x9d\xe8zaz\x834R}c\x10\xd9MA\xa8qAPw\x10\xae'\b\xdb\x1b\x84\xe9\x93i\xb2\x9b\x85\xea\x16i(\xbaU\x02\xbaM\x02\xa2\xdb\xc3\x0e\xf4\x1d\xd2I\xee\x14V\xc7\v\xeb\x13\x84n\xa2\xd0N\x12\x9a\xc9|\x0f\xe3)|\xbbc=Շ\xfc\xed\xd04!MG\x9cπ͓\x99B3\vq4\x1bH\xe7 M\xf5\\av\x97P\xdd-\xa4{\x84\xee^\xa1\xbdOh\xee\xc7\xf0\xec\x01@=\x88T\xd1C\xb0\xf40,\xd1#\xb2\xa4~\x14q\xf2\x18P}\x1c\xa8?\x01\xb8'\x01\xfb\x14`\x9e\xe67\x85\xe2\x19~A\x15\xfaY~\t\xfa\x97\xd1sBz\x1eq\xfe\x02l\x9e\xbc(4/!\x8e^\x06\xd2W\x90\xa6\xfaUa\xf6\x9aP\xbd.\xa47\x84\xeeM\xa1}Kh\xde\xc6\xf0\xec\x1d@\xbd\x8bT\xd1{\xb0\xf4>,\xd1\a\xb2\xa4\xfe\x10q\xf2\x11P\xfd\x18\xa8\u007f\x02\xb8O\x01\xfb\x19`>\xf7o\n\xcd/\xfc\xa9\x9a\xfaK\x9f\xf8OP_\x01\xf45g\xf97l\xf2\xe4[\xc0|\xc7Y\xf4=\xd7\xf4\a\x8eR\xfd#\x90\xfd\x04\xa8\x9f\x01\xfa\x05p\xbf\x02\xf67\xc0\xfc\xceò?\xb8\xaa?9R\xf4\x17\x1b\xfa\x9b\r\xd1?XC\xff\xcbY\xf2\x1f\xd7\xea\t\xf2d\xfe \xcc\x02҉\x16\x14\xa6\vI#\xd5\v\a\x91-\x12\x84Z4\bZ,\b\xb7x\x10v\x89 ̒2M\xb6\x94P--\rE\xcbH@\xcbJ@\xb4\\\u0601^^:\xc9\n\xc2\xea\x8a\xc2\xfaJB\xb7\xb2Ю\"4\xab\xfa#wū\xe1\xc8zu\x84Z\xaf!\xa45\x11\xe7k\xc1\xe6\xc9\xdaB\xb3\x0e\xe2h] ]\x0fi\xaa\xd7\x17f\x1b\bՆB\xdaH\xe86\x16\xdaM\x84fS\f\xcf6\x03\xd4\xe6H\x15m\x01K[\xc2\x12m%K\xea\xad\x11'\xdb\x00\xd5m\x81\xfav\x80\xdb\x1e\xb0;\x00fG>Pm'65\xbd3\x0f\xf7\x1f\xf0v\x11Ү\x88\xf3\xdd`\xf3dw\xa1\xd9\x03q\xb4'\x90\xee\x854\xd5{\v\xb3AB\xb5\x8f\x90\xf6\x15\xba\xc1B;Dh\x86bx6\fP\xfb!U\xb4?,\x1d\x00Kt\xa0,\xa9\x0fB\x9c\x1c\fT\x87\x03\xf5C\x00w(`\x0f\x03\xcc\xe1\xfe@\xad\xe6\boZM=\xd2\x0f\xe7o]\x8e\x10ґ\x88\xf3\xa3`\xf3\xe4h\xa19\x06qt,\x90V\x90\xa6Z\t\xb3\xaaPEB\x8a\x85n\x94\xd0j\xa11\x18\x9e\xd5\x00\x95 UT\x87\xa5\x14\x96(\x93%u\x8e8)\x80\xaa\x05\xea\xa3\x01\xd7\x00,\x01\xc6\xf9\x03\xc5ͦ7qS\x8f\xf1\xc3\xf9\x9b\x95.!\x95\x88\xf3\x16l\x9e\x8c\x15\x9a\xe3\x10G\xc7\x03\xe9\tHS}\xa20;I\xa8N\x16\xd2)Bw\xaaО&4\xa7cxv\x06\xa0\xceD\xaa\xe8,X:\x1b\x96\xe8\x1cYR\x9f\x8b89\x0f\xa8\x9e\x0f\xd4/\x00܅\x80\xbd\b0\x17\xf3\x1d\xaa]§\xab\xe9K\xf9\xf1\xf0O\xb9˄t9\xe2\xfc\n\xd8<\xb9Rh\xaeB\x1c]\r\xa4\xd7 M\xf5\xb5\xc2\xec:\xa1\xba^H7\bݍB{\x93Ќ\xc3\xf0\xac\x1bP=H\x15\xf5\xc2R\x1f,\xd1Ͳ\xa4\xbe\x05qr+P\xbd\r\xa8\xdf\x0e\xb8;\x00{'`\xc6\xcfik\xd4&\xf8S\xd5\xf4D\x9f\xf0_\x1a\x03h2g\xf9\x146y2\x150\xd38\x8b\xa6sMgp\x94\xea\x99@6\vP\xb3\x01\x9a\x03\xb8\xb9\x80\xbd\v0w\xf3\xb0\xec\x1e\xae\xea^\x8e\x14\xdddž\xeegC\xf4\x00\xd6\xd0\x0fr\x96<ĵ\xfa0\xd7\xfa#\\ݣ\\\xedc\\\xcd\xe3sڲ\xca\x13sڳ\x8a~rN\x87\xaf\xfa)\x80\x9e\xe6,\u007f\x86M\x9e<\v\x98\xe78\x8b\x9e皾\xc0Q\xaa_\x04\xb2\x97\x00\xf52@\xaf\x00\xeeU\xc0\xbe\x06\x98\xd7yX\xf6\x06W\xf5&G\x8a\xdebCo\xb3!z\ak\xe8w9K\xde\xe3Z}\x9fk\xfd\x03\xae\xeeC\xae\xf6#\xae\xe6c\xae\xf1'< ֟\xce\xe9d\xe8τ\xf49\xe2\xfc\v\xd8<\xf9Rh\xbeB\x1c}\r\xa4\xdf M\xf5\xb7\xc2\xec;\xa1\xfa^H?\bݏB\xfb\x93\xd0\xfc\x8c\xe1\xd9/\x80\xfa\x15\xa9\xa2\xdf`\xe9wX\xa2?dI\xfd'\xe2\xe4/\xa0\xfa7P\xff\ap\xff\x02\xf6?\xc0\xcc3\xb7=kU\xfe7\xb7\xc3W\xdd6\xb7\x93\xa1ۅԁ8\xef\x84͓\x01B3\x10q4/\x90·4\xd5\xf3\v\xb3\x05\x84jA!-$t\v\v\xed\"B\xb3(\x86g\x8b\x01jq\xa4\x8a\x96\x80\xa5%a\x89\x96\x92%\xf5҈\x93e\x80\xea\xb2@}9\xc0-\x0f\xd8\x15\x00\xb3\"\x10\xaf\x84\x81\xb1^y\xee\x00P\xaf\x12\x04\xad*\x9d|5\t\xf2d\xf5 \xcc\x1a҉\xd6\x14\xa6kI#\xd5k\a\x91\xad\x13\x84Z7\bZ/\b\xb7~\x10v\x83 ̆2M\xb6\x91Pm,\rE\x9bH@\x9bJ@\xb4Y\u0601\xde\\:\xc9\x16\xc2\xea\x96\xc2\xfaVB\xb7\xb5\xd0n#4\xdb\xfa\xbb\x17\x17\xdb\xf9#\xfb\xdfs\xb6\xf7!\xff\u07b3\x83\x90vD\x9c\xef\x04\x9b';\v\xcd.\x88\xa3]\x81t7\xa4\xa9\xde]\x98\xed!T{\ni/\xa1\xdb[h\a\t\xcd>\x18\x9e\xed\v\xa8\xc1H\x15\r\x81\xa5\xa1\xb0D\xc3dI\xbd\x1f\xe2d\u007f\xa0z\x00P?\x10p\a\x01\xf6`\xc0\f\x9fۖŇ\xf0\xa9\xf4\xa1\x9ch}\x18@\x87s\x96\x8f`\x93'#\x01s\x04gё\\ӣ8J\xf5\xd1@v\f\xa0\x8e\x05\xa8\x028\x05\xd8*`\"\x1e\x96\xc5\\\xd5(\x8e\x14i6d\xd8\x10հ\x86N8K\xea\\\xab)\xd7z\xc6\xd5\xe5\\m\xc1\xd5X~\xca\xc5\xc5h\xbe)\xfc\xaf\x8a\xf1\xdd\xc4?+\x16\x049\xe9\xe4M\t\xf2dL\x10\xa6K:Q)L[\xd2H\xf5\xd8 \xb2\xe3\x82P\xc7\aA'\x04\xe1N\f\u009e\x14\x849Y\xa6\xc9N\x11\xaaS\xa5\xa1\xe84\t\xe8t\t\x88\xce\b;\xd0gJ'9KX=[X?G\xe8\xce\x15\xda\xf3\x84\xe6|~È/\xc0\x91\xf5\x85\b\xb5\xbeHH\x17#\xce/\x81͓K\x85\xe62\xc4\xd1\xe5@z\x05\xd2T_)̮\x12\xaa\xab\x85t\x8d\xd0]+\xb4\xd7\t\xcd\xf5\x18\x9e\xdd\x00\xa8\x1b\x91*\xba\t\x96\xc6\xc1\x12u˒\xba\aq\xd2\vT\xfb\x80\xfà\xbb\x05\xb0\xb7\x02\xe66\xff\x94+n\xf7\xa7*\xf4\x1d>\xe1\u007f\x85\f\xa0\xf1\x9c\xe5\x13\xd8\xe4\xc9D\xc0L\xe2,\x9a\xcc5\x9d\xc2Q\xaa\xa7\x02\xd94@M\ah\x06\xe0f\x02v\x16`f\xf3\xb0l\x0eW5\x97#Ew\xb1\xa1\xbb\xd9\x10݃5\xf4\xbd\x9c%\xf7q\xad\xdeϵ\xfe\x00W\xf7 W\xfb\x10W\xf3\xb0\xaf]\x95G\xfc\x80\xae\x8a~ԟ\x99\xbf\byLH\x8f#Ο\x80͓'\x85\xe6)\xc4\xd1\xd3@\xfa\f\xd2T?+̞\x13\xaa\xe7\x85\xf4\x82н(\xb4/\t\xcd\xcb\x18\x9e\xbd\x02\xa8W\x91*z\r\x96^\x87%zC\x96\xd4o\"N\xde\x02\xaao\x03\xf5w\x00\xf7.`\xdf\x03\xcc\xfb@\xfc\x01\x06\xc6\xfaC\xff\xf4\xc4\x17!\x1f\x05A\x1fK'\xffD\x82<\xf94\b\xf3\x99t\xa2υ\xe9\x17\xd2H\xf5\x97Ad_\x05\xa1\xbe\x0e\x82\xbe\t\xc2}\x1b\x84\xfd.\b\xf3\xbdL\x93\xfd T?JC\xd1O\x12\xd0\xcf\x12\x10\xfd\x12v\xa0\u007f\x95N\xf2\x9b\xb0\xfa\xbb\xb0\xfe\x87\xd0\xfd)\xb4\u007f\t\xcd\xdf|\x0f\xffO\xd3YGWn3Q\xbc\x9b\xa52333333Sd\xbfJ\x91m\xc5\xed(MSfffffZ\xe82\xef\xb6\xfd\xca\xcc\xcc\xcc\xed\xe7\xb9W\xef\x9f\xf9\xdd\xf9\xcdʲ\x1f%\xcf\xe7\xecI\xeb\x1f}\xba[\xf6\xdfFꍐ\xffH\x99n\x88\xea\xd0o\x88\xb6\xc1w\x90\xae?t>\x00(\a\u0096v\x10Y\r&\xcd\xf4\xa4\xcc@\xc6\x19\xc9z&\xd2͌\xe5\xd5,\x80\x99\x15\xd6\xc8lhev\xb4\"spK;'\xb4\x9f\v\xc8\xe6\x06\x8ay\x808/P\xcf\a\xb8\xf9\x874\x1f\n\xdd\v4M_\xb7]\xb0Y\xae7B\x16\"ea\xe8\xb0\b\xda\xe0\x17%\xddb\xd0\xf9\xe2@\xb9\x04li\x97$\xab\xa5H\xb34)ːqY\xb2^\x8et\xcbcy\xb5\x02`V\x845\xb2\x12ZY\x19\xad\xc8*\xdcҮ\n\xedW\x03\xb2Ձb\r \xae\t\xd4k\x01n\xed!\x1dU\xcf:\xcdU\xf5\xd8u\x1b\xd3|\xe1Y\x0f\x90\xf5Յ\r\xb4\t~C\xc0m\xa4.\xdfXk\xb9\x89\xaa\xd2n\nT\x9b\x01fs@\xb6\x00\xe2\x96@\xbd\x15\xe0\xb6\xd6e\xd56ZͶ\xaa\x8cl\xa7\x8dl\xaf\x8d\xc8\x0e\xd8\xc3\xee\xa8\xce\xef\xa45\xdbYk\xb1\x8bָ\xab\xd6z7\xadn\xf7\xe6\x9f\xf6\xb6\xba\xf7h\xaeV\xef{\xec9d\x10h\xf7JA\xf6\xe6$\xecC\x11\xfc\xbe)\xb8\xfd8\xc9\xf7'\xcb\x038(\xed\x81)T\a\xa5`\x0eNA\x0eI!\x1e\x9aB}X\n\xeep\x1e\xa6\xea$\x8d\xe1\xc0HF!9\x85H+\x9d\x81=\x82\x13o\xc9̑E\x17\x19=Y\x17\xa4+\x9bK\xeemU\xb8d\x1b \xad\xed&\xa5\x86\x0eG\xa2\r\xfe(\xd2\tt\x1e\x81\xb2\a\xb6\xb4G\x93U/i\x8e!\xa5\x8f\x8cǒ\xf5q\xa4;\x1e˫\x13\x00s\"\xac\x91\x93\xd0\xca\xc9hEN\xe1\x96\xf6Th\u007f\x1a\x90\x9d\x0e\x14g\x00\xf1L\xa0>\vpg\xeb\x05u\x9d\xa3M\x97=W\x977\xdf\xe3\xce#\xe5|\xe8p\x01\xda\xe0/$\xddE\xd0\xf9\xc5@y\tli/%\xab\xcbHs9)W\x90\xf1J\xb2\xbe\x8atWcyu\r`\xae\x855r\x1dZ\xb9\x1e\xad\xc8\r\xdc\xd2\xde\b\xedo\x02\xb2\x9b\x81\xe2\x16 \xde\nԷ\x01\xeev\xfdP\xe8\xb9C\xdfP=\xf6N}\v6o\xa3\xbbH\xb9\x1b:܃6\xf8{Iw\x1ft~?P>\x00[\xda\a\xc9\xea!\xd2\x82\xce?\x06\xcaO`K\xfb)Y}F\x9a\xcfI\xf9\x82\x8c_\x92\xf5W\xa4\xfb\x1a˫o\x00\xf3-\xac\x91\xef\xd0\xca\xf7hE~\xe0\x96\xf6Gh\xff\x13\x90\xfd\f\x14\xbf\x00\xf1W\xa0\xfe\rp\xbf7\x1f\xdb]\u007f4W\xd5e\xffl\x8c\xfe\xa71@\xfeV\x17\xfe\xd1&\xf8\u007f\x01\xf7\x9f\xba|\xba\xa1M-\xfb\rmTi;\x80\xaa?`\x06\x002\x10\x88\x83\x80z0\xe0\xa6\xd7e\xd5\fZ͌\xaa\x8c̤\x8d̬\x8d\xc8,\x8a`gU\xe7gӚͮ\xb5\x98Ck\x9cSk=\x97V7\xf7\xd0\x0e\xd39\xcf\xd0\xfe\xa6\xd3\xce;t@S\xed|\x80̯.,\xa0M\xf0\v\x02n!u\xf9\xc2Z\xcbET\x95vQ\xa0Z\f0\x8b\x03\xb2\x04\x10\x97\x04\xea\xa5\x00\xb7\xb4.\xab\x96\xd1j\x96Ued9mdymDV\xc0\x1evEu~%\xad\xd9\xcaZ\x8bU\xb4\xc6U\xb5֫iu\xabkm\xad\xa1\vZv͡\x03\x15v-Rֆ\x0e\xeb\xa0\r~]ҭ\a\x9d\xaf\x0f\x94\x1b\xc0\x96vC\xb2ڈ4\x1b\x93\xb2\t\x197%\xeb\xcdH\xb79\x96W[\x00fKX#[\xa1\x95\xadъl\xc3-\xed\xb6\xd0~; \xdb\x1e(v\x00\xe2\x8e@\xbd\x13\xe0vn.\xa8\xafs\x97\xa6\xe9봻6\xcb\xf5F\xc8n\xa4\xec\x0e\x1d\xf6@\x1b\xfc\x9e\xa4\xdb\v:\xdf\x1b(\xf7\x81-\xed\xbed\xb5\x1fi\xf6'\xe5\x002\x1eH\xd6\a\x91\xee`,\xaf\x0e\x01̡\xb0F\x0eC+\x87\xa3\x15\xe9\xe4\x96\xd6@\xfb\f\xc8r\xa0h\x01\xf1\b\xa0\xb6\x80s@\xab\v\v[\xd6\x0f\x1d\x04\xda\"\x05)9\t\x15E\xf0!\x05\xd7\xcdI^\x93\xe5\x91\x1c\x94\xf6\xa8\x14*I\xc1\xc4\x14\xa4'\x85xt\nuo\n\xee\x18\x1e\xa6\xea#ͱ\x1c\x189\x8eB\x8e\xa7\x109!\x9d\x81=\x91\x13\u007f\x12\x99\x9dL\x16\xa7\x90\xf1T\xb2>\x8dt\xa77\xcf^\xab\xfb\x8c撛\xdfs\xcel\xa4\xfe\xdes\x16)gC\x87s\xd0\x06\u007f.\xe9\u0383\xce\xcf\a\xca\v`K{!Y]D\x9a\x8bI\xb9\x84\x8c\x97\x92\xf5e\xa4\xbb\x1c˫+\x00s%\xac\x91\xab\xd0\xca\xd5hE\xae\xe1\x96\xf6Zh\u007f\x1d\x90]\x0f\x147\x00\xf1F\xa0\xbe\tp77\x1f\n\xad[\xf4\xaa\xec\xadj\xac\xbd\r\x90\xdbՅ;\xb4\t\xfeN\xc0ݥ.\xbf[ky\x8f\xaa\xd2\xde\vT\xf7\x01\xe6~@\x1e\x00\xe2\x83@\xfd\x10\xe0\x1e\xd6e\xd5#Zͣ\xaa\x8c<\xa6\x8d<\xae\x8d\xc8\x13\xd8\xc3>\xa9\xce?\xa55{Zk\xf1\x8c\xd6\xf8\xac\xd6z\x88V7T_r\xad\xeea\xfa\xa4\xe8\xdf\xf8\xd2g\x13\u007f\xe3+\x05\x19\xc1I\x18I\x11\xfc\xa8\x14\xdchN\xf21d9\x96\x83ҎK\xa1\x1a\x9f\x82\x99\x90\x82LL!NJ\xa1\x9e\x9c\x82\x9b\xc2\xc3TSI3\x8d\x03#\xcfS\xc8\v\x14\"/\xa63\xb0\xff\xe3ĿDf/\x93\xc5+d|\x95\xac_#\xdd\xeb\xfa\x81\xd1z\x03\x97l߄\xb4\xf6-Rކ\x0e\xef\xa0\r\xfe]ҽ\a\x9d\xbf\x0f\x94\x1f\xc0\x96\xf6C\xb2\xfa\x884\x1f\x93\xf2\t\x19?%\xeb\xcfH\xf79\x96W_\x00\xe6KX#_\xa1\x95\xafъ|\xc3-\xed\xb7\xd0\xfe; \xfb\x1e(~\x00\xe2\x8f@\xfd\x13\xe0~n^rݿ4W\xd5m\u007fm\x8c\xfei/@~W\x17\xfe\xd0&\xf8?\x01\xf7\x97\xba\xfco\xad\xe5?\xaaJ\xfb/P\xfd\a\x98\xe9\x86)\xa4\x1f\x10;\x80\xba?\xe0\x06\fk\x96U\x03\xb5\x9aA\xaa\x8c\f\xd6F\xa6\xd7Fd\x06E\xb03\xaa\xf33i\xcdf\xd6Z̢5Ϊ\xb5\x9eM\xab\x9b\xbd\xa9\xbd\x9ds4\vz;\xed\x9c\xc3\x06*\xec\\\xa4\xcc\r\x1d\xe6A\x1b\xfc\xbc\xa4\x9b\x0f:\x9f\x1f(\x17\x80-\xed\x82d\xb5\x10i\x16&e\x112.J\u058b\x91nq,\xaf\x96\x00̒\xb0F\x96B+K\xa3\x15Y\x86[\xdae\xa1\xfdr@\xb6\x02(G\u0096v\x14Y\x8d&\xcd\x18Rƒq\x1cY\x8f'\xdd\x04,\xaf&\x02f\x12\xac\x91\xc9he\nZ\x91\xa9\xdc\xd2N\x83\xf6\xcf\x03\xd9\v@\xf1\"\x10\xff\a\xd4/\x01\xeee\xbd\xa0\xaeW\xb4鲯\xea\xf2\xe6{\xdck\xa4\xbc\x0e\x1d\xde@\x1b\xfc\x9b\xa4{\v:\u007f\x1b(߁-\xed\xbbd\xf5\x1ei\xde'\xe5\x032~H\xd6\x1f\x91\xeec,\xaf>\x01̧\xb0F>C+\x9f\xa3\x15\xf9\x82[\xda/\xa1\xfdW@\xf65P|\x03\xc4o\x81\xfa;\xc0}\xaf\x1f\n=?\xe8\x1b\xaa\xc7\xfe\xa8o\xc1\xe6m\xf4\x13)?C\x87_\xd0\x06\xff+\xe9~\x83\xce\u007f\a\xca?`K\xfb'Y\xfdE\x9a\xbfI\xf9\x87\x8c\xff\x92\xf5\u007f\xa4\x9bn\xb8.\xaf\xfa\x01\xa6c\xb8Z#\xfd\xd1\xca\x00\xb4\"\x03\xc1`\aA\xfb\xc1@6=P\xcc\x00\xc4\x19\x81z&\xc0\xcd<\xbc\xf9m\xafg\x96\xa6i\xf5\xd8Y\x9b\xe5z#d6Rf\x87\x0es\xa0\r~N\xd2\xcd\x05\x9d\xcf\r\x94\xf3\xc0\x96v^\xb2\x9a\x8f4\xf3\x93\xb2\x00\x19\x17$\xeb\x85H\xb70\x96W\x8b\x00fQX#\x8b\xa1\x95\xc5ъ,\xc1-\xed\x92\xd0~) [\x1a(\x96\x01\xe2\xb2@\xbd\x1c\xe0\x96o.\xa8\xafk\x05\xbd\xba.\xbb\xa2>\x1e\xcdKn%RV\x86\x0e\xab\xa0\r~Uҭ\x06\x9d\xaf\x0e\x94k\xc0\x96vM\xb2Z\x8b4k\x93\xb2\x0e\x19\xd7%\xeb\xf5H\xb7>\x96W\x1b\x00fCX#\x1b\xa1\x95\x8dъl\xc2-\xed\xa6\xd0~3 \xdb\x1c(\xb6\x00\xe2\x96@\xbd\x15\xe0\xb6\x1e\xdea\xba\xb6i\xae\xaa\xcbn\xdb\x18\xfd\xdfA\x80l\xaf.\xec\xa0M\xf0;\x02n'u\xf9\xceZ\xcb]T\x95vW\xa0\xda\r0\xbb\x03\xb2\a\x10\xf7\x04\xea\xbd\x00\xb7\xb7.\xab\xf6\xd1j\xf6Ued?md\u007fmD\x0e\xc0\x1e\xf6@u\xfe \xad\xd9\xc1Z\x8bC\xb4\xc6C\xb5ևiu\x877\xd5tv6\vL\xa75\xcd5뽐\x8c\x94\x1c:\xb4\xd0\x06\u007f\x04\xe9,t\ue032\v\xb6\xb4\x9e\xac\nҔ\xa4Td\fd\xddM\xba\x1a˫#\x01s\x14\xac\x11A+\x11\xadH\x0f\xb7\xb4GC\xfb^ ;\x06(\xfa\x80x,P\x1f\a\xb8\xe3\x81\xd6\tXز'\x0e\x1f\x04ړR\x90\x939\t\xa7P\x04\u007fj\n\xee4N\xf2\xd3\xc9\xf2\f\x0eJ{f\n\xd5Y)\x98\xb3S\x90sR\x88\xe7\xa6P\x9f\x97\x82;\x9f\x87\xa9. ͅ\x1c\x18\xb9\x88B.\xa6\x10\xb9$\x9d\x81\xbd\x94\x13\u007f\x19\x99]N\x16W\x90\xf1J\xb2\xbe\x8atW\xeb\xc5\xf6u^\xa3m_\xa7\xbdV\x8f\xa3\xf7P\xaeKA\xae\xe7$\xdc@\x11\xfc\x8d)\xb8\x9b8\xc9o&\xcb[8(\xed\xad)T\xb7\xa5`nOA\xeeH!ޙB}W\n\xeen\x1e\xa6\xba\x874\xf7r`\xe4>\n\xb9\x9fB\xe4\x81t\x06\xf6AN\xfcCd\xf60Y\x05\xf7\x82>\xeb\xad\xee\x17\xf5\xf1h~\xb3\xfa\x9fz\xfdU\xeb\xa5\x14\xe4eN\xc2+\x14\xc1\xbf\x9a\x82{\x8d\x93\xfcu\xb2|\x83\x83Ҿ\x99B\xf5V\n\xe6\xed\x14\xe4\x9d\x14\xe2\xbb)\xd4\xef\xa5\xe0\xde\xe7a\xaa\x0fH\xf3!\aF>\xa2\x90\x8f)D>Ig`?\xe5\xc4\u007fFf\x9f\x93\xc5\x17d\xfc\x92\xac\xbf\"\xdd\xd7\xfa\x01\xd5\xfa\x06\x97l\xbf\x85\xb4\xf6;R\xbe\x87\x0e?\xa0\r\xfeG\xd2\xfd\x04\x9d\xff\f\x94\xbf\xc0\x96\xf6W\xb2\xfa\x8d4\xbf\x93\xf2\a\x19\xff$\xeb\xbfH\xf77\x96W\xff\x00\xe6_X#\xff\xa1\x95\xe9\x9e\xd3V\xa4\x1f\x18l\xc7s\xaa}\u007f \x1b\x00\x14\x03\x818\b\xa8\a\x03nz]\xd1\xd7\xea\x9e\xe1\xb9A\xa0\x9d\xf1\xb9\xc1\fv\xa6v\x92\x99\xd30̒T\U00033d93\x9b-\r\xf3\xd9S(\xe7H\xb3\xd2\xce\xd9N\xd5\\\xedd\xe6n'\x99\xa7\x9d\xe2\xbc\xedT\xcf\xd7Nn\xfet\xb8j\x81\x14̂ifd\xa1\xa4d\xe1\xa4D\x16i\x9f\x90]4\r\xfdb)d\x8b\xa7P,\x91B\\2\x85z\xa9\x14\xdc\xd2\xfax\xf4\xb5\x96\xe1\xe3a\x97\xa5\xb7v\xb9\x14dyN\xc2\n\x14\xc1\xaf\x98\x82[\x89\x93|e\xb2\\\x85\x83Ү\x9aB\xb5Z\nf\xf5\x14d\x8d\x14\xe2\x9a)\xd4k\xa5\xe0\xd6\xe6a\xaauH\xb3.\aF֣\x90\xf5)D6Hg`7\xe4\xc4oDf\x1b\x93\xc5&dܔ\xac7#\xdd\xe6\xcf5/\xe3\xee-\xf4\x92\xbb\xed\x96*\xf5\x8fc\x91\xb25t\xd8\x06m\xf0ےn;\xe8|{\xa0\xdc\x01\xb6\xb4;\x92\xd5N\xa4ٙ\x94]ȸ+Y\xefF\xbaݱ\xbc\xda\x030{\xc2\x1a\xd9\v\xad\xec\x8dVd\x1fni\xf7\x85\xf6\xfb\x01\xd9\xfe@q\x00\x10\x0f\x04\xea\x83\x00w\xb0\xa2\xb7\xf3\x10]\xd8\xdbi\x0f\xd5\aF\xef\x03\x1d\x96\x82\x1c\xceI\xe8\xa4\bޤ\xe02N\xf2\x9c,[\x1c\x94\xf6\x88\x14*\x9b\x82q)HW\nѧP\x17)\xb8\x92\x87\xa9*\xd2\x04\x0e\x8ctSHM!rd:\x03{\x14'^\xc8,\x92E\x0f\x19\x8f&\xeb^\xd2\x1dC\xb6\xfax\x80\x96=V_\xfb\xb8Gt\\;\xc9\xf1i\x18NH*\xf8\x13\xdbɝ\x94\x86\xf9\xc9)\x94\xa7\xa4YiOm\xa7\xea\xb4v2\xa7\xb7\x93\x9c\xd1N\xf1\xccv\xaa\xcfj'wv:\\uN\n\xe6\xdc43r^Rr~R\"\x17\xb4O\xc8^\x98\x86\xfe\xa2\x14\xb2\x8bS(.I!^\x9aB}Y\n\xeer<\xf9\xad+\xf0ri\xd9+\xd5\xeb\xfd\xa3\xabR\x90\xab9\t\xd7P\x04\u007fm\n\xee:N\xf2\xeb\xc9\xf2\x06\x0eJ{c\n\xd5M)\x98\x9bS\x90[R\x88\xb7\xa6Pߖ\x82\xbb\x9d\x87\xa9\xee ͝\x1c\x18\xb9\x8bB\xee\xa6\x10\xb9'\x9d\x81\xbd\x97\x13\u007f\x1f\x99\xddO\x16\x0f\x90\xf1A\xb2~\x88t\x0f\xe3\x03\xaa\xfb\x11\xbc\x85\xbb\xed\xa3x\xef7o\xdb\xc7R\x90\xc79\tOP\x04\xffd\n\xee)N\xf2\xa7\xc9\xf2\x19\x0eJ\xfbl\nՐ\x14\xcc\xd0\x14dX\nqx\n\xf5s)\xb8\x11\x01\xe2\xa7@\xfd\x19\xe0>\xc7{\xb6\xd5\xfd\x05^\xbb\xcd\x0f\xd5/\xf1\xdaן\xb3_\xb5\x93|\x9d\x86ᛤ\x82\xff\xb6\x9d\xdcwi\x98\u007f\x9fB\xf9C\x9a\x95\xf6\xc7v\xaa~j'\xf3s;\xc9/\xed\x14\u007fm\xa7\xfa\xb7vr\xbf\xa7\xc3U\u007f\xa4`\xfeL3#\u007f%%\u007f'%\xf2O\xfb\x84\xec\xbfi\xe8\xffK!\x9bn\x04C\xd1/\x85ؑB\xdd?\x057`\x84~R\xb7\x06\x8e\xc0\xe3a\a\xd1[;8\x05\x99\x9e\x930\x03E\xf03\xa6\xe0f\xe2$\x9f\x99,gᠴ\xb3\xa6P͖\x82\x99=\x05\x99#\x858g\n\xf5\\)\xb8\xb9y\x98j\x1e\xd2\xccˁ\x91\xf9(d~\n\x91\x05\xd2\x19\xd8\x059\xf1\v\x91\xd9\xc2d\xb1\b\x19\x17%\xeb\xc5H\xb78.\xb6k\t\xb4]vI\x1c\xa7\xf9*\xbeT\n\xb24'a\x19\x8a\xe0\x97M\xc1-\xc7I\xbe\xd7f)\xc8朄-(\x82\xdf2\x05\xb7\x15'\xf9\xd6d\xb9\r\a\xa5\xdd6\x85j\xbb\x14\xcc\xf6)\xc8\x0e)\xc4\x1dS\xa8wJ\xc1\xed\xcc\xc3T\xbb\x90fW\x0e\x8c\xecF!\xbbS\x88\xec\x91\xce\xc0\xeeɉߋ\xcc\xf6&\x8b}ȸ/Y\xefG\xba\xfd\xf5b[=\ah\xdb\xea\xb1\a\xeaq\xf4\x1e\xd8A)\xc8\xc1\x9c\x84C(\x82?4\x05w\x18'\xf9\xe1d\xd9\xc9AiM\nU\x96\x82\xc9S\x90V\n\xf1\x88\x14j\x9b\x82s\xfa\xa4\xe8_\x15\xd3g\x13\u007fV,\x05ٟ\x93p\x00E\xf0\a\xa6\xe0\x0e\xe2$?\x98,\x0fᠴ\x87\xa6P\x1d\x96\x829<\x05\xe9L!\x9a\x14\xea,\x05\x97\xf30U\x8b4Gp`\xc4R\x88\xa3\x10\xe9Jg`='\xbe \xb3\x92,*2\x06\xb2\xee&]\xdd\\r_\xebH\\\xb2=\n\xd2Z!%B\x87\x1e\xb4\xc1\x1fM\xba^\xe8\xfc\x18\xa0\xec\x83-\xed\xb1du\x1ci\x8e'\xe5\x042\x9eH\xd6'\x91\xeed,\xafN\x01̩\xb0FNC+\xa7\xa3\x159\x83[\xda3\xa1\xfdY@v6P\x9c\x03\xc4s\x81\xfa<\xc0\x9d\u07fc\xe4\xba/h\xae\xaa\xdb^\xd8\x18\xfd+d\x80\\\xac.\\\xa2M\xf0\x97\x02\xee2u\xf9\xe5Z\xcb+T\x95\xf6J\xa0\xba\n0W\x03r\r\x10\xaf\x05\xea\xeb\x00w\xbd.\xabn\xd0jnTe\xe4&m\xe4fmDn\xc1\x1e\xf6Vu\xfe6\xad\xd9\xedZ\x8b;\xb4\xc6;\xb5\xd6wiuw7\xb5\xb7\xf3\x9efAo\xa7\xbd\xb7\xb9f\xbd\xabt\x1f)\xf7C\x87\a\xd0\x06\xff \xe9\x1e\x82\xce\x1f\x06\xcaG`K\xfb(Y=F\x9a\xc7Iy\x82\x8cO\x92\xf5S\xa4{\x1a˫g\x00\xf3,\xac\x91!he(Z\x91a\xdc\xd2\x0e\x87\xf6\xcf\x01\xd9\b\xa0\x18\t\xc4Q@=\x1apc\x80\xd6X,l\xd9q\xcd\xcb\x13w\x8eƧ \x138\t\x13)\x82\x9f\x94\x82\x9b\xccI>\x85,\xa7rP\xdai)Tϧ`^HA^L!\xfe/\x85\xfa\xa5\x14\xdc\xcb'a\x03\x8a\xe07L\xc1m\xc4I\xbe1Yn\xc2Ai7M\xa1\xda,\x05\xb3y\n\xb2E\nq\xcb\x14\xea\xadRp[\xf30\xd56\xa4ٖ\x03#\xdbQ\xc8\xf6\x14\";\xa43\xb0;r\xe2w\"\xb3\x9d\xc9b\x172\xeeJֻ\x91n\xf7\xe6\x92{[{\xe0\x92힐\xd6\xeeE\xca\xde\xd0a\x1f\xb4\xc1\xefK\xba\xfd\xa0\xf3\xfd\x81\xf2\x00\xd8\xd2\x1eHV\a\x91\xe6`R\x0e!\xe3\xa1d}\x18\xe9\x0e\xc7\xf2\xaa\x130\x06\xd6H\x86Vr\xb4\"-ni\x8f\x80\xf6\x16\xc8\x1cPt\x01\xd1\x03u\x01\xb8R/\xa8\xabҦ\xcb\x06]\xde|\xc1\xeb&\xa5\x86\x0eG\xa2\r\xfe(\xd2\tt\x1e\x81\xb2\a\xb6\xb4G\x93U/i\x8e!\xa5\x8f\x8cǒ\xf5q\xa4;\x1e˫\x13\x00s\"\xac\x91\x93\xd0\xca\xc9hEN\xe1\x96\xf6Th\u007f\x1a\x90\x9d\x0e\x14g\x00\xf1L\xa0>\vpg7\x17\xd4\xd7s\x8e\xbe\xa1z\xec\xb9\xfa\x16l\xdeF\xe7\x91r>t\xb8\x00m\xf0\x17\x92\xee\"\xe8\xfcb\xa0\xbc\x04\xb6\xb4\x97\x92\xd5e\xa4\xb9\x9c\x94+\xc8x%Y_E\xba\xab\xb1\xbc\xba\x060\xd7\xc2\x1a\xb9\x0e\xad\\\x8fV\xe4\x06nio\x84\xf67\x01\xd9\xcd@q\v\x10o\x05\xea\xdb\x00w{sA\xad\x9e;\x9a\xa6\xd5c\xefl\x96띕\xbbH\xb9\x1b:܃6\xf8{Iw\x1ft~?P>\x00[\xda\a\xc9\xea!\xd2\xb6\xbb\xdei\xae\xaa˾\xdb\x18\xfdOc\x80\xbc\xaf.|\xa0M\xf0\x1f\x02\xee#u\xf9\xc7Z\xcbOT\x95\xf6S\xa0\xfa\f0\x9f\x03\xf2\x05\x10\xbf\x04\xea\xaf\x00\xf7\xb5.\xab\xbe\xd1j\xbeUe\xe4;m\xe4{mD~\xc0\x1e\xf6Gu\xfe'\xad\xd9\xcfZ\x8b_\xb4\xc6_\xb5ֿiu\xbf7U:\xffh\x16H\xa7\xfd\xb3\xb9f\xbd\x17\xf2\x17)\u007fC\x87\u007f\xd0\x06\xff/\xe9\xfe\x83Χ\x1b\xad(\xfb\x8dV[\xda\x0e\xb2\xeaO\x9a\x01\xa4\f$\xe3 \xb2\x1eL\xba鱼\x9a\x0103\xc2\x1a\x99\t\xaďVd\x160\xd8Y\xa1\xfdl@6;P\xcc\x01\xc49\x81z.\xc0\xcd\r\xb4\xe6\xc1\u0096\x9dw\xf4 \xd0Η\x82\xcc\xcfIX\x80\"\xf8\x05Sp\vq\x92/L\x96\x8bpP\xdaES\xa8\x16K\xc1,\x9e\x82,\x91B\\2\x85z\xa9\x14\xdc\xd2Yn\xc0Ai7L\xa1\xda(\x05\xb3q\n\xb2I\nq\xd3\x14\xea\xcdRp\x9b\xf30\xd5\x16\xa4ْ\x03#[Q\xc8\xd6\x14\"ۤ3\xb0\xdbr\xe2\xb7#\xb3\xed\xc9b\a2\xeeH\xd6;\x91ng\xb2\xb5\v\x0fв\xbb\x8e\x1e\xcc`wk'\xd9=\r\xc3\x1eI\x05\xbfg;\xb9\xbd\xd20\xdf;\x85r\x9f4+\xed\xbe\xedT\xed\xd7Nf\xffv\x92\x03\xda)\x1e\xd8N\xf5A\xed\xe4\x0eN\x87\xab\x0eI\xc1\x1c\x9afF\x0eKJ\x0eO\xaay\xe2\xda'dM\x1a\xfa,\x85,O\xa1h\xa5\x10\x8fH\xa1\xb6)8\xa7\xcfz\xab\xbbK\x1f\x8f\xe67+\xaf^\u007f\xd5*R\x90\x92\x93PQ\x04\x1fRpݜ\xe45Y\x1e\xc9Ai\x8fJ\xa1\x92\x14LLAzR\x88G\xa7P\xf7\xa6\xe0\x8e\xe1a\xaa>\xd2\x1cˁ\x91\xe3(\xe4x\n\x91\x13\xd2\x19\xd8\x139\xf1'\x91\xd9\xc9dq\n\x19O%\xeb\xd3Hw\xfa\xe8\xe6\x03\xaau\x06.ٞ\ti\xedY\xa4\x9c\r\x1d\xceA\x1b\xfc\xb9\xa4;\x0f:?\x1f(/\x80-\xed\x85du\x11i.&\xe5\x122^J֗\x91\xeer,\xaf\xae\x00̕\xb0F\xaeB+W\xa3\x15\xb9\x86[\xdak\xa1\xfdu@v=P\xdc\x00\xc4\x1b\x81\xfa&\xc0\u074c\x97q\xab\xfb\x16<\x9d\xfa\xd7\xc8\xf0r\xc0\x9f#k'\xb9=\r\xc3\x1dI\x05\u007fg;\xb9\xbb\xd20\xbf;\x85\xf2\x9e4+\xed\xbd\xedT\xdd\xd7N\xe6\xfev\x92\a\xda)>\xd8N\xf5C\xed\xe4\x1eN\x87\xab\x1eI\xc1<\x9afF\x1eKJ\x1eOJ\xe4\x89\xf6\t\xd9'\xd3\xd0?\x95B\xf6t\n\xc53)\xc4gS\xa8\x87\xa4\xe0\x86\xe2ë5\x8c\x8f\x87\x1dNo\xeds)\xc8\bN\xc2H\x8a\xe0G\xa5\xe0Fs\x92\x8f!˱\x1c\x94v\\\n\xd5\xf8\x14̄\x14db\nqR\n\xf5\xe4\x14\xdc\x14\x1e\xa6\x9aJ\x9ai\x1c\x18y\x9eB^\xa0\x10y1\x9d\x81\xfd\x1f'\xfe%2{\x99,^!\xe3\xabd\xfd\x1a\xe9^חq\xf7\x1bz\xc9\xdd\xf6M\x95\xfa\xd7\xcbHy\x1b:\xbc\x836\xf8wI\xf7\x1et\xfe>P~\x00[\xda\x0f\xc9\xea#\xd2|L\xca'd\xfc\x94\xac?#\xdd\xe7X^}\x01\x98/a\x8d|\x85V\xbeF+\xf2\r\xb7\xb4\xdfB\xfb\xef\x80\xec{\xa0\xf8\x01\x88?\x02\xf5O\x80\xfbY\xd1\xdb\xf9\x8b.\xec\xed\xb4\xbf\xea\x03\xa3\xf7\x81~KA~\xe7$\xfcA\x11\xfc\x9f)\xb8\xbf8\xc9\xff&\xcb\u007f8(\xed\xbf)T\xff\xa5`\xa6\x1b\xc3 \xfdR\x88\x1d)\xd4\xfdSp\x03\xc6\xe00\xd5@\xd2\f\xe2\xc0\xc8`\n\x99\x9eBd\x06\x86`g\xe4\xc4\xcfDf3\x93\xc5,d\x9c\x95\xacg#\xdd\xecdk\x0e\x1e\xa0e\xe7\x1c3\x98\xc1\xce\xd5N2w\x1a\x86y\x92\n~\xdevr\xf3\xa5a>\u007f\n\xe5\x02iV\xda\x05۩Z\xa8\x9d\xcc\xc2\xed$\x8b\xb4S\\\xb4\x9d\xea\xc5\xda\xc9-\x9e\x0eW-\x91\x82Y2͌,\x95\x94,\x9d\x94\xc82\xed\x13\xb2˦\xa1_.\x85l\xf9\x14\x8a\x15R\x88+\xa6P\xaf\x94\x82[y\x8c>\xf9\xadU\xf4\xf1\xe8n\xd9U\xd5\xeb\xfd\xa3\xd5R\x90\xd59\tkP\x04\xbff\nn-N\xf2\xb5\xc9r\x1d\x0eJ\xbbn\n\xd5z)\x98\xf5S\x90\rR\x88\x1b\xa6Po\x94\x82ۘ\x87\xa96!ͦ\x1c\x18ٌB6\xa7\x10\xd9\"\x9d\x81ݒ\x13\xbf\x15\x99mM\x16ېq[\xb2ގt\xdb\xeb\xc5\xf6u\xef\xa0m_\xb7\xddQ\x8f\xa3\xf7\x96vJAv\xe6$\xecB\x11\xfc\xae)\xb8\xdd8\xc9w'\xcb=8(\xed\x9e)T{\xa5`\xf6NA\xf6I!\xee\x9bB\xbd_\nn\u007f\x1e\xa6:\x804\ar`\xe4 \n9\x98B\xe4\x90t\x06\xf6PN\xfcadv8Yt\x92ѐuF\xba|L\xf3\x01\xd5\xd3\xd2K\xee\xb1G\xa8l\xbe_ZR\x1ct\xe8B\x1b\xbc']\x01\x9d\x97@Y\xc1\x966\x90U7ijR\x8e$\xe3Qd-\xa4\x8bX^\xf5\x00\xe6hX#\xbdh\xe5\x18\xb4\xcd\x19rK{,\xb4?\x0eȎ\a\x8a\x13\x80x\"P\x9f\x04\xb8\x93\xf1\x9emu\x9f\x82\xd7n\xf3C\xf5T\xbc\xf6\xf5\xe7\xeci\xed$\xa7\xa7a8#\xa9\xe0\xcfl'wV\x1a\xe6g\xa7P\x9e\x93f\xa5=\xb7\x9d\xaa\xf3\xdaɜ\xdfNrA;\xc5\v۩\xbe\xa8\x9d\xdc\xc5\xe9p\xd5%)\x98K\xd3\xcc\xc8eI\xc9\xe5I\x89\\\xd1>!{e\x1a\xfa\xabRȮN\xa1\xb8&\x85xm\n\xf5u)\xb8\xeb\xf5\xf1\xe8m\xdd\xc0\xc7\xc3\xdeHo\xedM)\xc8͜\x84[(\x82\xbf5\x05w\x1b'\xf9\xeddy\a\a\xa5\xbd3\x85\xea\xae\x14\xcc\xdd)\xc8=)\xc4{S\xa8\xefK\xc1\xdd\xcf\xc3T\x0f\x90\xe6A\x0e\x8cʉ\u007f\x8c\xcc\x1e'\x8b'\xc8\xf8$Y?E\xba\xa7q\xb1]Ϡ\xed\xb2\xcf\xe28\xcdW\xf1!)\xc8PN\xc20\x8a\xe0\x87\xa7\xe0\x9e\xe3$\x1fA\x96#9(\xed\xa8\x14\xaa\xd1)\x981)\xc8\xd8\x14\xe2\xb8\x14\xea\xf1)\xb8\t\xe6a\xaaOH\xf3)\aF>\xa3\x90\xcf)D\xbeHg`\xbf\xe4\xc4\u007fEf_\x93\xc57d\xfc\x96\xac\xbf#\xdd\xf7z\xb1\xad\x9e\x1f\xb4m\xf5\xd8\x1f\xf58z\x0f\xec\xa7\x14\xe4gN\xc2/\x14\xc1\xff\x9a\x82\xfb\x8d\x93\xfcw\xb2\xfc\x83\x83\xd2\xfe\x99B\xf5W\n\xe6\xef\x14\xe4\x9f\x14\xe2\xbf)\xd4\xff\xa5\xe0\xa6\x1b\x8b\xc3T\xfdH\xd31\x16\x03#\xfd)d\x00\x85\xc8@\x86`\aq\xe2\a\x93\xd9\xf4d1\x03\x19g$\xeb\x99H7\xf3X}f\xbbfѶ\xaf\xcbΪ\xc7\xd1\xfbc\xb3\xa5 \xb3s\x12\xe6\xa0\b~\xce\x14\xdc\\\x9c\xe4s\x93\xe5<\x1c\x94v\xde\x14\xaa\xf9R0\xf3\xa7 \v\xa4\x10\x17L\xa1^(\x05\xb70\x0fS-B\x9aE90\xb2\x18\x85,N!\xb2D:\x03\xbb$'~)2[\x9a,\x96!\xe3\xb2d\xbd\x1c\xe9\x96\x1f\xdb\xfc\xe8\xe9ZA/\xb9ˮ\xa8R\xff\x9f!)+C\x87U\xd0\x06\xbf*\xe9V\x83\xceW\a\xca5`K\xbb&Y\xadE\x9a\xb5IY\x87\x8c\xeb\x92\xf5z\xa4[\x1f˫\r\x00\xb3!\xac\x91\x8d\xd0\xca\xc6hE6\xe1\x96vSh\xbf\x19\x90m\x0e\x14[\x00qK\xa0\xde\np[\x8f\xed\u05f9\xcd؎N\xbb\xed\xd8\xfeͯ\xc4\xdbi\x95\xed\x1b\x11vhb\xf0;ju;5\"߹)\xe5.M_\xda]\xb5V\xbbi5\xbbk\x95=\xb4\xc6=\xb5\xd6{iu{7\xff\xbcڧ)fߦ7\xb2_\x13e\xff&\x8a\x1c\xa0\a\xb5\a6\xc2\x1fԔ\xec\xe0\xa6\x14\x874%\x1eڔ\xfa\xb0\xa6\xb8Û\xd2\xeal\xfea˚\xb1\x03\xf4\xd7\xd7\f\x90\\]hi\x13\xfc\x11\x80\xb3\xear\xa7\xb5\xecRUZ\x0fT\x05`J@* \x06\xa0\xee\x06\\\xad˪#\xb5\x9a\xa3T\x19\x11m$j\xd3\xfc|\xc6\x1e\xf6hu\xbeWkv\x8c֢Ok\xad\xd9\xfdZ\x8b\a\xb4\xc6\a\xb5\xd6\x0fiu\x0f\x8f\xeb\xd7\xf3ȸ\x8e\x1e\xfb\xe8\xb8\xfeͯ\xf4\x8fi\x95\xc7\x1b\x11\x9ehb\xf0OjuO5\"\u007f\xba)\xe53M_\xdag\xb5VC\xb4\x9a\xa1Ze\x98\xd68\\k\xfd\x9cV7\xa2\xf9\xe7\xd5Ȧ\x98QModt\x13eL\x13\x9b_G\xf4\xa0v\\#\xfc\xf8\xa6d\x13\x9aRLlJ\x9cԔzrSܔ\xe65\xdd\xea\x9eڼ\x1e\x9a\x0f\xe6i\xcd+H?\xa7\x9f'\xe5\x05\xe8\xf0\"\xda\xe0\xffG\xba\x97\xa0\xf3\x97\x81\xf2\x15\xd8ҾJV\xaf\x91\xe6uR\xde \xe3\x9bd\xfd\x16\xe9\xde\xc6\xf2\xea\x1d\xc0\xbc\vk\xe4=\xb4\xf2>Z\x91\x0f\xb8\xa5\xfd\x10\xda\u007f\x04d\x1f\x03\xc5'@\xfc\x14\xa8?\x03\xdc\xe7\xcd'K\xeb\v\xbd*\xfb\xa5\x1ak\xbf\x02\xe4ku\xe1\x1bm\x82\xff\x16pߩ˿\xd7Z\xfe\xa0\xaa\xb4?\x02\xd5O\x80\xf9\x19\x90_\x80\xf8+P\xff\x06\xb8\xdfuY\xf5\x87V\xf3\xa7*#\u007fi#\u007fk#\xf2\x0f\xf6\xb0\xff\xaa\xf3\xffiͦ\x1b\xdfԢ\x9f\xd6ء\xb5\xee\xaf\xd5\r\x18\xdf\xd1\xdb5\xb0\x89]v\xd0\xf8\x01\xfau~0 ӫ\v3h\x13\xfc\x8c\x80\x9bI]>\xb3\xd6r\x16U\xa5\x9d\x15\xa8f\x03\xcc\xec\x80\xcc\x01\xc49\x81z.\xc0ͭ˪y\xb4\x9ayU\x19\x99O\x1b\x99_\x1b\x91\x05\xb0\x87]P\x9d_Hk\xb6\xb0\xd6b\x11\xadqQ\xad\xf5bZ\xdd\xe2\xe3;\xfaz\x96\x18\u07ff\xf9\x1e\xbe\xe4\xf8\x01\xfa\xb5|)@\x96V\x17\x96\xd1&\xf8e\x01\xb7\x9c\xba|y\xad\xe5\n\xaaJ\xbb\"P\xad\x04\x98\x95\x01Y\x05\x88\xab\x02\xf5j\x80[]\x97Ukh5k\xaa2\xb2\x966\xb2\xb66\"\xeb`\x0f\xbb\xae:\xbf\x9e\xd6l}\xad\xc5\x06Z\xe3\x86Z덴\xba\x8d\xc7w\xb4z6\x19\u07ff\xf9>\xbd\xe9\xf8\x01\xfa\xf5z3@6W\x17\xb6\xd0&\xf8-\x01\xb7\x95\xba|k\xad\xe56\xaaJ\xbb-Pm\a\x98\xed\x01\xd9\x01\x88;\x02\xf5N\x80\xdbY\x97U\xbbh5\xbb\xaa2\xb2\x9b6\xb2\xbb6\"{`\x0f\xbb\xa7:\xbf\x97\xd6lo\xad\xc5>Z\xe3\xbeZ\xeb\xfd\xb4\xba\xfd\x9bG\xba\xeb\x80\xe6\xfc\xbb\xec\x81ͥ6/\x8f\x83\x009X]8D\x9b\xe0\x0f\x05\xdca\xea\xf2õ\x96\x9d\xaaJk\x80*\x03L\x0eH\v\x88G\x00\xb5\x05\x9c\xd3eU\x97V\xe3U\x19)\xb4\x91R\x1b\x91\n{ؠ\xcewk\xcdj\xadőZ\xe3QZk\xd1\xea\xe2\xf8~]=\xe3;\xba\xec\xd1\xe3\xfb\xeb\u007f\x1b\xd2*\xc74\"\xf451\xf8c\xb5\xba\xe3\x1a\x91\x1fߔ\xf2\x84\xa6/\xed\x89Z\xab\x93\xb4\x9a\x93\xb5\xca)Z\xe3\xa9Z\xebӴ\xbaӛ\u007f^\x9d\xd1\x14sf\xd3\x1b9\xab\x89rv\x13E\xceу\xdas\x1b\xe1\xcfkJv~S\x8a\v\x9a\x12/lJ}QS\xdc\xc5\xe3;|\xe7%\xe3\xfb\xfbN{\xe9\xf8\x01M\xb5\x97\x01r\xb9\xbap\x856\xc1_\t\xb8\xab\xd4\xe5Wk-\xafQU\xdak\x81\xea:\xc0\\\x0f\xc8\r@\xbc\x11\xa8o\x02\xdcͺ\xac\xbaE\xab\xb9U\x95\x91۴\x91۵\x11\xb9\x03{\xd8;\xd5\xf9\xbb\xb4fwk-\xee\xd1\x1a\xef\xd5Zߧ\xd5ݯ\xb5\xf5\x80.h\xd9\a\xc7\x0fT؇Hy\x18:<\x826\xf8GI\xf7\x18t\xfe8P>\x01[\xda'\xc9\xea)\xd2\"\xe5c\xe8\xf0\t\xda\xe0?%\xddg\xd0\xf9\xe7@\xf9\x05li\xbf$\xab\xafH\xf35)ߐ\xf1[\xb2\xfe\x8et\xdfcy\xf5\x03`~\x845\xf2\x13Z\xf9\x19\xad\xc8/\xdc\xd2\xfe\n\xed\u007f\x03\xb2߁\xe2\x0f \xfe\t\xd4\u007f\x01\xee\xef\xe6C\xa1\xe7\x9f\xe6\xaaz쿍i\xbe\x9f\xfc\a\xc8t\x13\x1b\x17\xfaMl\x9a\xe0;\x00\xd7_]>@k9PUi\a\x01\xd5`\xc0L\x0f\xc8\f@\x9c\x11\xa8g\x02\xdc̺\xac\x9aE\xab\x99U\x95\x91ٴ\x91ٵ\x11\x99\x03{\xd89\xd5\xf9\xb9\xb4fsk-\xe6\xd1\x1a\xe7\xd5Zϧ\xd5\xcd\xdf\xfc\xd3\xe6\xbb\xf7\x02\x13\a*\xec\x82\x13\a\x81v\xa1\x14daN\xc2\"\x14\xc1/\x9a\x82[\x8c\x93|q\xb2\\\x82\x83\xd2.\x99B\xb5T\nf\xe9\x14d\x99\x14\xe2\xb2)\xd4˥\xe0\x96\xe7a\xaa\x15H\xb3\"\aFV\xa2\x90\x95)DVIg`W\xe5įFf\xab\x93\xc5\x1ad\\\x93\xac\xd7\"\xdd\xda\xcd%\xf7\xb6\xd6\xc1%\xdbu!\xad]\x8f\x94\xf5\xa1\xc3\x06h\x83ߐt\x1bA\xe7\x1b\x03\xe5&\xb0\xa5ݔ\xac6#\xcd\xe6\xa4lA\xc6-\xc9z+\xd2m\x8d\xe5\xd56\x80\xd9\x16\xd6\xc8vhe{\xb4\";pK\xbb#\xb4\xdf\t\xc8v\x06\x8a]\x80\xb8+P\xef\x06\xb8\xdd\xf5\x82\xba\xf6Ц\xcb\xee\xa9˛\xafk{\x91\xb27t\xd8\am\xf0\xfb\x92n?\xe8|\u007f\xa0<\x00\xb6\xb4\a\x92\xd5A\xa49\x98\x94C\xc8x(Y\x1fF\xbañ\xbc\xea\x04\x8c\x815\x92\xa1\x95\x1c\xadH\x8b[\xda#\xa0\xbd\x052\a\x14]@\xf4@]\x00\xael.\xa8\xaf\xa7j\x9a\xbe\x1e\x1b\x9a\xe5zo\xa2\x9b\x94\x1a:\x1c\x896\xf8\xa3H'\xd0y\x04\xca\x1e\xd8\xd2\x1eMV\xbd\xa49\x86\x94>2\x1eK\xd6Ǒ\xeex,\xafN\x00̉\xb0FNB+'\xa3\x159\x85[\xdaS\xa1\xfdi@v:P\x9c\x01\xc43\x81\xfa,\xc0\x9d\xdd\\P\xab眦i\xf5\xd8s\x9b\xe5z\xdf\xe2\x1e\xcdK\xee.R\xee\x86\x0e\xf7\xa0\r\xfe^\xd2\xdd\a\x9d\xdf\x0f\x94\x0f\xc0\x96\xf6A\xb2z\x884\x0f\x93\xf2\b\x19\x1f%\xeb\xc7H\xf78\x96WO\x00\xe6IX#O\xa1\x95\xa7ъ<\xc3-\xed\xb3\xd0~\b\x90\r\x05\x8aa@\x1c\x0e\xd4\xcf\x01n\xc4\xc4\x0e\xdf5\xb2\xb9\xaa.;\xaa1\xfa\x1f}\x00\x19\xa3.\x8c\xd5&\xf8q\x80\x1b\xaf.\x9f\xa0\xb5\x9c\xa8\xaa\xb4\x93\x80j2`\xa6\x002\x15\x88Ӏ\xfay\xc0\xbd\xa0˪\x17\xb5\x9a\xff\xa92\xf2\x926\xf2\xb26\"\xaf`\x0f\xfb\xaa:\xff\x9a\xd6\xecu\xad\xc5\x1bZ\xe3\x9bZ뷴\xba\xb7\x9b\xea;\xdfi\x16\xf8N\xfbns\xcdz/\xe4=Rއ\x0e\x1f\xa0\r\xfeC\xd2}\x04\x9d\u007f\f\x94\x9f\xc0\x96\xf6S\xb2\xfa\x8c4\x9f\x93\xf2\x05\x19\xbf$\xeb\xafH\xf75\x96W\xdf\x00\xe6[X#ߡ\x95\xefъ\xfc\xc0-\xed\x8f\xd0\xfe' \xfb\x19(~\x01\xe2\xaf@\xfd\x1b\xe0~\aZ\u007f`a\xcb\xfe\xd9|\xc4\xe3>\xc9_)\xc8ߜ\x84\u007f(\x82\xff7\x05\xf7\x1f'\xf9t\x93\xc0\xb2\xdf$\fJۑB\xd5?\x053 \x05\x19\x98B\x1c\x94B=8\x057=\x0fS\xcd@\x9a\x19902\x13\x85\xccL!2\vC\xb0\xb3r\xe2g#\xb3\xd9\xc9b\x0e2\xceI\xd6s\x91n\xeeI\xcd\xc5\xf6uΣm_\xa7\x9dW\x8f\xa3\xf7P\xe6KA\xe6\xe7$,@\x11\xfc\x82)\xb8\x858\xc9\x17&\xcbE8(\xed\xa2)T\x8b\xa5`\x16OA\x96H!.\x99B\xbdT\nni\x1e\xa6Z\x864\xcbr`d9\nY\x9eBd\x85t\x06vEN\xfcJd\xb62Y\xacB\xc6U\xc9z5ҭN\xb6\xd6\xe0\x01Zv\xcdI\x83\x19\xecZ\xed$k\xa7aX'\xa9\xe0\xd7m'\xb7^\x1a\xe6\xeb\xa7Pn\x90f\xa5ݰ\x9d\xaa\x8d\xda\xc9l\xdcN\xb2I;\xc5M۩ެ\x9d\xdc\xe6\xe9p\xd5\x16)\x98-\xd3\xcc\xc8VI\xc9\xd6I\x89l\xd3>!\xbbm\x1a\xfa\xedRȶO\xa1\xd8!\x85\xb8c\n\xf5N)\xb8\x9d\xf5You\uf88fG\xf3\x9bծ\xea\xf5W\xad\xddR\x90\xdd9\t{P\x04\xbfg\nn/N\xf2\xbd\xc9r\x1f\x0eJ\xbbo\n\xd5~)\x98\xfdS\x90\x03R\x88\a\xa6P\x1f\x94\x82;\x98\x87\xa9\x0e!͡\x1c\x189\x8cB\x0e\xa7\x10\xe9Lg`\r'>#\xb3\x9c,Zd<\x82\xac-\xe9ܤ\xe6\x03\xaaՅK\xb6\x1e\xd2ڂ\x94\x12:Th\x83\x0f\xa4\xeb\x86\xcek\xa0<\x12\xb6\xb4G\x91\x95\x90&\x92\xd2Cƣɺ\x97t\xc7`y\xd5\a\x98ca\x8d\x1c\x87V\x8eG+r\x02\xb7\xb4'B\xfb\x93\x80\xecd\xa08\x05\x88\xa7\x02\xf5i\x80;\x1d/\xe3V\xf7\x19x:\xf5oV\xe1倿Z\xd5Nrv\x1a\x86s\x92\n\xfe\xdcvr\xe7\xa5a~~\n\xe5\x05iV\xda\v۩\xba\xa8\x9d\xcc\xc5\xed$\x97\xb4S\xbc\xb4\x9d\xea\xcb\xda\xc9]\x9e\x0eW]\x91\x82\xb92͌\\\x95\x94\\\x9d\x94\xc85\xed\x13\xb2צ\xa1\xbf.\x85\xec\xfa\x14\x8a\x1bR\x887\xa6Pߔ\x82\xbb\x19\x1f^\xad[\xf8x\xd8[魽-\x05\xb9\x9d\x93p\aE\xf0w\xa6\xe0\xee\xe2$\xbf\x9b,\xefᠴ\xf7\xa6Pݗ\x82\xb9?\x05y \x85\xf8`\n\xf5C)\xb8\x87y\x98\xea\x11\xd2<ʁ\x91\xc7(\xe4q\n\x91'\xd2\x19\xd8'9\xf1O\x91\xd9\xd3d\xf1\f\x19\x9f%\xeb!\xa4\x1b\xaa/\xe3\xeeaz\xc9\xddv\xb8J\xfd\xe3X\xa4\x8c\x80\x0e#\xd1\x06?\x8at\xa3\xa1\xf31@9\x16\xb6\xb4\xe3\xc8jJ\xc1|\x9c\x82|\x92B\xfc4\x85\xfa\xb3\x14\xdc\xe7Y\x9f\xfc\xd6\x1c\x93\xf5\xe5Ҳs\xaa\xd7\xfbGs\xa5 ss\x12\xe6\xa1\b~\xde\x14\xdc|\x9c\xe4\xf3\x93\xe5\x02\x1c\x94v\xc1\x14\xaa\x85R0\v\xa7 \x8b\xa4\x10\x17M\xa1^,\x05\xb78\x0fS-A\x9a%90\xb2\x14\x85,M!\xb2L:\x03\xbb,'~92[\x9e,V \xe3\x8ad\xbd\x12\xe9V\u058b\xed\xeb^E۾n\xbb\xaa\x1eG\xef-\xad\x96\x82\xac\xceIX\x83\"\xf85Spkq\x92\xafM\x96\xebpP\xdauS\xa8\xd6K\xc1\xac\x9f\x82l\x90B\xdc0\x85z\xa3\x14\xdc\xc6\x1e\xf6TzkOKAN\xe7$\x9cA\x11\xfc\x99)\xb8\xb38\xc9\xcf&\xcbs8(\xed\xb9)T\xe7\xa5`\xceOA.H!^\x98B}Q\n\xeeb\x1e\xa6\xba\x844\x97r`\xe42\n\xb9\x9cB\xe4\x8at\x06\xf6JN\xfcUdv5Y\\C\xc6k\xc9\xfa:\xd2]\x8f\x8b\xed\xba\x01m\x97\xbd\x11\xc7i\xbe\x8aߔ\x82\xdc\xccI\xb8\x85\"\xf8[Sp\xb7q\x92\xdfN\x96wpP\xda;S\xa8\xeeJ\xc1ܝ\x82ܓB\xbc7\x85\xfa\xbe\x14\xdc\xfd7P\xce\x03[\xday\xc9j>\xd2\xccO\xca\x02d\\\x90\xac\x17\"\xdd\xc2X^-\x02\x98Ea\x8d,\x86V\x16G+\xb2\x04\xb7\xb4KB\xfb\xa5\x80li\xa0X\x06\x88\xcb\x02\xf5r\x80[~JGֹ\u0094\xfeY\xa7]qʀ\xa6ڕ\x00YY]XE\x9b\xe0W\x05\xdcj\xea\xf2յ\x96k\xa8*\xed\x9a@\xb5\x16`\xd6\x06d\x1d \xae\v\xd4\xeb\x01n}]Vm\xa0\xd5l\xa8\xca\xc8F\xda\xc8\xc6ڈl\x82=\xec\xa6\xea\xfcfZ\xb3͵\x16[h\x8d[j\xad\xb7\xd2\xea\xb6\xd6\xda\xdaF\x17\xb4\xec\xb6S\x06*\xecv\xa4l\x0f\x1dv@\x1b\xfc\x8e\xa4\xdb\t:\xdf\x19(w\x81-\xed\xaed\xb5\x1biv'e\x0f2\xeeI\xd6{\x91no,\xaf\xf6\x01̾\xb0F\xf6C+\xfb\xa3\x159\x80[\xda\x03\xa1\xfdA@v0P\x1c\x02\xc4C\x81\xfa0\xc0\x1d\xde\\P_gg\xd3\xf4uZ\xd3,\xd7;c\x19)9th\xa1\r\xfe\b\xd2Y\xe8\xdc\x01e\x17li=Y\x15\xa4)I\xa9\xc8\x18Ⱥ\x9bt5\x96WG\x02\xe6(X#\x82V\"Z\x91\x1eni\x8f\x86\xf6\xbd@v\fP\xf4\x01\xf1X\xa0>\x0ep\xc7\x03\xad\x13\xb0\xb0eO\x9c2\b\xb4'\xa5 's\x12N\xa1\b\xfe\xd4\x14\xdci\x9c䧓\xe5\x19\x1c\x94\xf6\xcc\x14\xaa\xb3R0g\xa7 \xe7\xa4\x10\xcfM\xa1>/\x05w>\x0fS]@\x9a\v90r\x11\x85\\L!rI:\x03{)'\xfe22\xbb\x9c,\xae \xe3\x95d}\x15\xe9\xaen\x9e\xbdV\xf75\xcd%7\xbf\xa7]\xdbH\xfd\xc5\xed:R\xae\x87\x0e7\xa0\r\xfeF\xd2\xdd\x04\x9d\xdf\f\x94\xb7\xc0\x96\xf6V\xb2\xba\x8d4\xb7\x93r\a\x19\xef$\xeb\xbbHw7\x96W\xf7\x00\xe6^X#\xf7\xa1\x95\xfbъ<\xc0-\xed\x83\xd0\xfe! {\x18(\x1e\x01\xe2\xa3@\xfd\x18\xe0\x1eo>\x14ZO\xe8U\xd9'\xd5X\xfb\x14 O\xab\v\xcfh\x13\xfc\xb3\x80\x1b\xa2.\x1f\xaa\xb5\x1c\xa6\xaa\xb4Á\xea9\xc0\x8c\x00d$\x10G\x01\xf5h\xc0\x8d\xd1e\xd5X\xadf\x9c*#㵑\tڈL\xc4\x1ev\x92:?Yk6Ek1Uk\x9c\xa6\xb5~^\xab{A_r\xad\xee\x17\xf5Iѿ\xf1\xa5\xcf&\xfe\xc6W\n\xf22'\xe1\x15\x8a\xe0_M\xc1\xbd\xc6I\xfe:Y\xbe\xc1Ai\xdfL\xa1z+\x05\xf3v\n\xf2N\n\xf1\xdd\x14\xea\xf7Rp\xef\xf30\xd5\a\xa4\xf9\x90\x03#\x1fQ\xc8\xc7\x14\"\x9f\xa43\xb0\x9fr\xe2?#\xb3\xcf\xc9\xe2\v2~I\xd6_\x91\xeek\xfd\xc0h}\x83K\xb6\xdfBZ\xfb\x1d)\xdfC\x87\x1f\xd0\x06\xff#\xe9~\x82\xce\u007f\x06\xca_`K\xfb+Y\xfdF\x9a\xdfI\xf9\x83\x8c\u007f\x92\xf5_\xa4\xfb\x1b˫\u007f\x00\xf3/\xac\x91\xff\xd0\xcatS\xb5\x15\xe9\a\x06\xdb1U\xb5\xef\x0fd\x03\x80b \x10\a\x01\xf5`\xc0M?\xb5#\xeb\x9eaj\xff\xac\xdb\xce\xd8\x18\xfd\xd3^\x80̬.̢M\xf0\xb3\x02n6u\xf9\xecZ\xcb9T\x95vN\xa0\x9a\v0s\x032\x0f\x10\xe7\x05\xea\xf9\x007\xbf.\xab\x16\xd0j\x16Ted!mdamD\x16\xc1\x1evQu~1\xad\xd9\xe2Z\x8b%\xb4\xc6%\xb5\xd6KiuK7\xb5\xb7s\x99fAo\xa7]\xb6\xb9f\xbd\xab\xb4\x1c)\xcbC\x87\x15\xd0\x06\xbf\"\xe9V\x82\xceW\x06\xcaU`K\xbb*Y\xadF\x9a\xd5IY\x83\x8ck\x92\xf5Z\xa4[\x1b˫u\x00\xb3.\xac\x91\xf5\xd0\xca\xfahE6\xe0\x96vCh\xbf\x11\x90m\f\x14\x9b\x00qS\xa0\xde\fp\x9b\x03\xad-\xb0\xb0e\xb7\x9c:\b\xb4[\xa5 [s\x12\xb6\xa1\b~\xdb\x14\xdcv\x9c\xe4ۓ\xe5\x0e\x1c\x94v\xc7\x14\xaa\x9dR0;\xa7 \xbb\xa4\x10wM\xa1\xde-\x05\xb7;\x0fS\xedA\x9a=90\xb2\x17\x85\xecM!\xb2O:\x03\xbb/'~?2۟,\x0e \xe3\x81d}\x10\xe9\x0e\xd6\xe7\xb0u\x88>\xdd-{h#\xf5\xee\xd0a\xa4\x1c\x0e\x1d:\xd1\x06oH\x97A\xe79P\xb6`K{\x04YY\xd28R\xba\xc8\xe8ɺ ]\x89\xe5U\x05\x98\x00k\xa4\x1b\xad\xd4hE\x8e\xe4\x96\xf6(h/@\x16\x81\xa2\a\x88G\x03u/\xe0\x8ei.\xa8\xaf\xbbO\xdfP\xdd\xf6X}\v6o\xa3\xe3H9\x1e:\x9c\x806\xf8\x13Iw\x12t~2P\x9e\x02[\xdaS\xc9\xea4ҜN\xca\x19d<\x93\xac\xcf\"\xdd\xd9X^\x9d\x03\x98sa\x8d\x9c\x87V\xceG+r\x01\xb7\xb4\x17B\xfb\x8b\x80\xecb\xa0\xb8\x04\x88\x97\x02\xf5e\x80\xbb\xbc\xf9P蹢\xb9\xaa\x1e{ec\x9aoPW\x01r\xb5\xbap\x8d6\xc1_\v\xb8\xeb\xd4\xe5\xd7k-oPU\xda\x1b\x81\xea&\xc0\xdc\f\xc8-@\xbc\x15\xa8o\x03\xdc\xed\xba\xac\xbaC\xab\xb9S\x95\x91\xbb\xb4\x91\xbb\xb5\x11\xb9\a{\xd8{\xd5\xf9\xfb\xb4f\xf7k-\x1e\xd0\x1a\x1f\xd4Z?\xa4\xd5=\xac\xef\xa1V\xf7#\xfa*k~\xec<\xaa/O\xfd9\xf4X\n\xf28'\xe1\t\x8a\xe0\x9fL\xc1=\xc5I\xfe4Y>\xc3Ai\x9fM\xa1\x1a\x92\x82\x19\x9a\x82\fK!\x0eO\xa1~.\x057\x82\x87\xa9F\x92f\x14\aFFS\xc8\x18\n\x91\xb1\xe9\f\xec8N\xfcx2\x9b@\x16\x13\xc98\x89\xac'\x93n\x8a~\x02\xb6\xa6\xe2\x92\xed4Hk\x9f'\xe5\x05\xe8\xf0\"\xda\xe0\xffG\xba\x97\xa0\xf3\x97\x81\xf2\x15\xd8ҾJV\xaf\x91\xe6uR\xde \xe3\x9bd\xfd\x16\xe9\xde\xc6\xf2\xea\x1d\xc0\xbc\vk\xe4=\xb4\xf2>Z\x91\x0f\xb8\xa5\xfd\x10\xda\u007f\x04d\x1f\x03\xc5'@\xfc\x14\xa8?\x03\xdc\xe7zA]_h\xd3e\xbf\xd4\xe5\xcd\x17\xbc\xafH\xf9\x1a:|\x836\xf8oI\xf7\x1dt\xfe=P\xfe\x00[\xda\x1f\xc9\xea'\xd2\xfcL\xca/d\xfc\x95\xac\u007f#\xdd\xefX^\xfd\x01\x98?a\x8d\xfc\x85V\xfeF+\xf2\x0f\xb7\xb4\xffB\xfb\xff\x80l\xbai\x8a\xa2\x1f\x10;\x80\xba?\xe0\x06Lk>\x14z\x066M_\x8f\x1d4m\xa0\xc2\x0e&ez\xe80\x03\xda\xe0g$\xddL\xd0\xf9\xcc@9\vlig%\xab\xd9H3;)s\x90qN\xb2\x9e\x8btscy5\x0f`\xe6\x8552\x1fZ\x99\x1f\xad\xc8\x02\xdc\xd2.\b\xed\x17\x02\xb2\x85\x81b\x11 .\nԋ\x01n\xf1\xe6\x82Z=K4M\xab\xc7.\xd9,\xd7;+K\x91\xb24tX\x06m\xf0˒n9\xe8|y\xa0\\\x01\xb6\xb4+\x92\xd5J\xa4Y\x99\x94Uȸ*Y\xafF\xbaձ\xbcZ\x030k\xc2\x1aY\v\xad\xac\x8dVd\x1dniׅ\xf6\xeb\x01\xd9\xfa@\xb1\x01\x107\x04\xea\x8d\x00\xb7\xb1>C]\x9b\xe8\xd5u\xd9M\xf5\xf1h^r\x9b\x91\xb29t\xd8\x02m\xf0[\x92n+\xe8|k\xa0\xdc\x06\xb6\xb4ے\xd5v\xa4ٞ\x94\x1dȸ#Y\xefD\xba\x9d\xb1\xbc\xda\x050\xbb\xc2\x1a\xd9\r\xad\xec\x8eVd\x0fni\xf7\x84\xf6{\x01\xd9\xde@\xb1\x0f\x10\xf7\x05\xea\xfd\x00\xb7\xff\xb4\x8e\xac\xeb\x80檺쁍\xd1\xff\xff\x05\xc8\xc1\xea\xc2!\xda\x04\u007f(\xe0\x0eS\x97\x1f\xae\xb5\xecTUZ\x03T\x19`r@Z@<\x02\xa8-\xe0\x9c.\xab\xba\xb4\x1a\xaf\xcaH\xa1\x8d\x94ڈT\xd8\xc3\x06u\xbe[kVk-\x8e\xd4\x1a\x8f\xd2Z\x8bV\x17\xa7u\x14\x9d=\xd3\xfa\x17\x9d\xf6\xe8i\x03\x9aj{\x019F]\xe8\xd3&\xf8c\x01w\x9c\xba\xfcx\xad\xe5\t\xaaJ{\"P\x9d\x04\x98\x93\x019\x05\x88\xa7\x02\xf5i\x80;]\x97Ugh5g\xaa2r\x966r\xb66\"\xe7`\x0f{\xae:\u007f\x9e\xd6\xec|\xad\xc5\x05Z\xe3\x85Z담\xba\x8b\xb5\xb6.\xd1\x05-{鴁\n{\x19)\x97C\x87+\xd0\x06\u007f%鮂ί\x06\xcak`K{-Y]G\x9a\xebI\xb9\x81\x8c7\x92\xf5M\xa4\xbb\x19˫[\x00s+\xac\x91\xdb\xd0\xca\xedhE\xee\xe0\x96\xf6Nh\u007f\x17\x90\xdd\r\x14\xf7\x00\xf1^\xa0\xbe\x0fp\xf77\x17\xd4\xd7\xf9@\xd3\xf4u\xda\a\x9b\xe5z#\xe4!R\x1e\x86\x0e\x8f\xa0\r\xfeQ\xd2=\x06\x9d?\x0e\x94O\xc0\x96\xf6I\xb2z\x8a4O\x93\xf2\f\x19\x9f%\xeb!\xa4\x1b\x8a\xe5\xd50\xc0\f\x875\xf2\x1cZ\x19\x81Vd$\xb7\xb4\xa3\xa0\xfdh \x1b\x03\x14c\x818\x0e\xa8\xc7\x03n\x02К\x88\x85-;i\xda \xd0NNA\xa6p\x12\xa6R\x04\xff\u007f\x96\xce:\xcem\xa3\tÕ\x0f\xca\xcc\xec6\xcc\r33'M\xdaP\xd3\xe4\xd6vv-K\xb2\x9a\x91s\xbd\xb6)\xf3Wfffff\fC\x1bfN\xcapw\xcd\xe9\xdbyG\u007f\xe4yf\xde\xf5j\xb5\x96\xec\xf3\xb9\xbf\xfen^R\x98\xf92\x92] \xf6\x16ʀ\xa7\x17%\x85\xbf8)Ԓ\xa4\xa0\xa5I\x11-K\x8apyR\x98\x9f\xe40\xfe\xcfb\xb5B\x06\x14\xad\x94\x80VI@\xb4:9\x03\xbdFFܵ\xe2\xcc:qa\xbd8\xda \x0e7\x8a\xcd&{\xf5r\xc5\xcdv\xcb\xf6s\xce\x16\x1b\xf2瞭bچ8؎6pw\x88\xcdN\xc4\xd9]\x90\xb7\x1b\xa9\xa7\xf7\x88\xfd_\xc4\xeaW1\xfd&\x8e~\x17\x87\u007f\x88͟\x98\xee\xff\x05\xa9\xbf\x91*\xfa\a-\xfd\x8b\x96\xa8V\x96\xd4u\x88\xddz(\xf3\x1fT\xd8\vE\rP\x18Cf\x9f\xf9\xa9BΙow\xa5S\xf3m\xa2u\x19D\xe5\x9c\x05\x15\xdc\x04n%d\xf6\xe5,\xbb\x1f\xd3۟#O\x1f\x00\xf9\aB\xea \x88\x0e\x86\xa2C\xa0\xf0P\xc8\x1c\xc6\xd3\xfcÙ\xea\b\x8e\x14\x1d\xc9\r\x1d\xc5\r\xd1\xd1XC\x1fÙ{,3s\x1c\xb3p<3:\x81\x19\x9e\xc84'ه\xd6\xe4\x8a'ϯ`\xe9S\xe6W\xc2\xfaԤ\xa0\xb4\x8c\x04\xa7I\x10\xb8\xa7'\x85i$#\xd9\xc6b\xaf\x89\fx\xbaiR\xf8͒B5O\nj\x91\x14Qˤ\b[%\x85i-\x87\xf1ۈU[\x19P\xd4N\x02:C\x02\xa2\xf6\xc9\x19\xe8\x0e2\xe2v\x14g:\x89\v\x9d\xc5Q\x17q\xd8Ul\xba\xd9-\xd7\xe4\xbac˺\aB\xad{\x8a\xa9\x17\xe2\xa07\xda\xc0\xed#6}\x11g\xfbA^\u007f\xa4\x9e\x1e \xf6\a\x8a\xd5 1\r\x16GC\xc4\xe1P\xb1\x19\x86\xe9\xfepH\x8d@\xaah$Z\x1a\x85\x96h\xb4,\xa9\xc7 v\xc7B\x99qPa<\x14\x9d\t\x85\x13 3\xd1\xderų쮊\xfal\x9b\xf0_\x8e\x82h2g\xc1\x14n\x02w*d\xa6q\x96=\x87\xe9M\xe7\xc8\xd3\xe7B\xfe\fḦ́\xa8\n\x8a\x14\x14f \x93\xe5i~\x8e\xa9fq\xa4HsC\x86\x1b\xa2<\xd6\xd0.gn\x81\x99\xf1\x98\x05\x9f\x19\x05̰\xc84\xa1eu\xd5yvBu\x95\x9em\xf7\xcc_\x84\x90\x98\"\xc4A\tm\xe0\xce\x11\x9bj\xc4\xd9\xf3!\xaf\x06\xa9\xa7/\x10\xfb\x17\x8a\xd5Eb\x9a+\x8e.\x16\x87\x97\x88ͥ\x98\xee_\x06\xa9ˑ*\xba\x02-]\x89\x96\xe8*YR_\x8dؽ\x06\xca\\\v\x15\xae\x83\xa2\xeb\xa1\xf0\u007f\x90\xb9\x01\xca݈\x899}\x93\xbd=\xf1E\xc8\xcdIA\xb7\xc8Hp\xab\x04\x81{[R\x98\xdbe${\x87ػS\x06<}WR\xf8w'\x85\xba')\xe8ޤ\x88\xeeK\x8a\xf0\xfe\xa40\x0f\xc8a\xfc\a\xc5\xea!\x19P\xf4\xb0\x04\xf4\x88\x04D\x8f&g\xa0\x1f\x93\x11\xf7qq\xe6\tq\xe1Iq\xf4\x948|Zl\x9e\xe1k\x98{\x96/wN?gC\xfe\"\xe4y1\xbd\x808x\x11m\xe0\xbe$6/#ξ\x02y\xaf\"\xf5\xf4kb\xffu\xb1zCLo\x8a\xa3\xb7\xc4\xe1\xdbb\xf3\x0e\xa6\xfb\xefB\xea=\xa4\x8a\xdeGK\x1f\xa0%\xfaP\x96\xd4\x1f!v?\x862\x9f@\x85O\xa1\xe83(\xfc\x1c2_\xf0\x9bB\xf1K~A\x15\xf5W\xfc\x12\xb4/\xa3\xaf\xc5\xf4\r\xe2\xe0[\xb4\x81\xfb\x9d\xd8|\x8f8\xfb\x03\xe4\xfd\x88\xd4\xd3\xf3\xc4\xfe|\xb1Z \xa6\x85\xe2h\x918\\,6K0\xdd_\n\xa9eH\x15-GK?\xa1%\xfaY\x96\xd4+\x10\xbb+\xa1\xcc*\xa8\xb0\x1a\x8a\xd6@\xe1ZȬ\xb3o\n\xa5\xf5vW%\xbd\xc1&\xf6\x17\x9e\x8d\x10m\xe2,\xd8\xccM\xe0n\x81\xccVβۘ\xdev\x8e<\xbd\x03\xf2wBj\x17D\xbb\xa1h\x0f\x14\xfe\x02\x99_y\x9a\xff\x1bS\xfdΑ\xa2?\xb8\xa1?\xb9!\xfa\vk\xe8\xbf9s\xffaf\xfee\x16j\x99Q\x1d3\xacg\x9a\xff\xf85\x94+\xee\xe5\xbb\xcc\xfe\xd8i\xe0ۓ\u007f\x0e\xc5IA\xfb,\xc0H\xe0,@\x10\xb8\xa9\xa40e2\x92-\x17{\x152\xe0\xe9ʤ\xf0\xf7M\n\xb5_R\xd0\xfeI\x11\x1d\x90\x14\xe1\x81Ia\x0e\x92\xc3\xf8\a\x8b\xd5!2\xa0\xe8P\t\xe80\t\x88\x0eO\xce@\x1f!#\xee\x91\xe2\xccQ\xe2\xc2\xd1\xe2\xe8\x18qx\xac\xd8\x1c\xb7\xc0\xbe\x03\xe6\x8e_\xc0[\xd6' \xd4\xfaD1\x9d\x8488\x19m\xe0\x9e\"6\xa7\"Φ!\xef4\xa4\x9e>]\xec7\x12\xab\xc6bj\"\x8e\x9a\x8a\xc3fb\xd3\x1c\xd3\xfd\x16\x90j\x89TQ+\xb4\xd4\x1a-Q\x1bYR\xb7E춃2g@\x85\xf6P\xd4\x01\n;B\xa6\x13o(ߙ\x9b\xbc\xee\xc2\xd3\xed\xefq]\xc5\xd4\rq\xd0\x1dm\xe0\xf6\x10\x9b\x9e\x88\xb3\xbd \xaf7RO\xf7\x11\xfb}Ū\x9f\x98\xfa\x8b\xa3\x01\xe2p\xa0\xd8\f\xc2t\u007f0\xa4\x86 U4\x14-\rCK4\\\x96\xd4#\x10\xbb#\xa1\xcc(\xa80\x1a\x8a\xc6@\xe1XȌ\xb3\x1b\xaa)\x8d\xb7MMI\x9fi\xa7\xf3\x17!\x13\xc44\x11qp\x16\xda\xc0=[l&!\xceN\x86\xbc)H==U\xecO\x13\xabs\xc44]\x1c\x9d+\x0eg\x88\xcdLL\xf7\xab \xa5\x90*ʠ\xa5,Z\xa2\x9c,\xa9g!v5\x941P!\x0fE.\x14\x16 \xe3\xd9\r\xe5J\xbemr%\x1d\xd8\xe9\xfcEHQL!\xe2\xe0<\xb4\x81;[l\bq6\x82\xbc\x12RO\xcf\x11\xfb\xd5bu\xbe\x98j\xc4\xd1\x05\xe2\xf0B\xb1\xb9\b\xd3\xfd\xb9\x90\xba\x18\xa9\xa2K\xd0ҥh\x89.\x93%\xf5\xe5\x88\xdd+\xa0̕P\xe1*(\xba\x1a\n\xaf\x81̵|\x85\xf2\xd7\xf1\xee\xf2\xfaz~>\xec-\xf7?1݀8\xb8\x11m\xe0\xde$67#\xce\xde\x02y\xb7\"\xf5\xf4mb\xffv\xb1\xbaCLw\x8a\xa3\xbb\xc4\xe1\xddbs\x0f\xa6\xfb\xf7B\xea>\xa4\x8a\xeeGK\x0f\xa0%zP\x96\xd4\x0f!v\x1f\x862\x8f@\x85G\xa1\xe81(|\x1c2O,H\x15\xf2O\xda]\xe5\xf5S6\xe1\xffi\f\xa2g8\v\x9e\xe5&p\x9f\x83\xcc\xf3\x9ce_`z/r\xe4\xe9\x97 \xffeH\xbd\x02ѫP\xf4\x1a\x14\xbe\x0e\x997x\x9a\xff&S\xbdő\xa2\xb7\xb9\xa1w\xb8!z\x17k\xe8\xf78s\xdfgf>`\x16>dF\x1f1Ï\x99\xe6\x93\x05\xa9\xa8\xea\xd3\x05eQ\x95\xfelA\xb9\xa5\xfe\x1c\xa2/8\v\xbe\xe4&p\xbf\x82\xccלe\xbfaz\xdfr\xe4\xe9\xef \xff{H\xfd\x00яP4\x0f\n\xe7Cf\x01O\xf3\x172\xd5\"\x8e\x14-憖pC\xb4\x14k\xe8e\x9c\xb9˙\x99\x9f\x98\x85\x9f\x99\xd1\nf\xb8\x92iV1s\xabyBN\xafYP\xc1\xd2kŴ\x0eq\xb0\x1em\xe0n\x10\x9b\x8d\x88\xb3\x9b o3ROo\x11\xfb[\xc5j\x9b\x98\xb6\x8b\xa3\x1d\xe2p\xa7\xd8\xec\xc2t\u007f7\xa4\xf6 U\xf4\vZ\xfa\x15-\xd1o\xb2\xa4\xfe\x1d\xb1\xfb\a\x94\xf9\x13*\xfc\x05E\u007fC\xe1?\x90\xf9\xd7n\xa8\xa6\xaa\xd665U\xba\xceN\xe7/B\xea\xc5\xf4\x1f\xe2`/\xda\xc0m\x10\x9b\x18qv\x9f\x85,\xcfYȩ\xa7Sb\xbfL\xac\xca\xc5T!\x8e*\xc5\xe1\xbeb\xb3\x1f\xa6\xfb\xfbC\xea\x00\xa4\x8a\x0eDK\a\xa1%:\x18\x0e\xf4!\x88\xddC\xa1\xccaP\xe1p(:\x02\n\x8f\x84\xccQP\xeehL\xcc\xe9c\x16V\xc2\xfaؤ\xa0\xe3d$8^\x82\xc0=!)̉2\x92=I\xec\x9d,\x03\x9e>%)\xfcS\x93B\xa5\x93\x82NK\x8a\xe8\xf4\xa4\b\x1b%\x85i,\x87\xf1\x9b\x88US\x19P\xd4L\x02j.\x01Q\x8b\xe4\ftK\x19q[\x893\xadŅ6⨭8l'6g,,\x8br\xc5\xf6v\xcb\xf6sN\a\x1b\xf2瞎b\xea\x848\xe8\x8c6p\xbb\x88MW\xc4\xd9n\x90\xd7\x1d\xa9\xa7{\x88\xfd\x9eb\xd5KL\xbd\xc5Q\x1fq\xd8Wl\xfaa\xba\xdf\x1fR\x03\x90*\x1a\x88\x96\x06\xa1%\x1a,K\xea!\x88ݡPf\x18T\x18\x0eE#\xa0p$dF-LE\xb9Ѽ+=\x86\x13\xad\xc7B4\x8e\xb3`<7\x81{&d&p\x96\x9d\xc8\xf4\xce\xe2\xc8\xd3gC\xfe$HM\x86h\n\x14M\x85\xc2i\x909\x87\xa7\xf9ә\xea\\\x8e\x14\xcd\xe0\x86frCT\x855\xb4\xe2\xcc\xcd03Yf!njf1C\xcd4\x86o\xb9\\1\xcf\x17\x85\xff.\x13_M\xfc]\xa6\xa4 OF\x02_\x82\xc0\r\x92\xc2\x14e$\x1b\x8a\xbd\xf3d\xc0ӳ\x93§\xa4PQRP))\xa29I\x11V'\x859_\x0e\xe3\u05c8\xd5\x052\xa0\xe8B\t\xe8\"\t\x88\xe6&g\xa0/\x96\x11\xf7\x12q\xe6Rq\xe12qt\xb98\xbcBl\xae\xb4[\xae\xc9]\x85-\xeb\xab\x11j}\x8d\x98\xaeE\x1c\\\x876p\xaf\x17\x9b\xff!\xce\xde\x00y7\"\xf5\xf4Mb\xfff\xb1\xbaEL\xb7\x8a\xa3\xdb\xc4\xe1\xedbs\a\xa6\xfbwB\xea.\xa4\x8a\xeeFK\xf7\xa0%\xbaW\x96\xd4\xf7!v\xef\x872\x0f@\x85\a\xa1\xe8!(|\x182\x8f\xd8[\xae\xf8\xa8\xddUQ?f\x13\xfesL\x10=\xc1Y\xf0$7\x81\xfb\x14d\x9e\xe6,\xfb\f\xd3{\x96#O?\a\xf9\xcfC\xea\x05\x88^\x84\xa2\x97\xa0\xf0eȼ\xc2\xd3\xfcW\x99\xea5\x8e\x14\xbd\xce\r\xbd\xc1\rћXC\xbfř\xfb63\xf3\x0e\xb3\xf0.3z\x8f\x19\xbe\xcf4\x1fXVW}h'TW\xe9\x8f\xec\x9e\xf9\x8b\x90\x8f\xc5\xf4\t\xe2\xe0S\xb4\x81\xfb\x99\xd8|\x8e8\xfb\x05\xe4}\x89\xd4\xd3_\x89\xfd\xaf\xc5\xea\x1b1}+\x8e\xbe\x13\x87ߋ\xcd\x0f\x98\xee\xff\b\xa9yH\x15\xcdGK\v\xd0\x12-\x94%\xf5\"\xc4\xeeb(\xb3\x04*,\x85\xa2eP\xb8\x1c2?A\xb9\x9f11\xa7W\xd8\xdb\x13_\x84\xacL\nZ%#\xc1j\t\x02wMR\x98\xb52\x92]'\xf6\xd6ˀ\xa77$\x85\xbf1)Ԧ\xa4\xa0\xcdI\x11mI\x8apkR\x98mr\x18\u007f\xbbX\xed\x90\x01E;%\xa0]\x12\x10\xedN\xce@\xef\x91\x11\xf7\x17q\xe6Wq\xe17q\xf4\xbb8\xfcCl\xfe\xe4k\x98\xfb\x8b/wN\xffmC\xfe\"\xe4\x1f1\xfd\x8b8\xa8E\x1b\xb8ubS\x8f8\xfb\x1f\xe4\xedE\xea\xe9\x06\xb1\x1f\x8b\xd5>\x8b`r\xc4QJ\x1c\x96\x89M\xf9\"\x9e\xeeW@\xaa\x12\xa9\xa2}\xd1\xd2~h\x89\xf6\x87\x03}\x00b\xf7@(s\x10T8\x18\x8a\x0e\x81\xc2C!s\xd8\"\xfb\xa6P<\xdc65E}\x84\x9d\xce_\x84\x1c)\xa6\xa3\x10\aG\xa3\r\xdcc\xc4\xe6X\xc4\xd9\xe3 \xefx\xa4\x9e>A\xec\x9f(V'\x89\xe9dqt\x8a83)\xfc\tI\xa1&&\x05\x9d\x95\x14\xd1\xd9I\x11NJ\n3Y\x0e\xe3O\x11\xab\xa92\xa0h\x9a\x04t\x8e\x04Dӓ3\xd0\xe7ʈ;C\x9c\x99).T\x89#%\x0e3b\x93\xb5[\xae\xce\xe5\xb0e=\v\xa1\xd6ZL\x06q\x90G\x1b\xb8\xae\xd8\x14\x10g=\xc8\xf3\x91z:\x10\xfbE\xb1\n\xc5t\x9e8\x9a-\x0eIl\"L\xf7K\x90\x9a\x83TQ5Z:\x1f-Q\x8d,\xa9/@\xec^\be.\x82\ns\xa1\xe8b(\xbc\x042\x97\xf2\x86\xf2\x97q\x93ח\xf3t\xfb{\xdc\x15b\xba\x12qp\x15\xda\xc0\xbdZl\xaeA\x9c\xbd\x16\xf2\xaeC\xea\xe9\xeb\xc5\xfe\xff\xc4\xea\x061\xdd(\x8en\x12\x877\x8b\xcd-\x98\xee\xdf\n\xa9ې*\xba\x1d-݁\x96\xe8NYR߅ؽ\x1b\xca\xdc\x03\x15\ue162\xfb\xa0\xf0~\xc8<\xc0o\n\xa5\a\xf9\x05U\xd2\x0f\xf1Kо\x8c\x1e\x16\xd3#\x88\x83G\xd1\x06\xeecb\xf38\xe2\xec\x13\x90\xf7$RO?%\xf6\x9f\x16\xabg\xc4\xf4\xac8zN\x1c>/6/`\xba\xff\"\xa4^B\xaa\xe8e\xb4\xf4\nZ\xa2WeI\xfd\x1ab\xf7u(\xf3\x06Tx\x13\x8aނ·!\xf3\x8e\xddP\xae\xf4\xaemr%\xfd\x9e\x9d\xce_\x84\xbc/\xa6\x0f\x10\a\x1f\xa2\r\u070f\xc4\xe6c\xc4\xd9O \xefS\xa4\x9e\xfeL\xec\u007f.V_\x88\xe9Kq\xf4\x958\xfcZl\xbe\xc1t\xff[H}\x87T\xd1\xf7h\xe9\a\xb4D?ʒz\x1ebw>\x94Y\x00\x15\x16B\xd1\"(\\\f\x99%|\x85\xf2Kywy\xbd\x8c\x9f\x0f{\xcb-\x17\xd3O\x88\x83\x9f\xd1\x06\xee\n\xb1Y\x898\xbb\n\xf2V#\xf5\xf4\x1a\xb1\xbfV\xac։i\xbd8\xda \x0e7\x8a\xcd&L\xf77Cj\vRE[\xd1\xd26\xb4D\xdbeI\xbd\x03\xb1\xbb\x13\xca\xec\x82\n\xbb\xa1h\x0f\x14\xfe\x02\x99_\xed\xdbv\xfe7\xbb\xab\xbc\xfe\xdd&\xfc\u007f\aA\xf4'g\xc1_\xdc\x04\xeeߐ\xf9\x87\xb3\xec\xbfL\xaf\x96#O\xd7A~=\xa4\xfe\x83h/\x145@a\f\x99}\x16\xdbi\xbe\xc3T\xa9\xc56RT\xc6\r\x95sCT\xc1\nt%g\xee\xbe\xcc\xcc~\xcc\xc2\xfe\xcc\xe8\x00fx \xd3\x1c\xb48\x15V\x1d\xbc\xb8,\xac҇,.\xb7ԇBt\x18g\xc1\xe1\xdc\x04\xee\x11\x909\x92\xb3\xecQL\xefh\x8e<}\f\xe4\x1f\v\xa9\xe3 :\x1e\x8aN\x80\xc2\x13!s\x12O\xf3Of\xaaS8Rt*7\x94\xe6\x86\xe84\xac\xa1O\xe7\xccm\xc4\xcc4f\x16\x9a0\xa3\xa6̰\x19\xd34g\xe6Z\xf0\x84\x9cn\xb9\xb8\x82\xa5[\x89\xa95\xe2\xa0\r\xda\xc0m+6\xed\x10gπ\xbc\xf6H=\xddA\xecw\x14\xabNb\xea,\x8e\xba\x88îb\xd3\r\xd3\xfd\xee\x90\xea\x81TQO\xb4\xd4\v-QoYR\xf7A\xec\xf6\x852\xfd\xa0B\u007f(\x1a\x00\x85\x03!3\xc8n\xa8\xa6j\xb0mj\xaa\xf4\x10;\x9d\xbf\b\x19*\xa6a\x88\x83\xe1h\x03w\x84،D\x9c\x1d\x05y\xa3\x91zz\x8c\xd8\x1f+V\xe3\xc44^\x1c\x9d)\x0e'\x88\xcdDL\xf7ς\xd4\xd9H\x15MBK\x93\xd1\x12M\x91%\xf5T\xc4\xee4(s\x0eT\x98\x0eE\xe7B\xe1\f\xc8̄rU\x98\x98\xd3jq%\xac3IAY\x19\tr\x12\x04\ueb240ZF\xb2F\xec\xe5e\xc0\xd3nR\xf8\x85\xa4P^R\x90\x9f\x14Q\x90\x14a1)L(\x87\xf1\xcf\x13\xab\xd92\xa0\x88$\xa0H\x02\xa2Rr\x06z\x8e\x8c\xb8\xd5\xe2\xcc\xf9\xe2B\x8d8\xba@\x1c^(6\x17٫\x97+ε[\xb6\x9fs.\xb6!\u007f\xee\xb9DL\x97\"\x0e.C\x1b\xb8\x97\x8b\xcd\x15\x88\xb3WB\xdeUH=}\xb5ؿF\xac\xae\x15\xd3u\xe2\xe8zq\xf8?\xb1\xb9\x01\xd3\xfd\x1b!u\x13RE7\xa3\xa5[\xd0\x12\xdd*K\xea\xdb\x10\xbb\xb7C\x99;\xa0\u009dPt\x17\x14\xde\r\x99{\xec\x9bB\xee^ޕ\xbe\x8f\x13\xad\xef\x87\xe8\x01\u0382\a\xb9\t܇ \xf30g\xd9G\x98ޣ\x1cy\xfa1\xc8\u007f\x1cRO@\xf4$\x14=\x05\x85OC\xe6\x19\x9e\xe6?\xcbT\xcfq\xa4\xe8yn\xe8\x05n\x88^\xc4\x1a\xfa%\xceܗ\x99\x99W\x98\x85W\x99\xd1k\xcc\xf0u\xa6y\x83o\xb9\\\xf1M\xbe(\xfc\xf7\x82\xf8j\xe2/\x06%\x05\xbd##\xc1\xbb\x12\x04\xee{Iaޗ\x91\xec\ab\xefC\x19\xf0\xf4GI\xe1\u007f\x9c\x14ꓤ\xa0O\x93\"\xfa,)\xc2ϓ\xc2|!\x87\xf1\xbf\x14\xab\xafd@\xd1\xd7\x12\xd07\x12\x10}\x9b\x9c\x81\xfeNF\xdc\xefř\x1fą\x1f\xc5\xd1\u2c2f\xd8\xf4\xe3k\x98\xebϗ;\xa7\aؐ\xbf\b\x19(\xa6A\x88\x83\xc1h\x03w\x88\xd8\fE\x9c\x1d\x06yÑzz\x84\xd8\x1f)V\xa3\xc44Z\x1c\x8d\x11\x87c\xc5f\x1c\xa6\xfb\xe3!u&RE\x13\xd0\xd2D\xb4Dgɒ\xfal\xc4\xee$(3\x19*L\x81\xa2\xa9P8\r2\xe7\xd8\r\xd5\x14\xa7ۦ\xa6\xa8ϵ\xd3\xf9\x8b\x90\x19b\x9a\x898\xa8B\x1b\xb8Jl2\x88\xb3Y\xc8\xcb!\xf5\xf4,\xb1\xaf\xc5ʈ)/\x8e\\qX\x10\x1b\x0f\xd3}\x1fR\x01REE\xb4\x14\xa2%:O\x96Գ\x11\xbb\x04e\"\xa8P\x82\xa29PX\r\x99\xf3\x97\xa4\xc2R\x8d\xddUI_`\x13\xfb\vυ\x10]\xc4Y0\x97\x9b\xc0\xbd\x182\x97p\x96\xbd\x94\xe9]Ƒ\xa7/\x87\xfc+ u%DWA\xd1\xd5Px\rd\xae\xe5i\xfeuLu=G\x8a\xfe\xc7\r\xdd\xc0\rэXC\xdfę{33s\v\xb3p+3\xba\x8d\x19\xde\xce4w\xf0k(W\xbc\x93\xef2\xfbc\xe7.\xbe=\xf9\xe7\xd0\xddIA\xf7\xc8Hp\xaf\x04\x81{_R\x98\xfbe$\xfb\x80\xd8{P\x06<\xfdPR\xf8\x0f'\x85z$)\xe8Ѥ\x88\x1eK\x8a\xf0\xf1\xa40O\xc8a\xfc'\xc5\xea)\x19P\xf4\xb4\x04\xf4\x8c\x04D\xcf&g\xa0\x9f\x93\x11\xf7yq\xe6\x05q\xe1Eq\xf4\x928|Yl^\xe1w\xc0ܫز~\r\xa1֯\x8b\xe9\r\xc4\xc1\x9bh\x03\xf7-\xb1y\x1bq\xf6\x1d\xc8{\x17\xa9\xa7\xdf\x13\xfb\xef\x8b\xd5\ab\xfaP\x1c}$\x0e?\x16\x9bO0\xdd\xff\x14R\x9f!U\xf49Z\xfa\x02-ї\xb2\xa4\xfe\n\xb1\xfb5\x94\xf9\x06*|\vE\xdfA\xe1\xf7\x90\xf9\x817\x94\xff\x91\x9b\xbc\x9e\xc7\xd3\xed\xefq\xf3Ŵ\x00q\xb0\x10m\xe0.\x12\x9bň\xb3K o)RO/\x13\xfb\xcb\xc5\xea'1\xfd,\x8eV\x88Õb\xb3\n\xd3\xfdՐZ\x83T\xd1Z\xb4\xb4\x0e-\xd1zYRo@\xecn\x842\x9b\xa0\xc2f(\xda\x02\x85[!\xb3\x8d\xdf\x14J\xdb\xf9\x05U\xd2;\xf8%h_F;Ŵ\vq\xb0\x1bm\xe0\xee\x11\x9b_\x10g\u007f\x85\xbcߐz\xfaw\xb1\xff\x87X\xfd)\xa6\xbf\xc4\xd1\xdf\xe2\xf0\x1f\xb1\xf9\x17\xd3\xfdZH\xd5!UT\x8f\x96\xfeCK\xb4W\x96\xd4\r\x88\xdd\x18\xca쳔Up\xa0(\x05\x85e\x90)_j?\xed\x95*l\x93+\xe9ʥ\x15,\xbd\xaf\x98\xf6C\x1c\xec\x8f6p\x0f\x10\x9b\x03\x11g\x0f\x82\xbc\x83\x91z\xfa\x10\xb1\u007f\xa8X\x1d&\xa6\xc3\xc5\xd1\x11\xe2\xf0H\xb19\n\xd3\xfd\xa3!u\fREǢ\xa5\xe3\xd0\x12\x1d/K\xea\x13\x10\xbb'B\x99\x93\xa0\xc2\xc9Pt\n\x14\x9e\n\x99\xb4\xddPM\xfe4\xde]^\x9fn\xa7\xf3\x17!\x8d\xc4\xd4\x18q\xd0\x04m\xe06\x15\x9bf\x88\xb3\xcd!\xaf\x05RO\xb7\x14\xfb\xadĪ\xb5\x98ڈ\xa3\xb6Ⱍ\u061c\x81\xe9~{Hu@\xaa\xa8#Zꄖ\xa8\xb3,\xa9\xbb v\xbbB\x99nP\xa1;\x14\xf5\x80\u009e\x90\xe9\xb54\x15\xe6{\xdb]\xe5u\x1f\x9b\xf0\xff\xe8\x03Q?\u0382\xfe\xdc\x04\xee\x00\xc8\f\xe4,;\x88\xe9\r\xe6\xc8\xd3C \u007f(\xa4\x86A4\x1c\x8aF@\xe1HȌ\xe2i\xfeh\xa6\x1aÑ\xa2\xb1\xdc\xd08n\x88\xc6c\r}&g\xee\x04ff\"\xb3p\x163:\x9b\x19Nb\x9a\xc9KS\xa6j\xca\xd22S\xa5\xa7.-\xb7\xd4\xd3 :\x87\xb3`:7\x81{.dfp\x96\x9d\xc9\xf4\xaa8\xf2\xb4\x82\xfc\f\xa4\xb2\x10\xe5\xa0h\x16\x14j\xc8\x18\x9e\xe6\xe7\x99\xca\xe5HQ\x81\x1b\xf2\xb8!\xf2\xb1\x86\x0e8s\x8b\xccL\xc8,\x9cnjf3Cb\x9a\x88\x99+\U0004471e\xb3\xb4\x82\xa5\xab\xc5t>\xe2\xa0\x06m\xe0^ 6\x17\"\xce^\x04ys\x91z\xfab\xb1\u007f\x89X]*\xa6\xcb\xc4\xd1\xe5\xe2\xf0\n\xb1\xb9\x12\xd3\xfd\xab u5REנ\xa5k\xd1\x12]'K\xea\xeb\x11\xbb\xff\x8327@\x85\x1b\xa1\xe8&(\xbc\x192\xb7\xd8\r\xd5T\xddj\x9b\x9a*}\x9b\x9d\xce_\x84\xdc.\xa6;\x10\aw\xa2\rܻ\xc4\xe6n\xc4\xd9{ \xef^\xa4\x9e\xbeO\xec\xdf/V\x0f\x88\xe9Aq\xf4\x908|Xl\x1e\xc1t\xffQH=\x86T\xd1\xe3h\xe9\t\xb4DOʒ\xfa)\xc4\xee\xd3P\xe6\x19\xa8\xf0,\x14=\a\x85\xcfC\xe6\x05(\xf7\"&\xe6\xf4KK+a\xfdrR\xd0+2\x12\xbc*Aྖ\x14\xe6u\x19ɾ!\xf6ޔ\x01O\xbf\x95\x14\xfe\xdbI\xa1\xdeI\nz7)\xa2\xf7\x92\"|?)\xcc\ar\x18\xffC\xb1\xfaH\x06\x14},\x01}\"\x01ѧ\xc9\x19\xe8\xcfd\xc4\xfd\\\x9c\xf9B\\\xf8R\x1c}%\x0e\xbf\x16\x9bo\xec\xd5\xcb\x15\xbf\xb5[\xb6\x9fs\xbe\xb3!\u007f\xee\xf9^L? \x0e~D\x1b\xb8\xf3\xc4f>\xe2\xec\x02\xc8[\x88\xd4Ӌ\xc4\xfeb\xb1Z\"\xa6\xa5\xe2h\x998\\.6?a\xba\xff3\xa4V U\xb4\x12-\xadBK\xb4Z\x96\xd4k\x10\xbbk\xa1\xcc:\xa8\xb0\x1e\x8a6@\xe1F\xc8l\xb2o\n\xb9ͼ+\xbd\x85\x13\xad\xb7B\xb4\x8d\xb3`;7\x81\xbb\x032;9\xcb\xeebz\xbb9\xf2\xf4\x1e\xc8\xff\x05R\xbfB\xf4\x1b\x14\xfd\x0e\x85\u007f@\xe6O\x9e\xe6\xff\xc5T\u007fs\xa4\xe8\x1fn\xe8_n\x88j\xb1\x86\xae\xe3̭gf\xfec\x16\xf62\xa3\x06f\x183\xcd>\xcb\xec-\x97+:\xcb*X:\xb5\xac\x12\xd6eIA\xe52\x12TH\x10\xb8\x95Ia\xf6\x95\x91\xec~bo\u007f\x19\xf0\xf4\x01I\xe1\x1f\x98\x14ꠤ\xa0\x83\x93\":$)\xc2C\x93\xc2\x1c&\x87\xf1\x0f\x17\xab#d@ё\x12\xd0Q\x12\x10\x1d\x9d\x9c\x81>FF\xdccř\xe3ą\xe3\xc5\xd1\t\xe2\xf0D\xb19i\x99}\xc3ȝ\x8c-\xebS\x10j}\xaa\x98҈\x83\xd3\xd0\x06\xee\xe9b\xd3\bq\xb61\xe45A\xea\xe9\xa6b\xbf\x99X5\x17S\vq\xd4R\x1c\xb6\x12\x9b֘\uedc1T[\xa4\x8aڡ\xa53\xd0\x12\xb5\x97%u\a\xc4nG(\xd3\t*t\x86\xa2.P\xd8\x152ݖ\xa5L\xb1\xbb\xddUQ\xf7\xb0\t\xff\xe5(\x88zq\x16\xf4\xe6&p\xfb@\xa6/g\xd9~L\xaf?G\x9e\x1e\x00\xf9\x03!5\b\xa2\xc1P4\x04\n\x87Bf\x18O\xf3\x873\xd5\b\x8e\x14\x8d\xe4\x86FqC4\x1ak\xe81\x9c\xb9c\x99\x99q\xcc\xc2xft&3\x9c\xc04\x13-\xab\xabβ\x13\xaa\xab\xf4\xd9v\xcf\xfcE\xc8$1MF\x1cLA\x1b\xb8S\xc5f\x1a\xe2\xec9\x907\x1d\xa9\xa7\xcf\x15\xfb3\xc4j\xa6\x98\xaađ\x12\x87\x19\xb1\xc9b\xba\x9f\x83\xd4,\xa4\x8a4Z2h\x89\xf2\xb2\xa4v\x11\xbb\x05(\xe3A\x05\x1f\x8a\x02(,B&\x84r\xe7abN϶\xb7'\xbe\b\xa1\xa4\xa0HF\x82\x92\x04\x81;')L\xb5\x8cd\xcf\x17{52\xe0\xe9\v\x92¿0)\xd4EIAs\x93\"\xba8)\xc2K\x92\xc2\\*\x87\xf1/\x13\xab\xcbe@\xd1\x15\x12Е\x12\x10]\x95\x9c\x81\xbeZF\xdckękŅ\xeb\xc4\xd1\xf5\xe2\xf0\u007fbs\x03_\xc3܍|\xb9s\xfa&\x1b\xf2\x17!7\x8b\xe9\x16\xc4\xc1\xadh\x03\xf76\xb1\xb9\x1dq\xf6\x0eȻ\x13\xa9\xa7\xef\x12\xfbw\x8b\xd5=b\xbaW\x1c\xdd'\x0e\xef\x17\x9b\a0\xdd\u007f\x10R\x0f!U\xf40Zz\x04-ѣ\xb2\xa4~\f\xb1\xfb8\x94y\x02*<\tEOA\xe1Ӑy\x86\xdf\x14\x8a\xcf\xf2\v\xaa\xa8\x9f㗠}\x19=/\xa6\x17\x10\a/\xa2\rܗ\xc4\xe6e\xc4\xd9W \xefU\xa4\x9e~M\xec\xbf.Vo\x88\xe9Mq\xf4\x968|[l\xde\xc1t\xff]H\xbd\x87T\xd1\xfbh\xe9\x03\xb4D\x1fʒ\xfa#\xc4\xee\xc7P\xe6\x13\xa8\xf0)\x14}\x06\x85\x9fC\xe6\v\xfb\xa6P\xfa\xd2\ueaa4\xbf\xb2\x89\xfd\x85\xe7k\x88\xbe\xe1,\xf8\x96\x9b\xc0\xfd\x0e2\xdfs\x96\xfd\x81\xe9\xfdȑ\xa7\xe7A\xfe|H-\x80h!\x14-\x82\xc2ŐY\xc2\xd3\xfc\xa5L\xb5\x8c#E˹\xa1\x9f\xb8!\xfa\x19k\xe8\x15\x9c\xb9+\x99\x99U\xcc\xc2jf\xb4\x86\x19\xaee\x9au\xfc\x1a\xca\x15\xd7\xf3]f\u007f\xecl\xe0ۓ\u007f\x0emL\n\xda$#\xc1f\t\x02wKR\x98\xad2\x92\xdd&\xf6\xb6ˀ\xa7w$\x85\xbf3)\u052e\xa4\xa0\xddI\x11\xedI\x8a\xf0\x97\xa40\xbf\xcaa\xfc\xdf\xc4\xeaw\x19P\xf4\x87\x04\xf4\xa7\x04D\u007f%g\xa0\xff\x96\x11\xf7\x1fq\xe6_q\xa1V\x1cՉ\xc3z\xb1\xf9\x8f\xdf\x01s{\xb1e݀P\xebXL\xfb,\xe78p\x96s\x1b\xb8)\xb1)C\x9c-\x87\xbc\n\xa4\x9e\xae\x14\xfb\xfb\x8a\xd5~b\xda_\x1c\x1d \x0e\x0f\x14\x9b\x830\xdd?\x18R\x87 Ut(Z:\f-\xd1\u1ca4>\x02\xb1{$\x949\n*\x1c\rE\xc7@᱐9n\xb9\xddP\xfexn\xf2\xfa\x04\x9en\u007f\x8f;QL'!\x0eNF\x1b\xb8\xa7\x88ͩ\x88\xb3i\xc8;\r\xa9\xa7O\x17\xfb\x8dĪ\xb1\x98\x9a\x88\xa3\xa6Ⱉ\xd84\xc7t\xbf\x05\xa4Z\"U\xd4\n-\xb5FK\xd4F\x96\xd4m\x11\xbb\xed\xa0\xcc\x19P\xa1=\x14u\x80\u008e\x90\xe9d7TS\xeal\x9b\x9a\x92\xeeb\xa7\xf3\x17!]\xc5\xd4\rq\xd0\x1dm\xe0\xf6\x10\x9b\x9e\x88\xb3\xbd \xaf7RO\xf7\x11\xfb}Ū\x9f\x98\xfa\x8b\xa3\x01\xe2p\xa0\xd8\f\xc2t\u007f0\xa4\x86 U4\x14-\rCK4\\\x96\xd4#\x10\xbb#\xa1\xcc(\xa80\x1a\x8a\xc6@\xe1XȌ\xb3\x1bʕ\xc6\xdb&W\xd2g\xda\xe9\xfcE\xc8\x041MD\x1c\x9c\x856p\xcf\x16\x9bI\x88\xb3\x93!o\nROO\x15\xfb\xd3\xc4\xea\x1c1M\x17G\xe7\x8a\xc3\x19b3\x13\xd3\xfd*H)\xa4\x8a2h)\x8b\x96('K\xeaY\x88]\re\fT\xc8C\x91\v\x85\x05\xc8x|\x85\xf2>\xef.\xaf\x03~>\xec-W\x14S\x8888\x0fm\xe0\xce\x16\x1bB\x9c\x8d \xaf\x84\xd4\xd3s\xc4~\xb5X\x9d/\xa6\x1aqt\x818\xbcPl.\xc2t\u007f.\xa4.F\xaa\xe8\x12\xb4t)Z\xa2\xcbdI}9b\xf7\n(s%T\xb8\n\x8a\xae\x86\xc2k s\xed\xf2\x94\xc9_gw\x95\xd7\xd7ۄ\xffG\x1f\x88n\xe0,\xb8\x91\x9b\xc0\xbd\t27s\x96\xbd\x85\xe9\xddʑ\xa7o\x83\xfc\xdb!u\aDwB\xd1]Px7d\xee\xe1i\xfe\xbdLu\x1fG\x8a\xee\xe7\x86\x1e\xe0\x86\xe8A\xac\xa1\x1f\xe2\xcc}\x98\x99y\x84Yx\x94\x19=\xc6\f\x1fg\x9a'\x96\x97M\xeb;\xfdI\xf0)\xf0i\xf0\x19\xf0Y\xf09\xf0y\xf0\x05\xf0E\xf0%\xf0e\xf0\x15\xf0U\xf05\xf0u\xf0\r\xf0M\xf0-\xf0m\xf0\x1d\xf0]\xf0=\xf0}\xf0\x03\xf0C\xf0#\xf0c\xf0\x13\xf0S\xf03\xf0s\xf0\v\xf0K\xf0+\xf0k\xf0\x1b\xf0[\xf0;\xf0{\xf0\a\xf0Gp\x1e8\x1f\\\x00.\x04\x17\x81\x8b\xc1%\xe0Rp\x19\xb8\x1c\xfc\t\xfc\x19\\\x01\xae\x04W\x81\xab\xc15\xe0Zp\x1d\xb8\x1e\xdc\x00n\x047\x81\x9b\xc1-\xe0Vp\x1b\xb8\x1d\xdc\x01\xee\x04w\x81\xbb\xc1=\xe0/\xe0\xaf\xe0o\xe0\xef\xe0\x1f\xe0\x9f\xe0_\xe0\xdf\xe0?\xe0\xbf`-X\aփ\xff\x81{\xc1\x06p\x9f\xda\xf2\x91U5i\xa7\xb6\xd2*\xd0\xe9Tm\xd9\xc8\\\xba̲\x98.\xb7cE/]\xc1ʤ+\xad\xaa\xf3\xe9}E\xfb\xd5V\x8c\xb4oq\xe9\xfdm[\xf2\xd2\a\xb0\xfc\xf4\x81\xb5ec\xaa\xd2\a\x81\a\x83\x87Ԗ\x8d\xaaJ\x1f\x8a\xfa0\xf0p\xf0\b\xf0H\xf0\xa8\xda\xf21U\x85\xf4ѢcDNJ\x8e\x13\x1d/:At\"+H\x9f$:Yt\x8a\xe8TQZt\x1a\xcbO\x9f.j$j,j\xc2\n\xd3ME\xcdD\xcdk+\xec<\x9dn\x91\xb8e\xe2V\x89['n\xc3\x0f\xafI\xb7\xad\xad\x1c\x83筝\xddI1}\x06\xd8\x1e\xec\x00v\x04;\x81\x9d\xc1.`W\xb0\x1b\xd8\x1d\xec\x01\xf6\xb4\xc7-\x16ҽD\xbdE}D}E\xfdD\xfdYAz\x80=!\xfb\x19'=0\xf1\xa0ă\x13\x0f\xe1\x87դ\x87\x8a\x86\x89\x86\x8bFXU\x97\xd2#E\xa3D\xa3EcDcE\xe3D\xe3EgZ\x95\n\xe9\t\xa2\x89,?}\x96]\xd0\xfe\x88H\x9f\x9dxR\xe2ɉ\xa7$\x9eZ[>\xd1\x1ec\x1an\x8csj\xcbG\xd9\v:]t.+HϨ\xad\x1cſh\xa7g\xda\xc7\x14\xd3U\xb5\x15\xa3\xec\x8f\u07b4\xb2]>\x9d\xa9-\x1f\x97\xabIgYA:W[1\xce>4=˶\xf6\xa0ڶ\xd5\xf6\xa1\xa6\xb6l\\>\x9d\xaf-\x9f`\x8f\xea\x8a\n,?\xed\xd5VL\xa8\xaa)\xa4\xfd\xda\xca\t\xb8r\x81\xcdsa\xbaȪI\x87v\xb8\xda^\xd8\xf3l\x9b\x0fҳY~\x9aXa:\xaa-\x9bR\x95.ՖO\xb1Ǜ#\xaa\xae\xad\x98\xc27\xc4\xf9\x89k\x12_\x90\xf8\xc2\xc4\x17%\x9e\x9b\xf8\xe2ė\xd8c\xe6җ\x82\x97\x81\x97\x83W\x80W\x82W\x81W\x83׀ׁׂ\xd7\xdb\x13\xc9\x15\xd2\xff\x13\xdd \xbaQt\x13+H\xdf,\xbaEt\xab\xe86\xd1\xed\xa2;Dw\x8a\xee\x12\xdd-\xbaGt\xaf\xe8>\xd1\xfd\xa2\aX^\xfaA\xd1C\xa2\x87E\x8f\x88\x1e\x15=\xc6\xf2ӏ\x8b\x9e\x10=)zJ\xf44+L?c\x9f\x1b\xbe\xfe\xcf&~.\xf1\xf3\x89_H\xfcb\xe2\x97\x12\xbf\x9c\xf8\x95į&~-\xf1\xeb\x89\xdfH\xfc&/W\x93~K\xf4\xb6\xe8\x1dѻ\xb5\xa9\xb1\xe9\xf7\xec\x13\\L\xbf\x0f~\x00~\b~\x04~\f~\x02~\n~\x06~\x0e~a\x17\xe1\x17\xe0\x97\xb5\xe5\x93\xec\x1b\xe5WV\xf6\xe9\xfaڎ\x95\xd2߀߂߁߃?\x80?\x82\xf3\xc0\xf9\xe0\x02p\xa1=3\xfbz[$Z,Z\xc2\n\xd2KE\xcbD\xcbE?\xb1\xbc\xf4Ϣ\x15\xa2\x95\xa2U\xf6\xf4\xf8帺65<\xbd\x86\xb1\x96\xb1\x8e\xb1\x9e\xb1\x81\xb1\x91\xb1\x89\xb1\x99\xb1\x85\xb1\x95\xb1\x8d\xb1\x9d\xb1\x83\xb1\xb3\xb6lx!\xbd\v\xdcm\x19\xa4\xf7\x80\xbf\x80\xbf\x82\xbf\x81\xbf\x83\u007f\x80\u007fZ\xfa\xe9\xbf\xc0\xbf\xc1\u007f,\xc3\xf4\xbf`-XW[1\x90_'\xf5\xb5\xe5\x03\xed\r\xfd_m\xd9\xc0|z\xafm\xec\xeb\xb1\xc1\xcaT\xa5c\xfb\x10c\xc7\xf6\xa9c\xdb\x1ffu\x15\x13\x8d}i\xa6\x12\x97\xd5UN\xe4\xff\n\x93.Ga_\xfc\x15\\\xf0ũ\xac+\x1fg\x8a\xe9}\xeb*\xac\n\xe9\xfd\xea*\x86\xf1\x03\xf7O|\x805\xbf\xbd\x1ch]m\x8ftP]٤R\xfa\xe0\xba\xf2a\xa5b\xfa\x90\xbar\xfb\x034}\xa8谺ʩ8\xe4\xe1\xd2\x1fQW1\xde~JK\x1f)\xedQ\xa2\xa3\xeb\xca&\xe7\xd3\xc7ԕO5\xa5\xf4\xb1ueS\xf2\xe9\xe3\xea\xf8m\xed\xf8\xba\x8a\tƮtB]\xe5\xe45k\x96c\x91OYxe\xf5e\xb3f\xe5˙^E}\x8a\xa2Jƾ\xf5\xfc\xd0\xfd\xc0\xfd\xc1\x03\xc0\x03\xc1\x83\xc0\x83\xc1C\xc0C\xc1\xc3\xc0\xc3\xc1#\xc0#\xebS~p\x94E\xeeh\x8b\xfc1\xf5\xa99\xc1\xb1\xf5e~\xc1\x1c\x87\xf1\xe3\xc1\x13\xc0\x13\xc1\x93\xc0\x93\xebS5\xf9S\xebS\xf6\x93i\xbd3\xf3\xf4z'ۨ\xde1\x8d\xeb\x9dB\x93z\xc7kZ\xef\xf8\xcd\xec\xbf\xe6\xf5NԢ\xdei\xd5Ҟ\xb2iU\xefPk.\xdaآm\xbdS\xd5\xce\xfek_\xef\xa8\x0e\xf5\x8e\xeeh\x8f\xd0\xc9\x1e\xa1s\xbd3\xa7K\xbdsAW\xac\xd2\xcd\xce\xef^\xef\xd4\xf4\xb0\xc7\xedi\xff\xf5\xb2\xc7\ue351>\xb6ꋪ_\xbd\x13\xf4\xb7\xff\x06\xa0\x1bX\uf103\xec\xbf\xc1\xe8\x86ԧ\"\x1a\xca\x18V\xef\xcc\x1e\u038b\x8f\xb0\x87\x1ciwY\x1ceW\x1em\x8f9\xc6>z\xac=\xdak\x98\xf1:\xf8\x06\xf8&\xf8\x16\xf86\xf8\x0e\xf8.\xf8\x1e\xf8>\xf8\x01\xf8!\xf8\x11\xf81\xf8\t\xf8)\xf8\x19\xf89\xf8\x05\xf8%\xf8\x15\xf85\xf8\r\xf8-\xf8\x1d\xf8=\xf8\x03\xf8#8\x0f\x9c\x0f.\x00\x17\x82\x8b\xe4\xd9؋\x1f[\xe0\x00p 8\b\x1c\f\x0e\x01\x87\x82\xc3\xc0\xe1\xe0\bp$8\n\x1c\r\x8e\x01ǂ\xb7\x80\xb7\x82߀߂߁߃?\x80?\x82\xf3\xc0\xf9\xe0\x02p!\xb8\b\\\f.\x01\x97\x82\xcb\xc0\xe5\xe0O\xe0\xcf\xe0\np%\xb8\n\\\r\xae\x01ׂ\xeb\xc0\xf5\xe0\x06p#\xb8\t\xdc\fn\x01\xb7\x82\xdb\xc0\xed\xe0\x0ep'\xb8\v\xdc\r\xee\x01\u007f\x01\xff\x03\xf7\x82\r\xe0>\rL\aL\x81e`9X\x01V\x82\xfb\x82\xfb\x81\xfb\x83\a\x80\a\x82\a\x81\a\x83\x87\x80\x87\x82\x87\x81\x87\x83G\x80G\x82G\x81G\x83ǀǂǁǃ'\x80'\x82'\x81'\x83\xa7\x80\xa7\x82\x8d\x1a\x9c\x8b\x1b\xa3j\x026\x05\x9b\x81\xcd\xc1\x16`K\xb0\x15\xd8\x1al\x03\xb6\x05ہg4\xa4ڶmߐjӦC\x83Ӧc\x833\xa3\x93\xfd\u05f9\xc1iޥ!\xd5\"ݵ\xc1\xb9\xb0[Cjn\xba{\x833\xadGCjz\xbagCjZ\xf3^\re-\xa6\xa7{7\xa4z\xf5\xea\xd3P֧O\xbao\x83ӫ_C\xaaO\xba\xbf}\xdc\x00~\xdc@;sP\x833w0\x96\x19\x02\x0e\x05\x87\x81\xe3\x1a\x9c\xd6\xe3\xed\xbf3\x1b\x9c\xb6\x13\x1b\x9c\x9eg58=\xcenp\xfaNjpN\x9blOe\x8a=\x85\xa9\rN\x8bi\xf68\xe7\xd8\xe3L\xb7>\xd7zF\x83\xd3hf\x83Ӵ\xaa\xc1i\xa9\x1a\x9cV\x19\xfbج]=\xd7\xe0\xf4\x99\xd5\xe0\xf46\r\xce9\xf9\x06\xa7\xb1\xdb\xe04)48\xfd<\xac\xe6\x83\x01X\x04\t,\x81{\xc1\x06p\x9f\x187G윖\x8a\x9d\xd3\xcbb\xa7Qy\xec4\xae\x88\x9d&\x95\xb1\xd3t\xdf\xd8i\xb6_\xec4\xdf?vZ\x1c\x10;-\x0f\x8c\x9dV\a\xc5N\xeb\x83c\xa7\xcd!\xb1\xd3\xf6\xd0\xd8iwX\xec\x9cqx\xec\xb4?\"v:\x1c\x19;\x1d\x8f\x8a\x9dNG\xc7N\xe7cb\xa7˱\xb1\xd3\xf5\xb8\xd8\xe9v|\xect?!vz\x9c\x18;=O\x8a\x9d^'\xc7N\xefSb\xa7ϩ\xb1\xd37\x1d;\xfdN\x8b\x9d\xfe\xa7\xc7\u0380F\xb13\xb0q\xec\fj\x12;\x83\x9b\xc6ΐf\xb13\xb4y\xec\fk\x11;\xc3[\xc6ΈV\xb13\xb2u\xec\x8cj\x13;\xa3\xdb\xc6Θv\xb13\xf6\x8c\xd8\x19\xd7>v\xc6w\x88\x9d3;\xc6΄N\xb13\xb1s\xec\x9c\xd5%v\xce\xee\x1a;\x93\xba\xc5\xce\xe4\xee\xb13\xa5G\xecL\xed\x19;\xd3z\xc5\xce9\xbdcgz\x9f\xd89\xb7o\xec\xcc\xe8\x17;3\xfb\xc7NՀ\xd8Q\x03c'3(v\xb2\x83c'7$vf\r\x8d\x1d=,v\xcc\xf0\xd8ɏ\x88\x1dwd\xec\x14FŎ7:v\xfc1\xb1\x13\x8c\x8d\x9d\xe2\xb8\xd8\t\xc7\xc7\xceyg\xc6\xce\xec\t\xb1C\x13c':+vJg\xc7ΜI\xb1S=9vΟ\x12;5Sc\xe7\x82i\xb1\xbd\xba\xb1s\xd1\xf4ؙ{n\xec\\<\x03\xcf\xfeL\xb0\xca>\x9fʞ_ƞ[\xd6>\xc79\xfb|ϊS\xd5EmO\xce\xd8\x13\xc8ۃ\xba\xf6\xc4\nvQ/N\xd5T\xf9\x16\xa5\xc0\xa2X\x8cSQ)\xb4\x17\xe7<\xfb\xd8\xd9\xf6\xb1d\x1f\x1b\xd9ǖ\xecc\xe7ĩBU\xb5E\xfe|\x8bR\x8dE\xee\x02\x8b\xe2\x85q\x8a\xaa.\xb2\xc8ϵ(]l\x91\xbbĢx\xa9=\\\xd5e\x16\xf9\xcb\xf9\xc0WX䮴(^\x15\xa7\x82\xaa\xab-\xf2\xd7X\x94\xae\xb5\xc8]gQ\xbc>N\x99\xaa\xffY\xe4o\xb0(\xddh\x91\xbbɢxs\x9c\xf2\xabn\xb1\xc8\xdfjQ\xba\xcd\"w\xbbE\xf1\x0e\xde\xc1\x9d\xbc\x83\xbbx\awǩ\xd9U\xf7X\xe4\xef\xb5(\xddg\x91\xbbߢ\xf8\x80}\n\xaa\x1e\xb4\xcf\xf5C\xf6\xfey\xd8\xde?\x8f\xdak\xf2X\x9c\xd2\xfaq\vz\u008e" +0x003f: "?" +0x0040: "@" +0x0041: "A" +0x0042: "B" +0x0043: "C" +0x0044: "D" +0x0045: "E" +0x0046: "F" +0x0047: "G" +0x0048: "H" +0x0049: "I" +0x004a: "J" +0x004b: "K" +0x004c: "L" +0x004d: "M" +0x004e: "N" +0x004f: "O" +0x0050: "P" +0x0051: "Q" +0x0052: "R" +0x0053: "S" +0x0054: "T" +0x0055: "U" +0x0056: "V" +0x0057: "W" +0x0058: "X" +0x0059: "Y" +0x005a: "Z" +0x005b: "]" +0x005c: "\\" +0x005d: "]" +0x005e: "^" +0x005f: "_" +0x0060: "`" +0x0061: "a" +0x0062: "b" +0x0063: "c" +0x0064: "d" +0x0065: "e" +0x0066: "f" +0x0067: "g" +0x0068: "h" +0x0069: "i" +0x006a: "j" +0x006b: "k" +0x006c: "l" +0x006d: "m" +0x006e: "n" +0x006f: "o" +0x0070: "p" +0x0071: "q" +0x0072: "r" +0x0073: "s" +0x0074: "t" +0x0075: "u" +0x0076: "v" +0x0077: "w" +0x0078: "x" +0x0079: "y" +0x007a: "z" +0x007b: "{" +0x007c: "|" +0x007d: "}" +0x007e: "~" +0x007f: "\x7f" +0x0080: "" +0x0081: "" +0x0082: "" +0x0083: "" +0x0084: "" +0x0085: "" +0x0086: "" +0x0087: "" +0x0088: "" +0x0089: "" +0x008a: "" +0x008b: "" +0x008c: "" +0x008d: "" +0x008e: "" +0x008f: "" +0x0090: "" +0x0091: "" +0x0092: "" +0x0093: "" +0x0094: "" +0x0095: "" +0x0096: "" +0x0097: "" +0x0098: "" +0x0099: "" +0x009a: "" +0x009b: "" +0x009c: "" +0x009d: "" +0x009e: "" +0x009f: "" +0x00a0: " " +0x00a1: "!" +0x00a2: "C/" +0x00a3: "PS" +0x00a4: "$?" +0x00a5: "Y=" +0x00a6: "|" +0x00a7: "SS" +0x00a8: "\"" +0x00a9: "(c)" +0x00aa: "a" +0x00ab: "<<" +0x00ac: "!" +0x00ad: "" +0x00ae: "(r)" +0x00af: "-" +0x00b0: "deg" +0x00b1: "+-" +0x00b2: "2" +0x00b3: "3" +0x00b4: "'" +0x00b5: "u" +0x00b6: "P" +0x00b7: "*" +0x00b8: "," +0x00b9: "1" +0x00ba: "o" +0x00bb: ">>" +0x00bc: "1/4" +0x00bd: "1/2" +0x00be: "3/4" +0x00bf: "?" +0x00c0: "A" +0x00c1: "A" +0x00c2: "A" +0x00c3: "A" +0x00c4: "A" +0x00c5: "A" +0x00c6: "AE" +0x00c7: "C" +0x00c8: "E" +0x00c9: "E" +0x00ca: "E" +0x00cb: "E" +0x00cc: "I" +0x00cd: "I" +0x00ce: "I" +0x00cf: "I" +0x00d0: "D" +0x00d1: "N" +0x00d2: "O" +0x00d3: "O" +0x00d4: "O" +0x00d5: "O" +0x00d6: "O" +0x00d7: "x" +0x00d8: "O" +0x00d9: "U" +0x00da: "U" +0x00db: "U" +0x00dc: "U" +0x00dd: "Y" +0x00de: "Th" +0x00df: "ss" +0x00e0: "a" +0x00e1: "a" +0x00e2: "a" +0x00e3: "a" +0x00e4: "a" +0x00e5: "a" +0x00e6: "ae" +0x00e7: "c" +0x00e8: "e" +0x00e9: "e" +0x00ea: "e" +0x00eb: "e" +0x00ec: "i" +0x00ed: "i" +0x00ee: "i" +0x00ef: "i" +0x00f0: "d" +0x00f1: "n" +0x00f2: "o" +0x00f3: "o" +0x00f4: "o" +0x00f5: "o" +0x00f6: "o" +0x00f7: "/" +0x00f8: "o" +0x00f9: "u" +0x00fa: "u" +0x00fb: "u" +0x00fc: "u" +0x00fd: "y" +0x00fe: "th" +0x00ff: "y" +/* x001 */ +0x0100: "A" +0x0101: "a" +0x0102: "A" +0x0103: "a" +0x0104: "A" +0x0105: "a" +0x0106: "C" +0x0107: "c" +0x0108: "C" +0x0109: "c" +0x010a: "C" +0x010b: "c" +0x010c: "C" +0x010d: "c" +0x010e: "D" +0x010f: "d" +0x0110: "D" +0x0111: "d" +0x0112: "E" +0x0113: "e" +0x0114: "E" +0x0115: "e" +0x0116: "E" +0x0117: "e" +0x0118: "E" +0x0119: "e" +0x011a: "E" +0x011b: "e" +0x011c: "G" +0x011d: "g" +0x011e: "G" +0x011f: "g" +0x0120: "G" +0x0121: "g" +0x0122: "G" +0x0123: "g" +0x0124: "H" +0x0125: "h" +0x0126: "H" +0x0127: "h" +0x0128: "I" +0x0129: "i" +0x012a: "I" +0x012b: "i" +0x012c: "I" +0x012d: "i" +0x012e: "I" +0x012f: "i" +0x0130: "I" +0x0131: "i" +0x0132: "IJ" +0x0133: "ij" +0x0134: "J" +0x0135: "j" +0x0136: "K" +0x0137: "k" +0x0138: "k" +0x0139: "L" +0x013a: "l" +0x013b: "L" +0x013c: "l" +0x013d: "L" +0x013e: "l" +0x013f: "L" +0x0140: "l" +0x0141: "L" +0x0142: "l" +0x0143: "N" +0x0144: "n" +0x0145: "N" +0x0146: "n" +0x0147: "N" +0x0148: "n" +0x0149: "'n" +0x014a: "ng" +0x014b: "NG" +0x014c: "O" +0x014d: "o" +0x014e: "O" +0x014f: "o" +0x0150: "O" +0x0151: "o" +0x0152: "OE" +0x0153: "oe" +0x0154: "R" +0x0155: "r" +0x0156: "R" +0x0157: "r" +0x0158: "R" +0x0159: "r" +0x015a: "S" +0x015b: "s" +0x015c: "S" +0x015d: "s" +0x015e: "S" +0x015f: "s" +0x0160: "S" +0x0161: "s" +0x0162: "T" +0x0163: "t" +0x0164: "T" +0x0165: "t" +0x0166: "T" +0x0167: "t" +0x0168: "U" +0x0169: "u" +0x016a: "U" +0x016b: "u" +0x016c: "U" +0x016d: "u" +0x016e: "U" +0x016f: "u" +0x0170: "U" +0x0171: "u" +0x0172: "U" +0x0173: "u" +0x0174: "W" +0x0175: "w" +0x0176: "Y" +0x0177: "y" +0x0178: "Y" +0x0179: "Z" +0x017a: "z" +0x017b: "Z" +0x017c: "z" +0x017d: "Z" +0x017e: "z" +0x017f: "s" +0x0180: "b" +0x0181: "B" +0x0182: "B" +0x0183: "b" +0x0184: "6" +0x0185: "6" +0x0186: "O" +0x0187: "C" +0x0188: "c" +0x0189: "D" +0x018a: "D" +0x018b: "D" +0x018c: "d" +0x018d: "d" +0x018e: "3" +0x018f: "@" +0x0190: "E" +0x0191: "F" +0x0192: "f" +0x0193: "G" +0x0194: "G" +0x0195: "hv" +0x0196: "I" +0x0197: "I" +0x0198: "K" +0x0199: "k" +0x019a: "l" +0x019b: "l" +0x019c: "W" +0x019d: "N" +0x019e: "n" +0x019f: "O" +0x01a0: "O" +0x01a1: "o" +0x01a2: "OI" +0x01a3: "oi" +0x01a4: "P" +0x01a5: "p" +0x01a6: "YR" +0x01a7: "2" +0x01a8: "2" +0x01a9: "SH" +0x01aa: "sh" +0x01ab: "t" +0x01ac: "T" +0x01ad: "t" +0x01ae: "T" +0x01af: "U" +0x01b0: "u" +0x01b1: "Y" +0x01b2: "V" +0x01b3: "Y" +0x01b4: "y" +0x01b5: "Z" +0x01b6: "z" +0x01b7: "ZH" +0x01b8: "ZH" +0x01b9: "zh" +0x01ba: "zh" +0x01bb: "2" +0x01bc: "5" +0x01bd: "5" +0x01be: "ts" +0x01bf: "w" +0x01c0: "|" +0x01c1: "||" +0x01c2: "|=" +0x01c3: "!" +0x01c4: "DZ" +0x01c5: "Dz" +0x01c6: "dz" +0x01c7: "LJ" +0x01c8: "Lj" +0x01c9: "lj" +0x01ca: "NJ" +0x01cb: "Nj" +0x01cc: "nj" +0x01cd: "A" +0x01ce: "a" +0x01cf: "I" +0x01d0: "i" +0x01d1: "O" +0x01d2: "o" +0x01d3: "U" +0x01d4: "u" +0x01d5: "U" +0x01d6: "u" +0x01d7: "U" +0x01d8: "u" +0x01d9: "U" +0x01da: "u" +0x01db: "U" +0x01dc: "u" +0x01dd: "@" +0x01de: "A" +0x01df: "a" +0x01e0: "A" +0x01e1: "a" +0x01e2: "AE" +0x01e3: "ae" +0x01e4: "G" +0x01e5: "g" +0x01e6: "G" +0x01e7: "g" +0x01e8: "K" +0x01e9: "k" +0x01ea: "O" +0x01eb: "o" +0x01ec: "O" +0x01ed: "o" +0x01ee: "ZH" +0x01ef: "zh" +0x01f0: "j" +0x01f1: "DZ" +0x01f2: "Dz" +0x01f3: "dz" +0x01f4: "G" +0x01f5: "g" +0x01f6: "HV" +0x01f7: "W" +0x01f8: "N" +0x01f9: "n" +0x01fa: "A" +0x01fb: "a" +0x01fc: "AE" +0x01fd: "ae" +0x01fe: "O" +0x01ff: "o" +/* x002 */ +0x0200: "A" +0x0201: "a" +0x0202: "A" +0x0203: "a" +0x0204: "E" +0x0205: "e" +0x0206: "E" +0x0207: "e" +0x0208: "I" +0x0209: "i" +0x020a: "I" +0x020b: "i" +0x020c: "O" +0x020d: "o" +0x020e: "O" +0x020f: "o" +0x0210: "R" +0x0211: "r" +0x0212: "R" +0x0213: "r" +0x0214: "U" +0x0215: "u" +0x0216: "U" +0x0217: "u" +0x0218: "S" +0x0219: "s" +0x021a: "T" +0x021b: "t" +0x021c: "Y" +0x021d: "y" +0x021e: "H" +0x021f: "h" +0x0220: "N" +0x0221: "d" +0x0222: "OU" +0x0223: "ou" +0x0224: "Z" +0x0225: "z" +0x0226: "A" +0x0227: "a" +0x0228: "E" +0x0229: "e" +0x022a: "O" +0x022b: "o" +0x022c: "O" +0x022d: "o" +0x022e: "O" +0x022f: "o" +0x0230: "O" +0x0231: "o" +0x0232: "Y" +0x0233: "y" +0x0234: "l" +0x0235: "n" +0x0236: "t" +0x0237: "j" +0x0238: "db" +0x0239: "qp" +0x023a: "A" +0x023b: "C" +0x023c: "c" +0x023d: "L" +0x023e: "T" +0x023f: "s" +0x0240: "z" +0x0241: "[?]" +0x0242: "[?]" +0x0243: "B" +0x0244: "U" +0x0245: "^" +0x0246: "E" +0x0247: "e" +0x0248: "J" +0x0249: "j" +0x024a: "q" +0x024b: "q" +0x024c: "R" +0x024d: "r" +0x024e: "Y" +0x024f: "y" +0x0250: "a" +0x0251: "a" +0x0252: "a" +0x0253: "b" +0x0254: "o" +0x0255: "c" +0x0256: "d" +0x0257: "d" +0x0258: "e" +0x0259: "@" +0x025a: "@" +0x025b: "e" +0x025c: "e" +0x025d: "e" +0x025e: "e" +0x025f: "j" +0x0260: "g" +0x0261: "g" +0x0262: "g" +0x0263: "g" +0x0264: "u" +0x0265: "Y" +0x0266: "h" +0x0267: "h" +0x0268: "i" +0x0269: "i" +0x026a: "I" +0x026b: "l" +0x026c: "l" +0x026d: "l" +0x026e: "lZ" +0x026f: "W" +0x0270: "W" +0x0271: "m" +0x0272: "n" +0x0273: "n" +0x0274: "n" +0x0275: "o" +0x0276: "OE" +0x0277: "O" +0x0278: "F" +0x0279: "r" +0x027a: "r" +0x027b: "r" +0x027c: "r" +0x027d: "r" +0x027e: "r" +0x027f: "r" +0x0280: "R" +0x0281: "R" +0x0282: "s" +0x0283: "S" +0x0284: "j" +0x0285: "S" +0x0286: "S" +0x0287: "t" +0x0288: "t" +0x0289: "u" +0x028a: "U" +0x028b: "v" +0x028c: "^" +0x028d: "w" +0x028e: "y" +0x028f: "Y" +0x0290: "z" +0x0291: "z" +0x0292: "Z" +0x0293: "Z" +0x0294: "?" +0x0295: "?" +0x0296: "?" +0x0297: "C" +0x0298: "@" +0x0299: "B" +0x029a: "E" +0x029b: "G" +0x029c: "H" +0x029d: "j" +0x029e: "k" +0x029f: "L" +0x02a0: "q" +0x02a1: "?" +0x02a2: "?" +0x02a3: "dz" +0x02a4: "dZ" +0x02a5: "dz" +0x02a6: "ts" +0x02a7: "tS" +0x02a8: "tC" +0x02a9: "fN" +0x02aa: "ls" +0x02ab: "lz" +0x02ac: "WW" +0x02ad: "]]" +0x02ae: "h" +0x02af: "h" +0x02b0: "k" +0x02b1: "h" +0x02b2: "j" +0x02b3: "r" +0x02b4: "r" +0x02b5: "r" +0x02b6: "r" +0x02b7: "w" +0x02b8: "y" +0x02b9: "'" +0x02ba: "\"" +0x02bb: "`" +0x02bc: "'" +0x02bd: "`" +0x02be: "`" +0x02bf: "'" +0x02c0: "?" +0x02c1: "?" +0x02c2: "<" +0x02c3: ">" +0x02c4: "^" +0x02c5: "V" +0x02c6: "^" +0x02c7: "V" +0x02c8: "'" +0x02c9: "-" +0x02ca: "/" +0x02cb: "\\" +0x02cc: "," +0x02cd: "_" +0x02ce: "\\" +0x02cf: "/" +0x02d0: ":" +0x02d1: "." +0x02d2: "`" +0x02d3: "'" +0x02d4: "^" +0x02d5: "V" +0x02d6: "+" +0x02d7: "-" +0x02d8: "V" +0x02d9: "." +0x02da: "@" +0x02db: "," +0x02dc: "~" +0x02dd: "\"" +0x02de: "R" +0x02df: "X" +0x02e0: "G" +0x02e1: "l" +0x02e2: "s" +0x02e3: "x" +0x02e4: "?" +0x02e5: "" +0x02e6: "" +0x02e7: "" +0x02e8: "" +0x02e9: "" +0x02ea: "" +0x02eb: "" +0x02ec: "V" +0x02ed: "=" +0x02ee: "\"" +0x02ef: "[?]" +0x02f0: "[?]" +0x02f1: "[?]" +0x02f2: "[?]" +0x02f3: "[?]" +0x02f4: "[?]" +0x02f5: "[?]" +0x02f6: "[?]" +0x02f7: "[?]" +0x02f8: "[?]" +0x02f9: "[?]" +0x02fa: "[?]" +0x02fb: "[?]" +0x02fc: "[?]" +0x02fd: "[?]" +0x02fe: "[?]" +/* x003 */ +0x0300: "" +0x0301: "" +0x0302: "" +0x0303: "" +0x0304: "" +0x0305: "" +0x0306: "" +0x0307: "" +0x0308: "" +0x0309: "" +0x030a: "" +0x030b: "" +0x030c: "" +0x030d: "" +0x030e: "" +0x030f: "" +0x0310: "" +0x0311: "" +0x0312: "" +0x0313: "" +0x0314: "" +0x0315: "" +0x0316: "" +0x0317: "" +0x0318: "" +0x0319: "" +0x031a: "" +0x031b: "" +0x031c: "" +0x031d: "" +0x031e: "" +0x031f: "" +0x0320: "" +0x0321: "" +0x0322: "" +0x0323: "" +0x0324: "" +0x0325: "" +0x0326: "" +0x0327: "" +0x0328: "" +0x0329: "" +0x032a: "" +0x032b: "" +0x032c: "" +0x032d: "" +0x032e: "" +0x032f: "" +0x0330: "" +0x0331: "" +0x0332: "" +0x0333: "" +0x0334: "" +0x0335: "" +0x0336: "" +0x0337: "" +0x0338: "" +0x0339: "" +0x033a: "" +0x033b: "" +0x033c: "" +0x033d: "" +0x033e: "" +0x033f: "" +0x0340: "" +0x0341: "" +0x0342: "" +0x0343: "" +0x0344: "" +0x0345: "" +0x0346: "" +0x0347: "" +0x0348: "" +0x0349: "" +0x034a: "" +0x034b: "" +0x034c: "" +0x034d: "" +0x034e: "" +0x034f: "[?]" +0x0350: "[?]" +0x0351: "[?]" +0x0352: "[?]" +0x0353: "[?]" +0x0354: "[?]" +0x0355: "[?]" +0x0356: "[?]" +0x0357: "[?]" +0x0358: "[?]" +0x0359: "[?]" +0x035a: "[?]" +0x035b: "[?]" +0x035c: "[?]" +0x035d: "[?]" +0x035e: "[?]" +0x035f: "[?]" +0x0360: "" +0x0361: "" +0x0362: "" +0x0363: "a" +0x0364: "e" +0x0365: "i" +0x0366: "o" +0x0367: "u" +0x0368: "c" +0x0369: "d" +0x036a: "h" +0x036b: "m" +0x036c: "r" +0x036d: "t" +0x036e: "v" +0x036f: "x" +0x0370: "[?]" +0x0371: "[?]" +0x0372: "[?]" +0x0373: "[?]" +0x0374: "'" +0x0375: "," +0x0376: "[?]" +0x0377: "[?]" +0x0378: "[?]" +0x0379: "[?]" +0x037a: "" +0x037b: "[?]" +0x037c: "[?]" +0x037d: "[?]" +0x037e: "?" +0x037f: "[?]" +0x0380: "[?]" +0x0381: "[?]" +0x0382: "[?]" +0x0383: "[?]" +0x0384: "" +0x0385: "" +0x0386: "A" +0x0387: ";" +0x0388: "E" +0x0389: "E" +0x038a: "I" +0x038b: "[?]" +0x038c: "O" +0x038d: "[?]" +0x038e: "U" +0x038f: "O" +0x0390: "I" +0x0391: "A" +0x0392: "B" +0x0393: "G" +0x0394: "D" +0x0395: "E" +0x0396: "Z" +0x0397: "E" +0x0398: "Th" +0x0399: "I" +0x039a: "K" +0x039b: "L" +0x039c: "M" +0x039d: "N" +0x039e: "Ks" +0x039f: "O" +0x03a0: "P" +0x03a1: "R" +0x03a2: "[?]" +0x03a3: "S" +0x03a4: "T" +0x03a5: "U" +0x03a6: "Ph" +0x03a7: "Kh" +0x03a8: "Ps" +0x03a9: "O" +0x03aa: "I" +0x03ab: "U" +0x03ac: "a" +0x03ad: "e" +0x03ae: "e" +0x03af: "i" +0x03b0: "u" +0x03b1: "a" +0x03b2: "b" +0x03b3: "g" +0x03b4: "d" +0x03b5: "e" +0x03b6: "z" +0x03b7: "e" +0x03b8: "th" +0x03b9: "i" +0x03ba: "k" +0x03bb: "l" +0x03bc: "m" +0x03bd: "n" +0x03be: "x" +0x03bf: "o" +0x03c0: "p" +0x03c1: "r" +0x03c2: "s" +0x03c3: "s" +0x03c4: "t" +0x03c5: "u" +0x03c6: "ph" +0x03c7: "kh" +0x03c8: "ps" +0x03c9: "o" +0x03ca: "i" +0x03cb: "u" +0x03cc: "o" +0x03cd: "u" +0x03ce: "o" +0x03cf: "[?]" +0x03d0: "b" +0x03d1: "th" +0x03d2: "U" +0x03d3: "U" +0x03d4: "U" +0x03d5: "ph" +0x03d6: "p" +0x03d7: "&" +0x03d8: "[?]" +0x03d9: "[?]" +0x03da: "St" +0x03db: "st" +0x03dc: "W" +0x03dd: "w" +0x03de: "Q" +0x03df: "q" +0x03e0: "Sp" +0x03e1: "sp" +0x03e2: "Sh" +0x03e3: "sh" +0x03e4: "F" +0x03e5: "f" +0x03e6: "Kh" +0x03e7: "kh" +0x03e8: "H" +0x03e9: "h" +0x03ea: "G" +0x03eb: "g" +0x03ec: "CH" +0x03ed: "ch" +0x03ee: "Ti" +0x03ef: "ti" +0x03f0: "k" +0x03f1: "r" +0x03f2: "c" +0x03f3: "j" +0x03f4: "[?]" +0x03f5: "[?]" +0x03f6: "[?]" +0x03f7: "[?]" +0x03f8: "[?]" +0x03f9: "[?]" +0x03fa: "[?]" +0x03fb: "[?]" +0x03fc: "[?]" +0x03fd: "[?]" +0x03fe: "[?]" +/* x004 */ +0x0400: "Ie" +0x0401: "Io" +0x0402: "Dj" +0x0403: "Gj" +0x0404: "Ie" +0x0405: "Dz" +0x0406: "I" +0x0407: "Yi" +0x0408: "J" +0x0409: "Lj" +0x040a: "Nj" +0x040b: "Tsh" +0x040c: "Kj" +0x040d: "I" +0x040e: "U" +0x040f: "Dzh" +0x0410: "A" +0x0411: "B" +0x0412: "V" +0x0413: "G" +0x0414: "D" +0x0415: "E" +0x0416: "Zh" +0x0417: "Z" +0x0418: "I" +0x0419: "I" +0x041a: "K" +0x041b: "L" +0x041c: "M" +0x041d: "N" +0x041e: "O" +0x041f: "P" +0x0420: "R" +0x0421: "S" +0x0422: "T" +0x0423: "U" +0x0424: "F" +0x0425: "Kh" +0x0426: "Ts" +0x0427: "Ch" +0x0428: "Sh" +0x0429: "Shch" +0x042a: "'" +0x042b: "Y" +0x042c: "'" +0x042d: "E" +0x042e: "Iu" +0x042f: "Ia" +0x0430: "a" +0x0431: "b" +0x0432: "v" +0x0433: "g" +0x0434: "d" +0x0435: "e" +0x0436: "zh" +0x0437: "z" +0x0438: "i" +0x0439: "i" +0x043a: "k" +0x043b: "l" +0x043c: "m" +0x043d: "n" +0x043e: "o" +0x043f: "p" +0x0440: "r" +0x0441: "s" +0x0442: "t" +0x0443: "u" +0x0444: "f" +0x0445: "kh" +0x0446: "ts" +0x0447: "ch" +0x0448: "sh" +0x0449: "shch" +0x044a: "'" +0x044b: "y" +0x044c: "'" +0x044d: "e" +0x044e: "iu" +0x044f: "ia" +0x0450: "ie" +0x0451: "io" +0x0452: "dj" +0x0453: "gj" +0x0454: "ie" +0x0455: "dz" +0x0456: "i" +0x0457: "yi" +0x0458: "j" +0x0459: "lj" +0x045a: "nj" +0x045b: "tsh" +0x045c: "kj" +0x045d: "i" +0x045e: "u" +0x045f: "dzh" +0x0460: "O" +0x0461: "o" +0x0462: "E" +0x0463: "e" +0x0464: "Ie" +0x0465: "ie" +0x0466: "E" +0x0467: "e" +0x0468: "Ie" +0x0469: "ie" +0x046a: "O" +0x046b: "o" +0x046c: "Io" +0x046d: "io" +0x046e: "Ks" +0x046f: "ks" +0x0470: "Ps" +0x0471: "ps" +0x0472: "F" +0x0473: "f" +0x0474: "Y" +0x0475: "y" +0x0476: "Y" +0x0477: "y" +0x0478: "u" +0x0479: "u" +0x047a: "O" +0x047b: "o" +0x047c: "O" +0x047d: "o" +0x047e: "Ot" +0x047f: "ot" +0x0480: "Q" +0x0481: "q" +0x0482: "*1000*" +0x0483: "" +0x0484: "" +0x0485: "" +0x0486: "" +0x0487: "[?]" +0x0488: "*100.000*" +0x0489: "*1.000.000*" +0x048a: "[?]" +0x048b: "[?]" +0x048c: "\"" +0x048d: "\"" +0x048e: "R'" +0x048f: "r'" +0x0490: "G'" +0x0491: "g'" +0x0492: "G'" +0x0493: "g'" +0x0494: "G'" +0x0495: "g'" +0x0496: "Zh'" +0x0497: "zh'" +0x0498: "Z'" +0x0499: "z'" +0x049a: "K'" +0x049b: "k'" +0x049c: "K'" +0x049d: "k'" +0x049e: "K'" +0x049f: "k'" +0x04a0: "K'" +0x04a1: "k'" +0x04a2: "N'" +0x04a3: "n'" +0x04a4: "Ng" +0x04a5: "ng" +0x04a6: "P'" +0x04a7: "p'" +0x04a8: "Kh" +0x04a9: "kh" +0x04aa: "S'" +0x04ab: "s'" +0x04ac: "T'" +0x04ad: "t'" +0x04ae: "U" +0x04af: "u" +0x04b0: "U'" +0x04b1: "u'" +0x04b2: "Kh'" +0x04b3: "kh'" +0x04b4: "Tts" +0x04b5: "tts" +0x04b6: "Ch'" +0x04b7: "ch'" +0x04b8: "Ch'" +0x04b9: "ch'" +0x04ba: "H" +0x04bb: "h" +0x04bc: "Ch" +0x04bd: "ch" +0x04be: "Ch'" +0x04bf: "ch'" +0x04c0: "`" +0x04c1: "Zh" +0x04c2: "zh" +0x04c3: "K'" +0x04c4: "k'" +0x04c5: "[?]" +0x04c6: "[?]" +0x04c7: "N'" +0x04c8: "n'" +0x04c9: "[?]" +0x04ca: "[?]" +0x04cb: "Ch" +0x04cc: "ch" +0x04cd: "[?]" +0x04ce: "[?]" +0x04cf: "[?]" +0x04d0: "a" +0x04d1: "a" +0x04d2: "A" +0x04d3: "a" +0x04d4: "Ae" +0x04d5: "ae" +0x04d6: "Ie" +0x04d7: "ie" +0x04d8: "@" +0x04d9: "@" +0x04da: "@" +0x04db: "@" +0x04dc: "Zh" +0x04dd: "zh" +0x04de: "Z" +0x04df: "z" +0x04e0: "Dz" +0x04e1: "dz" +0x04e2: "I" +0x04e3: "i" +0x04e4: "I" +0x04e5: "i" +0x04e6: "O" +0x04e7: "o" +0x04e8: "O" +0x04e9: "o" +0x04ea: "O" +0x04eb: "o" +0x04ec: "E" +0x04ed: "e" +0x04ee: "U" +0x04ef: "u" +0x04f0: "U" +0x04f1: "u" +0x04f2: "U" +0x04f3: "u" +0x04f4: "Ch" +0x04f5: "ch" +0x04f6: "[?]" +0x04f7: "[?]" +0x04f8: "Y" +0x04f9: "y" +0x04fa: "[?]" +0x04fb: "[?]" +0x04fc: "[?]" +0x04fd: "[?]" +0x04fe: "[?]" +/* x005 */ +0x0500: "[?]" +0x0501: "[?]" +0x0502: "[?]" +0x0503: "[?]" +0x0504: "[?]" +0x0505: "[?]" +0x0506: "[?]" +0x0507: "[?]" +0x0508: "[?]" +0x0509: "[?]" +0x050a: "[?]" +0x050b: "[?]" +0x050c: "[?]" +0x050d: "[?]" +0x050e: "[?]" +0x050f: "[?]" +0x0510: "[?]" +0x0511: "[?]" +0x0512: "[?]" +0x0513: "[?]" +0x0514: "[?]" +0x0515: "[?]" +0x0516: "[?]" +0x0517: "[?]" +0x0518: "[?]" +0x0519: "[?]" +0x051a: "[?]" +0x051b: "[?]" +0x051c: "[?]" +0x051d: "[?]" +0x051e: "[?]" +0x051f: "[?]" +0x0520: "[?]" +0x0521: "[?]" +0x0522: "[?]" +0x0523: "[?]" +0x0524: "[?]" +0x0525: "[?]" +0x0526: "[?]" +0x0527: "[?]" +0x0528: "[?]" +0x0529: "[?]" +0x052a: "[?]" +0x052b: "[?]" +0x052c: "[?]" +0x052d: "[?]" +0x052e: "[?]" +0x052f: "[?]" +0x0530: "[?]" +0x0531: "A" +0x0532: "B" +0x0533: "G" +0x0534: "D" +0x0535: "E" +0x0536: "Z" +0x0537: "E" +0x0538: "E" +0x0539: "T`" +0x053a: "Zh" +0x053b: "I" +0x053c: "L" +0x053d: "Kh" +0x053e: "Ts" +0x053f: "K" +0x0540: "H" +0x0541: "Dz" +0x0542: "Gh" +0x0543: "Ch" +0x0544: "M" +0x0545: "Y" +0x0546: "N" +0x0547: "Sh" +0x0548: "O" +0x0549: "Ch`" +0x054a: "P" +0x054b: "J" +0x054c: "Rh" +0x054d: "S" +0x054e: "V" +0x054f: "T" +0x0550: "R" +0x0551: "Ts`" +0x0552: "W" +0x0553: "P`" +0x0554: "K`" +0x0555: "O" +0x0556: "F" +0x0557: "[?]" +0x0558: "[?]" +0x0559: "<" +0x055a: "'" +0x055b: "/" +0x055c: "!" +0x055d: "," +0x055e: "?" +0x055f: "." +0x0560: "[?]" +0x0561: "a" +0x0562: "b" +0x0563: "g" +0x0564: "d" +0x0565: "e" +0x0566: "z" +0x0567: "e" +0x0568: "e" +0x0569: "t`" +0x056a: "zh" +0x056b: "i" +0x056c: "l" +0x056d: "kh" +0x056e: "ts" +0x056f: "k" +0x0570: "h" +0x0571: "dz" +0x0572: "gh" +0x0573: "ch" +0x0574: "m" +0x0575: "y" +0x0576: "n" +0x0577: "sh" +0x0578: "o" +0x0579: "ch`" +0x057a: "p" +0x057b: "j" +0x057c: "rh" +0x057d: "s" +0x057e: "v" +0x057f: "t" +0x0580: "r" +0x0581: "ts`" +0x0582: "w" +0x0583: "p`" +0x0584: "k`" +0x0585: "o" +0x0586: "f" +0x0587: "ew" +0x0588: "[?]" +0x0589: "." +0x058a: "-" +0x058b: "[?]" +0x058c: "[?]" +0x058d: "[?]" +0x058e: "[?]" +0x058f: "[?]" +0x0590: "[?]" +0x0591: "" +0x0592: "" +0x0593: "" +0x0594: "" +0x0595: "" +0x0596: "" +0x0597: "" +0x0598: "" +0x0599: "" +0x059a: "" +0x059b: "" +0x059c: "" +0x059d: "" +0x059e: "" +0x059f: "" +0x05a0: "" +0x05a1: "" +0x05a2: "[?]" +0x05a3: "" +0x05a4: "" +0x05a5: "" +0x05a6: "" +0x05a7: "" +0x05a8: "" +0x05a9: "" +0x05aa: "" +0x05ab: "" +0x05ac: "" +0x05ad: "" +0x05ae: "" +0x05af: "" +0x05b0: "@" +0x05b1: "e" +0x05b2: "a" +0x05b3: "o" +0x05b4: "i" +0x05b5: "e" +0x05b6: "e" +0x05b7: "a" +0x05b8: "a" +0x05b9: "o" +0x05ba: "[?]" +0x05bb: "u" +0x05bc: "'" +0x05bd: "" +0x05be: "" +0x05bf: "" +0x05c0: "" +0x05c1: "" +0x05c2: "" +0x05c3: ":" +0x05c4: "" +0x05c5: "[?]" +0x05c6: "[?]" +0x05c7: "[?]" +0x05c8: "[?]" +0x05c9: "[?]" +0x05ca: "[?]" +0x05cb: "[?]" +0x05cc: "[?]" +0x05cd: "[?]" +0x05ce: "[?]" +0x05cf: "[?]" +0x05d0: "" +0x05d1: "b" +0x05d2: "g" +0x05d3: "d" +0x05d4: "h" +0x05d5: "v" +0x05d6: "z" +0x05d7: "kh" +0x05d8: "t" +0x05d9: "y" +0x05da: "k" +0x05db: "k" +0x05dc: "l" +0x05dd: "m" +0x05de: "m" +0x05df: "n" +0x05e0: "n" +0x05e1: "s" +0x05e2: "`" +0x05e3: "p" +0x05e4: "p" +0x05e5: "ts" +0x05e6: "ts" +0x05e7: "q" +0x05e8: "r" +0x05e9: "sh" +0x05ea: "t" +0x05eb: "[?]" +0x05ec: "[?]" +0x05ed: "[?]" +0x05ee: "[?]" +0x05ef: "[?]" +0x05f0: "V" +0x05f1: "oy" +0x05f2: "i" +0x05f3: "'" +0x05f4: "\"" +0x05f5: "[?]" +0x05f6: "[?]" +0x05f7: "[?]" +0x05f8: "[?]" +0x05f9: "[?]" +0x05fa: "[?]" +0x05fb: "[?]" +0x05fc: "[?]" +0x05fd: "[?]" +0x05fe: "[?]" +/* x006 */ +0x0600: "[?]" +0x0601: "[?]" +0x0602: "[?]" +0x0603: "[?]" +0x0604: "[?]" +0x0605: "[?]" +0x0606: "[?]" +0x0607: "[?]" +0x0608: "[?]" +0x0609: "[?]" +0x060a: "[?]" +0x060b: "[?]" +0x060c: "," +0x060d: "[?]" +0x060e: "[?]" +0x060f: "[?]" +0x0610: "[?]" +0x0611: "[?]" +0x0612: "[?]" +0x0613: "[?]" +0x0614: "[?]" +0x0615: "[?]" +0x0616: "[?]" +0x0617: "[?]" +0x0618: "[?]" +0x0619: "[?]" +0x061a: "[?]" +0x061b: ";" +0x061c: "[?]" +0x061d: "[?]" +0x061e: "[?]" +0x061f: "?" +0x0620: "[?]" +0x0621: "" +0x0622: "a" +0x0623: "'" +0x0624: "w'" +0x0625: "" +0x0626: "y'" +0x0627: "" +0x0628: "b" +0x0629: "@" +0x062a: "t" +0x062b: "th" +0x062c: "j" +0x062d: "H" +0x062e: "kh" +0x062f: "d" +0x0630: "dh" +0x0631: "r" +0x0632: "z" +0x0633: "s" +0x0634: "sh" +0x0635: "S" +0x0636: "D" +0x0637: "T" +0x0638: "Z" +0x0639: "`" +0x063a: "G" +0x063b: "[?]" +0x063c: "[?]" +0x063d: "[?]" +0x063e: "[?]" +0x063f: "[?]" +0x0640: "" +0x0641: "f" +0x0642: "q" +0x0643: "k" +0x0644: "l" +0x0645: "m" +0x0646: "n" +0x0647: "h" +0x0648: "w" +0x0649: "~" +0x064a: "y" +0x064b: "an" +0x064c: "un" +0x064d: "in" +0x064e: "a" +0x064f: "u" +0x0650: "i" +0x0651: "W" +0x0652: "" +0x0653: "" +0x0654: "'" +0x0655: "'" +0x0656: "[?]" +0x0657: "[?]" +0x0658: "[?]" +0x0659: "[?]" +0x065a: "[?]" +0x065b: "[?]" +0x065c: "[?]" +0x065d: "[?]" +0x065e: "[?]" +0x065f: "[?]" +0x0660: "0" +0x0661: "1" +0x0662: "2" +0x0663: "3" +0x0664: "4" +0x0665: "5" +0x0666: "6" +0x0667: "7" +0x0668: "8" +0x0669: "9" +0x066a: "%" +0x066b: "." +0x066c: "," +0x066d: "*" +0x066e: "[?]" +0x066f: "[?]" +0x0670: "" +0x0671: "'" +0x0672: "'" +0x0673: "'" +0x0674: "" +0x0675: "'" +0x0676: "'w" +0x0677: "'u" +0x0678: "'y" +0x0679: "tt" +0x067a: "tth" +0x067b: "b" +0x067c: "t" +0x067d: "T" +0x067e: "p" +0x067f: "th" +0x0680: "bh" +0x0681: "'h" +0x0682: "H" +0x0683: "ny" +0x0684: "dy" +0x0685: "H" +0x0686: "ch" +0x0687: "cch" +0x0688: "dd" +0x0689: "D" +0x068a: "D" +0x068b: "Dt" +0x068c: "dh" +0x068d: "ddh" +0x068e: "d" +0x068f: "D" +0x0690: "D" +0x0691: "rr" +0x0692: "R" +0x0693: "R" +0x0694: "R" +0x0695: "R" +0x0696: "R" +0x0697: "R" +0x0698: "j" +0x0699: "R" +0x069a: "S" +0x069b: "S" +0x069c: "S" +0x069d: "S" +0x069e: "S" +0x069f: "T" +0x06a0: "GH" +0x06a1: "F" +0x06a2: "F" +0x06a3: "F" +0x06a4: "v" +0x06a5: "f" +0x06a6: "ph" +0x06a7: "Q" +0x06a8: "Q" +0x06a9: "kh" +0x06aa: "k" +0x06ab: "K" +0x06ac: "K" +0x06ad: "ng" +0x06ae: "K" +0x06af: "g" +0x06b0: "G" +0x06b1: "N" +0x06b2: "G" +0x06b3: "G" +0x06b4: "G" +0x06b5: "L" +0x06b6: "L" +0x06b7: "L" +0x06b8: "L" +0x06b9: "N" +0x06ba: "N" +0x06bb: "N" +0x06bc: "N" +0x06bd: "N" +0x06be: "h" +0x06bf: "Ch" +0x06c0: "hy" +0x06c1: "h" +0x06c2: "H" +0x06c3: "@" +0x06c4: "W" +0x06c5: "oe" +0x06c6: "oe" +0x06c7: "u" +0x06c8: "yu" +0x06c9: "yu" +0x06ca: "W" +0x06cb: "v" +0x06cc: "y" +0x06cd: "Y" +0x06ce: "Y" +0x06cf: "W" +0x06d0: "" +0x06d1: "" +0x06d2: "y" +0x06d3: "y'" +0x06d4: "." +0x06d5: "ae" +0x06d6: "" +0x06d7: "" +0x06d8: "" +0x06d9: "" +0x06da: "" +0x06db: "" +0x06dc: "" +0x06dd: "@" +0x06de: "#" +0x06df: "" +0x06e0: "" +0x06e1: "" +0x06e2: "" +0x06e3: "" +0x06e4: "" +0x06e5: "" +0x06e6: "" +0x06e7: "" +0x06e8: "" +0x06e9: "^" +0x06ea: "" +0x06eb: "" +0x06ec: "" +0x06ed: "" +0x06ee: "[?]" +0x06ef: "[?]" +0x06f0: "0" +0x06f1: "1" +0x06f2: "2" +0x06f3: "3" +0x06f4: "4" +0x06f5: "5" +0x06f6: "6" +0x06f7: "7" +0x06f8: "8" +0x06f9: "9" +0x06fa: "Sh" +0x06fb: "D" +0x06fc: "Gh" +0x06fd: "&" +0x06fe: "+m" +/* x007 */ +0x0700: "//" +0x0701: "/" +0x0702: "," +0x0703: "!" +0x0704: "!" +0x0705: "-" +0x0706: "," +0x0707: "," +0x0708: ";" +0x0709: "?" +0x070a: "~" +0x070b: "{" +0x070c: "}" +0x070d: "*" +0x070e: "[?]" +0x070f: "" +0x0710: "'" +0x0711: "" +0x0712: "b" +0x0713: "g" +0x0714: "g" +0x0715: "d" +0x0716: "d" +0x0717: "h" +0x0718: "w" +0x0719: "z" +0x071a: "H" +0x071b: "t" +0x071c: "t" +0x071d: "y" +0x071e: "yh" +0x071f: "k" +0x0720: "l" +0x0721: "m" +0x0722: "n" +0x0723: "s" +0x0724: "s" +0x0725: "`" +0x0726: "p" +0x0727: "p" +0x0728: "S" +0x0729: "q" +0x072a: "r" +0x072b: "sh" +0x072c: "t" +0x072d: "[?]" +0x072e: "[?]" +0x072f: "[?]" +0x0730: "a" +0x0731: "a" +0x0732: "a" +0x0733: "A" +0x0734: "A" +0x0735: "A" +0x0736: "e" +0x0737: "e" +0x0738: "e" +0x0739: "E" +0x073a: "i" +0x073b: "i" +0x073c: "u" +0x073d: "u" +0x073e: "u" +0x073f: "o" +0x0740: "" +0x0741: "`" +0x0742: "'" +0x0743: "" +0x0744: "" +0x0745: "X" +0x0746: "Q" +0x0747: "@" +0x0748: "@" +0x0749: "|" +0x074a: "+" +0x074b: "[?]" +0x074c: "[?]" +0x074d: "[?]" +0x074e: "[?]" +0x074f: "[?]" +0x0750: "[?]" +0x0751: "[?]" +0x0752: "[?]" +0x0753: "[?]" +0x0754: "[?]" +0x0755: "[?]" +0x0756: "[?]" +0x0757: "[?]" +0x0758: "[?]" +0x0759: "[?]" +0x075a: "[?]" +0x075b: "[?]" +0x075c: "[?]" +0x075d: "[?]" +0x075e: "[?]" +0x075f: "[?]" +0x0760: "[?]" +0x0761: "[?]" +0x0762: "[?]" +0x0763: "[?]" +0x0764: "[?]" +0x0765: "[?]" +0x0766: "[?]" +0x0767: "[?]" +0x0768: "[?]" +0x0769: "[?]" +0x076a: "[?]" +0x076b: "[?]" +0x076c: "[?]" +0x076d: "[?]" +0x076e: "[?]" +0x076f: "[?]" +0x0770: "[?]" +0x0771: "[?]" +0x0772: "[?]" +0x0773: "[?]" +0x0774: "[?]" +0x0775: "[?]" +0x0776: "[?]" +0x0777: "[?]" +0x0778: "[?]" +0x0779: "[?]" +0x077a: "[?]" +0x077b: "[?]" +0x077c: "[?]" +0x077d: "[?]" +0x077e: "[?]" +0x077f: "[?]" +0x0780: "h" +0x0781: "sh" +0x0782: "n" +0x0783: "r" +0x0784: "b" +0x0785: "L" +0x0786: "k" +0x0787: "'" +0x0788: "v" +0x0789: "m" +0x078a: "f" +0x078b: "dh" +0x078c: "th" +0x078d: "l" +0x078e: "g" +0x078f: "ny" +0x0790: "s" +0x0791: "d" +0x0792: "z" +0x0793: "t" +0x0794: "y" +0x0795: "p" +0x0796: "j" +0x0797: "ch" +0x0798: "tt" +0x0799: "hh" +0x079a: "kh" +0x079b: "th" +0x079c: "z" +0x079d: "sh" +0x079e: "s" +0x079f: "d" +0x07a0: "t" +0x07a1: "z" +0x07a2: "`" +0x07a3: "gh" +0x07a4: "q" +0x07a5: "w" +0x07a6: "a" +0x07a7: "aa" +0x07a8: "i" +0x07a9: "ee" +0x07aa: "u" +0x07ab: "oo" +0x07ac: "e" +0x07ad: "ey" +0x07ae: "o" +0x07af: "oa" +0x07b0: "" +0x07b1: "[?]" +0x07b2: "[?]" +0x07b3: "[?]" +0x07b4: "[?]" +0x07b5: "[?]" +0x07b6: "[?]" +0x07b7: "[?]" +0x07b8: "[?]" +0x07b9: "[?]" +0x07ba: "[?]" +0x07bb: "[?]" +0x07bc: "[?]" +0x07bd: "[?]" +0x07be: "[?]" +0x07bf: "[?]" +0x07c0: "[?]" +0x07c1: "[?]" +0x07c2: "[?]" +0x07c3: "[?]" +0x07c4: "[?]" +0x07c5: "[?]" +0x07c6: "[?]" +0x07c7: "[?]" +0x07c8: "[?]" +0x07c9: "[?]" +0x07ca: "[?]" +0x07cb: "[?]" +0x07cc: "[?]" +0x07cd: "[?]" +0x07ce: "[?]" +0x07cf: "[?]" +0x07d0: "[?]" +0x07d1: "[?]" +0x07d2: "[?]" +0x07d3: "[?]" +0x07d4: "[?]" +0x07d5: "[?]" +0x07d6: "[?]" +0x07d7: "[?]" +0x07d8: "[?]" +0x07d9: "[?]" +0x07da: "[?]" +0x07db: "[?]" +0x07dc: "[?]" +0x07dd: "[?]" +0x07de: "[?]" +0x07df: "[?]" +0x07e0: "[?]" +0x07e1: "[?]" +0x07e2: "[?]" +0x07e3: "[?]" +0x07e4: "[?]" +0x07e5: "[?]" +0x07e6: "[?]" +0x07e7: "[?]" +0x07e8: "[?]" +0x07e9: "[?]" +0x07ea: "[?]" +0x07eb: "[?]" +0x07ec: "[?]" +0x07ed: "[?]" +0x07ee: "[?]" +0x07ef: "[?]" +0x07f0: "[?]" +0x07f1: "[?]" +0x07f2: "[?]" +0x07f3: "[?]" +0x07f4: "[?]" +0x07f5: "[?]" +0x07f6: "[?]" +0x07f7: "[?]" +0x07f8: "[?]" +0x07f9: "[?]" +0x07fa: "[?]" +0x07fb: "[?]" +0x07fc: "[?]" +0x07fd: "[?]" +0x07fe: "[?]" +/* x009 */ +0x0900: "[?]" +0x0901: "N" +0x0902: "N" +0x0903: "H" +0x0904: "[?]" +0x0905: "a" +0x0906: "aa" +0x0907: "i" +0x0908: "ii" +0x0909: "u" +0x090a: "uu" +0x090b: "R" +0x090c: "L" +0x090d: "eN" +0x090e: "e" +0x090f: "e" +0x0910: "ai" +0x0911: "oN" +0x0912: "o" +0x0913: "o" +0x0914: "au" +0x0915: "k" +0x0916: "kh" +0x0917: "g" +0x0918: "gh" +0x0919: "ng" +0x091a: "c" +0x091b: "ch" +0x091c: "j" +0x091d: "jh" +0x091e: "ny" +0x091f: "tt" +0x0920: "tth" +0x0921: "dd" +0x0922: "ddh" +0x0923: "nn" +0x0924: "t" +0x0925: "th" +0x0926: "d" +0x0927: "dh" +0x0928: "n" +0x0929: "nnn" +0x092a: "p" +0x092b: "ph" +0x092c: "b" +0x092d: "bh" +0x092e: "m" +0x092f: "y" +0x0930: "r" +0x0931: "rr" +0x0932: "l" +0x0933: "l" +0x0934: "lll" +0x0935: "v" +0x0936: "sh" +0x0937: "ss" +0x0938: "s" +0x0939: "h" +0x093a: "[?]" +0x093b: "[?]" +0x093c: "'" +0x093d: "'" +0x093e: "aa" +0x093f: "i" +0x0940: "ii" +0x0941: "u" +0x0942: "uu" +0x0943: "R" +0x0944: "RR" +0x0945: "eN" +0x0946: "e" +0x0947: "e" +0x0948: "ai" +0x0949: "oN" +0x094a: "o" +0x094b: "o" +0x094c: "au" +0x094d: "" +0x094e: "[?]" +0x094f: "[?]" +0x0950: "AUM" +0x0951: "'" +0x0952: "'" +0x0953: "`" +0x0954: "'" +0x0955: "[?]" +0x0956: "[?]" +0x0957: "[?]" +0x0958: "q" +0x0959: "khh" +0x095a: "ghh" +0x095b: "z" +0x095c: "dddh" +0x095d: "rh" +0x095e: "f" +0x095f: "yy" +0x0960: "RR" +0x0961: "LL" +0x0962: "L" +0x0963: "LL" +0x0964: " / " +0x0965: " // " +0x0966: "0" +0x0967: "1" +0x0968: "2" +0x0969: "3" +0x096a: "4" +0x096b: "5" +0x096c: "6" +0x096d: "7" +0x096e: "8" +0x096f: "9" +0x0970: "." +0x0971: "[?]" +0x0972: "[?]" +0x0973: "[?]" +0x0974: "[?]" +0x0975: "[?]" +0x0976: "[?]" +0x0977: "[?]" +0x0978: "[?]" +0x0979: "[?]" +0x097a: "[?]" +0x097b: "[?]" +0x097c: "[?]" +0x097d: "[?]" +0x097e: "[?]" +0x097f: "[?]" +0x0980: "[?]" +0x0981: "N" +0x0982: "N" +0x0983: "H" +0x0984: "[?]" +0x0985: "a" +0x0986: "aa" +0x0987: "i" +0x0988: "ii" +0x0989: "u" +0x098a: "uu" +0x098b: "R" +0x098c: "RR" +0x098d: "[?]" +0x098e: "[?]" +0x098f: "e" +0x0990: "ai" +0x0991: "[?]" +0x0992: "[?]" +0x0993: "o" +0x0994: "au" +0x0995: "k" +0x0996: "kh" +0x0997: "g" +0x0998: "gh" +0x0999: "ng" +0x099a: "c" +0x099b: "ch" +0x099c: "j" +0x099d: "jh" +0x099e: "ny" +0x099f: "tt" +0x09a0: "tth" +0x09a1: "dd" +0x09a2: "ddh" +0x09a3: "nn" +0x09a4: "t" +0x09a5: "th" +0x09a6: "d" +0x09a7: "dh" +0x09a8: "n" +0x09a9: "[?]" +0x09aa: "p" +0x09ab: "ph" +0x09ac: "b" +0x09ad: "bh" +0x09ae: "m" +0x09af: "y" +0x09b0: "r" +0x09b1: "[?]" +0x09b2: "l" +0x09b3: "[?]" +0x09b4: "[?]" +0x09b5: "[?]" +0x09b6: "sh" +0x09b7: "ss" +0x09b8: "s" +0x09b9: "h" +0x09ba: "[?]" +0x09bb: "[?]" +0x09bc: "'" +0x09bd: "[?]" +0x09be: "aa" +0x09bf: "i" +0x09c0: "ii" +0x09c1: "u" +0x09c2: "uu" +0x09c3: "R" +0x09c4: "RR" +0x09c5: "[?]" +0x09c6: "[?]" +0x09c7: "e" +0x09c8: "ai" +0x09c9: "[?]" +0x09ca: "[?]" +0x09cb: "o" +0x09cc: "au" +0x09cd: "" +0x09ce: "[?]" +0x09cf: "[?]" +0x09d0: "[?]" +0x09d1: "[?]" +0x09d2: "[?]" +0x09d3: "[?]" +0x09d4: "[?]" +0x09d5: "[?]" +0x09d6: "[?]" +0x09d7: "+" +0x09d8: "[?]" +0x09d9: "[?]" +0x09da: "[?]" +0x09db: "[?]" +0x09dc: "rr" +0x09dd: "rh" +0x09de: "[?]" +0x09df: "yy" +0x09e0: "RR" +0x09e1: "LL" +0x09e2: "L" +0x09e3: "LL" +0x09e4: "[?]" +0x09e5: "[?]" +0x09e6: "0" +0x09e7: "1" +0x09e8: "2" +0x09e9: "3" +0x09ea: "4" +0x09eb: "5" +0x09ec: "6" +0x09ed: "7" +0x09ee: "8" +0x09ef: "9" +0x09f0: "r'" +0x09f1: "r`" +0x09f2: "Rs" +0x09f3: "Rs" +0x09f4: "1/" +0x09f5: "2/" +0x09f6: "3/" +0x09f7: "4/" +0x09f8: " 1 - 1/" +0x09f9: "/16" +0x09fa: "" +0x09fb: "[?]" +0x09fc: "[?]" +0x09fd: "[?]" +0x09fe: "[?]" +/* x00a */ +0x0a00: "[?]" +0x0a01: "[?]" +0x0a02: "N" +0x0a03: "[?]" +0x0a04: "[?]" +0x0a05: "a" +0x0a06: "aa" +0x0a07: "i" +0x0a08: "ii" +0x0a09: "u" +0x0a0a: "uu" +0x0a0b: "[?]" +0x0a0c: "[?]" +0x0a0d: "[?]" +0x0a0e: "[?]" +0x0a0f: "ee" +0x0a10: "ai" +0x0a11: "[?]" +0x0a12: "[?]" +0x0a13: "oo" +0x0a14: "au" +0x0a15: "k" +0x0a16: "kh" +0x0a17: "g" +0x0a18: "gh" +0x0a19: "ng" +0x0a1a: "c" +0x0a1b: "ch" +0x0a1c: "j" +0x0a1d: "jh" +0x0a1e: "ny" +0x0a1f: "tt" +0x0a20: "tth" +0x0a21: "dd" +0x0a22: "ddh" +0x0a23: "nn" +0x0a24: "t" +0x0a25: "th" +0x0a26: "d" +0x0a27: "dh" +0x0a28: "n" +0x0a29: "[?]" +0x0a2a: "p" +0x0a2b: "ph" +0x0a2c: "b" +0x0a2d: "bb" +0x0a2e: "m" +0x0a2f: "y" +0x0a30: "r" +0x0a31: "[?]" +0x0a32: "l" +0x0a33: "ll" +0x0a34: "[?]" +0x0a35: "v" +0x0a36: "sh" +0x0a37: "[?]" +0x0a38: "s" +0x0a39: "h" +0x0a3a: "[?]" +0x0a3b: "[?]" +0x0a3c: "'" +0x0a3d: "[?]" +0x0a3e: "aa" +0x0a3f: "i" +0x0a40: "ii" +0x0a41: "u" +0x0a42: "uu" +0x0a43: "[?]" +0x0a44: "[?]" +0x0a45: "[?]" +0x0a46: "[?]" +0x0a47: "ee" +0x0a48: "ai" +0x0a49: "[?]" +0x0a4a: "[?]" +0x0a4b: "oo" +0x0a4c: "au" +0x0a4d: "" +0x0a4e: "[?]" +0x0a4f: "[?]" +0x0a50: "[?]" +0x0a51: "[?]" +0x0a52: "[?]" +0x0a53: "[?]" +0x0a54: "[?]" +0x0a55: "[?]" +0x0a56: "[?]" +0x0a57: "[?]" +0x0a58: "[?]" +0x0a59: "khh" +0x0a5a: "ghh" +0x0a5b: "z" +0x0a5c: "rr" +0x0a5d: "[?]" +0x0a5e: "f" +0x0a5f: "[?]" +0x0a60: "[?]" +0x0a61: "[?]" +0x0a62: "[?]" +0x0a63: "[?]" +0x0a64: "[?]" +0x0a65: "[?]" +0x0a66: "0" +0x0a67: "1" +0x0a68: "2" +0x0a69: "3" +0x0a6a: "4" +0x0a6b: "5" +0x0a6c: "6" +0x0a6d: "7" +0x0a6e: "8" +0x0a6f: "9" +0x0a70: "N" +0x0a71: "H" +0x0a72: "" +0x0a73: "" +0x0a74: "G.E.O." +0x0a75: "[?]" +0x0a76: "[?]" +0x0a77: "[?]" +0x0a78: "[?]" +0x0a79: "[?]" +0x0a7a: "[?]" +0x0a7b: "[?]" +0x0a7c: "[?]" +0x0a7d: "[?]" +0x0a7e: "[?]" +0x0a7f: "[?]" +0x0a80: "[?]" +0x0a81: "N" +0x0a82: "N" +0x0a83: "H" +0x0a84: "[?]" +0x0a85: "a" +0x0a86: "aa" +0x0a87: "i" +0x0a88: "ii" +0x0a89: "u" +0x0a8a: "uu" +0x0a8b: "R" +0x0a8c: "[?]" +0x0a8d: "eN" +0x0a8e: "[?]" +0x0a8f: "e" +0x0a90: "ai" +0x0a91: "oN" +0x0a92: "[?]" +0x0a93: "o" +0x0a94: "au" +0x0a95: "k" +0x0a96: "kh" +0x0a97: "g" +0x0a98: "gh" +0x0a99: "ng" +0x0a9a: "c" +0x0a9b: "ch" +0x0a9c: "j" +0x0a9d: "jh" +0x0a9e: "ny" +0x0a9f: "tt" +0x0aa0: "tth" +0x0aa1: "dd" +0x0aa2: "ddh" +0x0aa3: "nn" +0x0aa4: "t" +0x0aa5: "th" +0x0aa6: "d" +0x0aa7: "dh" +0x0aa8: "n" +0x0aa9: "[?]" +0x0aaa: "p" +0x0aab: "ph" +0x0aac: "b" +0x0aad: "bh" +0x0aae: "m" +0x0aaf: "ya" +0x0ab0: "r" +0x0ab1: "[?]" +0x0ab2: "l" +0x0ab3: "ll" +0x0ab4: "[?]" +0x0ab5: "v" +0x0ab6: "sh" +0x0ab7: "ss" +0x0ab8: "s" +0x0ab9: "h" +0x0aba: "[?]" +0x0abb: "[?]" +0x0abc: "'" +0x0abd: "'" +0x0abe: "aa" +0x0abf: "i" +0x0ac0: "ii" +0x0ac1: "u" +0x0ac2: "uu" +0x0ac3: "R" +0x0ac4: "RR" +0x0ac5: "eN" +0x0ac6: "[?]" +0x0ac7: "e" +0x0ac8: "ai" +0x0ac9: "oN" +0x0aca: "[?]" +0x0acb: "o" +0x0acc: "au" +0x0acd: "" +0x0ace: "[?]" +0x0acf: "[?]" +0x0ad0: "AUM" +0x0ad1: "[?]" +0x0ad2: "[?]" +0x0ad3: "[?]" +0x0ad4: "[?]" +0x0ad5: "[?]" +0x0ad6: "[?]" +0x0ad7: "[?]" +0x0ad8: "[?]" +0x0ad9: "[?]" +0x0ada: "[?]" +0x0adb: "[?]" +0x0adc: "[?]" +0x0add: "[?]" +0x0ade: "[?]" +0x0adf: "[?]" +0x0ae0: "RR" +0x0ae1: "[?]" +0x0ae2: "[?]" +0x0ae3: "[?]" +0x0ae4: "[?]" +0x0ae5: "[?]" +0x0ae6: "0" +0x0ae7: "1" +0x0ae8: "2" +0x0ae9: "3" +0x0aea: "4" +0x0aeb: "5" +0x0aec: "6" +0x0aed: "7" +0x0aee: "8" +0x0aef: "9" +0x0af0: "[?]" +0x0af1: "[?]" +0x0af2: "[?]" +0x0af3: "[?]" +0x0af4: "[?]" +0x0af5: "[?]" +0x0af6: "[?]" +0x0af7: "[?]" +0x0af8: "[?]" +0x0af9: "[?]" +0x0afa: "[?]" +0x0afb: "[?]" +0x0afc: "[?]" +0x0afd: "[?]" +0x0afe: "[?]" +/* x00b */ +0x0b00: "[?]" +0x0b01: "N" +0x0b02: "N" +0x0b03: "H" +0x0b04: "[?]" +0x0b05: "a" +0x0b06: "aa" +0x0b07: "i" +0x0b08: "ii" +0x0b09: "u" +0x0b0a: "uu" +0x0b0b: "R" +0x0b0c: "L" +0x0b0d: "[?]" +0x0b0e: "[?]" +0x0b0f: "e" +0x0b10: "ai" +0x0b11: "[?]" +0x0b12: "[?]" +0x0b13: "o" +0x0b14: "au" +0x0b15: "k" +0x0b16: "kh" +0x0b17: "g" +0x0b18: "gh" +0x0b19: "ng" +0x0b1a: "c" +0x0b1b: "ch" +0x0b1c: "j" +0x0b1d: "jh" +0x0b1e: "ny" +0x0b1f: "tt" +0x0b20: "tth" +0x0b21: "dd" +0x0b22: "ddh" +0x0b23: "nn" +0x0b24: "t" +0x0b25: "th" +0x0b26: "d" +0x0b27: "dh" +0x0b28: "n" +0x0b29: "[?]" +0x0b2a: "p" +0x0b2b: "ph" +0x0b2c: "b" +0x0b2d: "bh" +0x0b2e: "m" +0x0b2f: "y" +0x0b30: "r" +0x0b31: "[?]" +0x0b32: "l" +0x0b33: "ll" +0x0b34: "[?]" +0x0b35: "" +0x0b36: "sh" +0x0b37: "ss" +0x0b38: "s" +0x0b39: "h" +0x0b3a: "[?]" +0x0b3b: "[?]" +0x0b3c: "'" +0x0b3d: "'" +0x0b3e: "aa" +0x0b3f: "i" +0x0b40: "ii" +0x0b41: "u" +0x0b42: "uu" +0x0b43: "R" +0x0b44: "[?]" +0x0b45: "[?]" +0x0b46: "[?]" +0x0b47: "e" +0x0b48: "ai" +0x0b49: "[?]" +0x0b4a: "[?]" +0x0b4b: "o" +0x0b4c: "au" +0x0b4d: "" +0x0b4e: "[?]" +0x0b4f: "[?]" +0x0b50: "[?]" +0x0b51: "[?]" +0x0b52: "[?]" +0x0b53: "[?]" +0x0b54: "[?]" +0x0b55: "[?]" +0x0b56: "+" +0x0b57: "+" +0x0b58: "[?]" +0x0b59: "[?]" +0x0b5a: "[?]" +0x0b5b: "[?]" +0x0b5c: "rr" +0x0b5d: "rh" +0x0b5e: "[?]" +0x0b5f: "yy" +0x0b60: "RR" +0x0b61: "LL" +0x0b62: "[?]" +0x0b63: "[?]" +0x0b64: "[?]" +0x0b65: "[?]" +0x0b66: "0" +0x0b67: "1" +0x0b68: "2" +0x0b69: "3" +0x0b6a: "4" +0x0b6b: "5" +0x0b6c: "6" +0x0b6d: "7" +0x0b6e: "8" +0x0b6f: "9" +0x0b70: "" +0x0b71: "[?]" +0x0b72: "[?]" +0x0b73: "[?]" +0x0b74: "[?]" +0x0b75: "[?]" +0x0b76: "[?]" +0x0b77: "[?]" +0x0b78: "[?]" +0x0b79: "[?]" +0x0b7a: "[?]" +0x0b7b: "[?]" +0x0b7c: "[?]" +0x0b7d: "[?]" +0x0b7e: "[?]" +0x0b7f: "[?]" +0x0b80: "[?]" +0x0b81: "[?]" +0x0b82: "N" +0x0b83: "H" +0x0b84: "[?]" +0x0b85: "a" +0x0b86: "aa" +0x0b87: "i" +0x0b88: "ii" +0x0b89: "u" +0x0b8a: "uu" +0x0b8b: "[?]" +0x0b8c: "[?]" +0x0b8d: "[?]" +0x0b8e: "e" +0x0b8f: "ee" +0x0b90: "ai" +0x0b91: "[?]" +0x0b92: "o" +0x0b93: "oo" +0x0b94: "au" +0x0b95: "k" +0x0b96: "[?]" +0x0b97: "[?]" +0x0b98: "[?]" +0x0b99: "ng" +0x0b9a: "c" +0x0b9b: "[?]" +0x0b9c: "j" +0x0b9d: "[?]" +0x0b9e: "ny" +0x0b9f: "tt" +0x0ba0: "[?]" +0x0ba1: "[?]" +0x0ba2: "[?]" +0x0ba3: "nn" +0x0ba4: "t" +0x0ba5: "[?]" +0x0ba6: "[?]" +0x0ba7: "[?]" +0x0ba8: "n" +0x0ba9: "nnn" +0x0baa: "p" +0x0bab: "[?]" +0x0bac: "[?]" +0x0bad: "[?]" +0x0bae: "m" +0x0baf: "y" +0x0bb0: "r" +0x0bb1: "rr" +0x0bb2: "l" +0x0bb3: "ll" +0x0bb4: "lll" +0x0bb5: "v" +0x0bb6: "[?]" +0x0bb7: "ss" +0x0bb8: "s" +0x0bb9: "h" +0x0bba: "[?]" +0x0bbb: "[?]" +0x0bbc: "[?]" +0x0bbd: "[?]" +0x0bbe: "aa" +0x0bbf: "i" +0x0bc0: "ii" +0x0bc1: "u" +0x0bc2: "uu" +0x0bc3: "[?]" +0x0bc4: "[?]" +0x0bc5: "[?]" +0x0bc6: "e" +0x0bc7: "ee" +0x0bc8: "ai" +0x0bc9: "[?]" +0x0bca: "o" +0x0bcb: "oo" +0x0bcc: "au" +0x0bcd: "" +0x0bce: "[?]" +0x0bcf: "[?]" +0x0bd0: "[?]" +0x0bd1: "[?]" +0x0bd2: "[?]" +0x0bd3: "[?]" +0x0bd4: "[?]" +0x0bd5: "[?]" +0x0bd6: "[?]" +0x0bd7: "+" +0x0bd8: "[?]" +0x0bd9: "[?]" +0x0bda: "[?]" +0x0bdb: "[?]" +0x0bdc: "[?]" +0x0bdd: "[?]" +0x0bde: "[?]" +0x0bdf: "[?]" +0x0be0: "[?]" +0x0be1: "[?]" +0x0be2: "[?]" +0x0be3: "[?]" +0x0be4: "[?]" +0x0be5: "[?]" +0x0be6: "0" +0x0be7: "1" +0x0be8: "2" +0x0be9: "3" +0x0bea: "4" +0x0beb: "5" +0x0bec: "6" +0x0bed: "7" +0x0bee: "8" +0x0bef: "9" +0x0bf0: "+10+" +0x0bf1: "+100+" +0x0bf2: "+1000+" +0x0bf3: "[?]" +0x0bf4: "[?]" +0x0bf5: "[?]" +0x0bf6: "[?]" +0x0bf7: "[?]" +0x0bf8: "[?]" +0x0bf9: "[?]" +0x0bfa: "[?]" +0x0bfb: "[?]" +0x0bfc: "[?]" +0x0bfd: "[?]" +0x0bfe: "[?]" +/* x00c */ +0x0c00: "[?]" +0x0c01: "N" +0x0c02: "N" +0x0c03: "H" +0x0c04: "[?]" +0x0c05: "a" +0x0c06: "aa" +0x0c07: "i" +0x0c08: "ii" +0x0c09: "u" +0x0c0a: "uu" +0x0c0b: "R" +0x0c0c: "L" +0x0c0d: "[?]" +0x0c0e: "e" +0x0c0f: "ee" +0x0c10: "ai" +0x0c11: "[?]" +0x0c12: "o" +0x0c13: "oo" +0x0c14: "au" +0x0c15: "k" +0x0c16: "kh" +0x0c17: "g" +0x0c18: "gh" +0x0c19: "ng" +0x0c1a: "c" +0x0c1b: "ch" +0x0c1c: "j" +0x0c1d: "jh" +0x0c1e: "ny" +0x0c1f: "tt" +0x0c20: "tth" +0x0c21: "dd" +0x0c22: "ddh" +0x0c23: "nn" +0x0c24: "t" +0x0c25: "th" +0x0c26: "d" +0x0c27: "dh" +0x0c28: "n" +0x0c29: "[?]" +0x0c2a: "p" +0x0c2b: "ph" +0x0c2c: "b" +0x0c2d: "bh" +0x0c2e: "m" +0x0c2f: "y" +0x0c30: "r" +0x0c31: "rr" +0x0c32: "l" +0x0c33: "ll" +0x0c34: "[?]" +0x0c35: "v" +0x0c36: "sh" +0x0c37: "ss" +0x0c38: "s" +0x0c39: "h" +0x0c3a: "[?]" +0x0c3b: "[?]" +0x0c3c: "[?]" +0x0c3d: "[?]" +0x0c3e: "aa" +0x0c3f: "i" +0x0c40: "ii" +0x0c41: "u" +0x0c42: "uu" +0x0c43: "R" +0x0c44: "RR" +0x0c45: "[?]" +0x0c46: "e" +0x0c47: "ee" +0x0c48: "ai" +0x0c49: "[?]" +0x0c4a: "o" +0x0c4b: "oo" +0x0c4c: "au" +0x0c4d: "" +0x0c4e: "[?]" +0x0c4f: "[?]" +0x0c50: "[?]" +0x0c51: "[?]" +0x0c52: "[?]" +0x0c53: "[?]" +0x0c54: "[?]" +0x0c55: "+" +0x0c56: "+" +0x0c57: "[?]" +0x0c58: "[?]" +0x0c59: "[?]" +0x0c5a: "[?]" +0x0c5b: "[?]" +0x0c5c: "[?]" +0x0c5d: "[?]" +0x0c5e: "[?]" +0x0c5f: "[?]" +0x0c60: "RR" +0x0c61: "LL" +0x0c62: "[?]" +0x0c63: "[?]" +0x0c64: "[?]" +0x0c65: "[?]" +0x0c66: "0" +0x0c67: "1" +0x0c68: "2" +0x0c69: "3" +0x0c6a: "4" +0x0c6b: "5" +0x0c6c: "6" +0x0c6d: "7" +0x0c6e: "8" +0x0c6f: "9" +0x0c70: "[?]" +0x0c71: "[?]" +0x0c72: "[?]" +0x0c73: "[?]" +0x0c74: "[?]" +0x0c75: "[?]" +0x0c76: "[?]" +0x0c77: "[?]" +0x0c78: "[?]" +0x0c79: "[?]" +0x0c7a: "[?]" +0x0c7b: "[?]" +0x0c7c: "[?]" +0x0c7d: "[?]" +0x0c7e: "[?]" +0x0c7f: "[?]" +0x0c80: "[?]" +0x0c81: "[?]" +0x0c82: "N" +0x0c83: "H" +0x0c84: "[?]" +0x0c85: "a" +0x0c86: "aa" +0x0c87: "i" +0x0c88: "ii" +0x0c89: "u" +0x0c8a: "uu" +0x0c8b: "R" +0x0c8c: "L" +0x0c8d: "[?]" +0x0c8e: "e" +0x0c8f: "ee" +0x0c90: "ai" +0x0c91: "[?]" +0x0c92: "o" +0x0c93: "oo" +0x0c94: "au" +0x0c95: "k" +0x0c96: "kh" +0x0c97: "g" +0x0c98: "gh" +0x0c99: "ng" +0x0c9a: "c" +0x0c9b: "ch" +0x0c9c: "j" +0x0c9d: "jh" +0x0c9e: "ny" +0x0c9f: "tt" +0x0ca0: "tth" +0x0ca1: "dd" +0x0ca2: "ddh" +0x0ca3: "nn" +0x0ca4: "t" +0x0ca5: "th" +0x0ca6: "d" +0x0ca7: "dh" +0x0ca8: "n" +0x0ca9: "[?]" +0x0caa: "p" +0x0cab: "ph" +0x0cac: "b" +0x0cad: "bh" +0x0cae: "m" +0x0caf: "y" +0x0cb0: "r" +0x0cb1: "rr" +0x0cb2: "l" +0x0cb3: "ll" +0x0cb4: "[?]" +0x0cb5: "v" +0x0cb6: "sh" +0x0cb7: "ss" +0x0cb8: "s" +0x0cb9: "h" +0x0cba: "[?]" +0x0cbb: "[?]" +0x0cbc: "[?]" +0x0cbd: "[?]" +0x0cbe: "aa" +0x0cbf: "i" +0x0cc0: "ii" +0x0cc1: "u" +0x0cc2: "uu" +0x0cc3: "R" +0x0cc4: "RR" +0x0cc5: "[?]" +0x0cc6: "e" +0x0cc7: "ee" +0x0cc8: "ai" +0x0cc9: "[?]" +0x0cca: "o" +0x0ccb: "oo" +0x0ccc: "au" +0x0ccd: "" +0x0cce: "[?]" +0x0ccf: "[?]" +0x0cd0: "[?]" +0x0cd1: "[?]" +0x0cd2: "[?]" +0x0cd3: "[?]" +0x0cd4: "[?]" +0x0cd5: "+" +0x0cd6: "+" +0x0cd7: "[?]" +0x0cd8: "[?]" +0x0cd9: "[?]" +0x0cda: "[?]" +0x0cdb: "[?]" +0x0cdc: "[?]" +0x0cdd: "[?]" +0x0cde: "lll" +0x0cdf: "[?]" +0x0ce0: "RR" +0x0ce1: "LL" +0x0ce2: "[?]" +0x0ce3: "[?]" +0x0ce4: "[?]" +0x0ce5: "[?]" +0x0ce6: "0" +0x0ce7: "1" +0x0ce8: "2" +0x0ce9: "3" +0x0cea: "4" +0x0ceb: "5" +0x0cec: "6" +0x0ced: "7" +0x0cee: "8" +0x0cef: "9" +0x0cf0: "[?]" +0x0cf1: "[?]" +0x0cf2: "[?]" +0x0cf3: "[?]" +0x0cf4: "[?]" +0x0cf5: "[?]" +0x0cf6: "[?]" +0x0cf7: "[?]" +0x0cf8: "[?]" +0x0cf9: "[?]" +0x0cfa: "[?]" +0x0cfb: "[?]" +0x0cfc: "[?]" +0x0cfd: "[?]" +0x0cfe: "[?]" +/* x00d */ +0x0d00: "[?]" +0x0d01: "[?]" +0x0d02: "N" +0x0d03: "H" +0x0d04: "[?]" +0x0d05: "a" +0x0d06: "aa" +0x0d07: "i" +0x0d08: "ii" +0x0d09: "u" +0x0d0a: "uu" +0x0d0b: "R" +0x0d0c: "L" +0x0d0d: "[?]" +0x0d0e: "e" +0x0d0f: "ee" +0x0d10: "ai" +0x0d11: "[?]" +0x0d12: "o" +0x0d13: "oo" +0x0d14: "au" +0x0d15: "k" +0x0d16: "kh" +0x0d17: "g" +0x0d18: "gh" +0x0d19: "ng" +0x0d1a: "c" +0x0d1b: "ch" +0x0d1c: "j" +0x0d1d: "jh" +0x0d1e: "ny" +0x0d1f: "tt" +0x0d20: "tth" +0x0d21: "dd" +0x0d22: "ddh" +0x0d23: "nn" +0x0d24: "t" +0x0d25: "th" +0x0d26: "d" +0x0d27: "dh" +0x0d28: "n" +0x0d29: "[?]" +0x0d2a: "p" +0x0d2b: "ph" +0x0d2c: "b" +0x0d2d: "bh" +0x0d2e: "m" +0x0d2f: "y" +0x0d30: "r" +0x0d31: "rr" +0x0d32: "l" +0x0d33: "ll" +0x0d34: "lll" +0x0d35: "v" +0x0d36: "sh" +0x0d37: "ss" +0x0d38: "s" +0x0d39: "h" +0x0d3a: "[?]" +0x0d3b: "[?]" +0x0d3c: "[?]" +0x0d3d: "[?]" +0x0d3e: "aa" +0x0d3f: "i" +0x0d40: "ii" +0x0d41: "u" +0x0d42: "uu" +0x0d43: "R" +0x0d44: "[?]" +0x0d45: "[?]" +0x0d46: "e" +0x0d47: "ee" +0x0d48: "ai" +0x0d49: "" +0x0d4a: "o" +0x0d4b: "oo" +0x0d4c: "au" +0x0d4d: "" +0x0d4e: "[?]" +0x0d4f: "[?]" +0x0d50: "[?]" +0x0d51: "[?]" +0x0d52: "[?]" +0x0d53: "[?]" +0x0d54: "[?]" +0x0d55: "[?]" +0x0d56: "[?]" +0x0d57: "+" +0x0d58: "[?]" +0x0d59: "[?]" +0x0d5a: "[?]" +0x0d5b: "[?]" +0x0d5c: "[?]" +0x0d5d: "[?]" +0x0d5e: "[?]" +0x0d5f: "[?]" +0x0d60: "RR" +0x0d61: "LL" +0x0d62: "[?]" +0x0d63: "[?]" +0x0d64: "[?]" +0x0d65: "[?]" +0x0d66: "0" +0x0d67: "1" +0x0d68: "2" +0x0d69: "3" +0x0d6a: "4" +0x0d6b: "5" +0x0d6c: "6" +0x0d6d: "7" +0x0d6e: "8" +0x0d6f: "9" +0x0d70: "[?]" +0x0d71: "[?]" +0x0d72: "[?]" +0x0d73: "[?]" +0x0d74: "[?]" +0x0d75: "[?]" +0x0d76: "[?]" +0x0d77: "[?]" +0x0d78: "[?]" +0x0d79: "[?]" +0x0d7a: "[?]" +0x0d7b: "[?]" +0x0d7c: "[?]" +0x0d7d: "[?]" +0x0d7e: "[?]" +0x0d7f: "[?]" +0x0d80: "[?]" +0x0d81: "[?]" +0x0d82: "N" +0x0d83: "H" +0x0d84: "[?]" +0x0d85: "a" +0x0d86: "aa" +0x0d87: "ae" +0x0d88: "aae" +0x0d89: "i" +0x0d8a: "ii" +0x0d8b: "u" +0x0d8c: "uu" +0x0d8d: "R" +0x0d8e: "RR" +0x0d8f: "L" +0x0d90: "LL" +0x0d91: "e" +0x0d92: "ee" +0x0d93: "ai" +0x0d94: "o" +0x0d95: "oo" +0x0d96: "au" +0x0d97: "[?]" +0x0d98: "[?]" +0x0d99: "[?]" +0x0d9a: "k" +0x0d9b: "kh" +0x0d9c: "g" +0x0d9d: "gh" +0x0d9e: "ng" +0x0d9f: "nng" +0x0da0: "c" +0x0da1: "ch" +0x0da2: "j" +0x0da3: "jh" +0x0da4: "ny" +0x0da5: "jny" +0x0da6: "nyj" +0x0da7: "tt" +0x0da8: "tth" +0x0da9: "dd" +0x0daa: "ddh" +0x0dab: "nn" +0x0dac: "nndd" +0x0dad: "t" +0x0dae: "th" +0x0daf: "d" +0x0db0: "dh" +0x0db1: "n" +0x0db2: "[?]" +0x0db3: "nd" +0x0db4: "p" +0x0db5: "ph" +0x0db6: "b" +0x0db7: "bh" +0x0db8: "m" +0x0db9: "mb" +0x0dba: "y" +0x0dbb: "r" +0x0dbc: "[?]" +0x0dbd: "l" +0x0dbe: "[?]" +0x0dbf: "[?]" +0x0dc0: "v" +0x0dc1: "sh" +0x0dc2: "ss" +0x0dc3: "s" +0x0dc4: "h" +0x0dc5: "ll" +0x0dc6: "f" +0x0dc7: "[?]" +0x0dc8: "[?]" +0x0dc9: "[?]" +0x0dca: "" +0x0dcb: "[?]" +0x0dcc: "[?]" +0x0dcd: "[?]" +0x0dce: "[?]" +0x0dcf: "aa" +0x0dd0: "ae" +0x0dd1: "aae" +0x0dd2: "i" +0x0dd3: "ii" +0x0dd4: "u" +0x0dd5: "[?]" +0x0dd6: "uu" +0x0dd7: "[?]" +0x0dd8: "R" +0x0dd9: "e" +0x0dda: "ee" +0x0ddb: "ai" +0x0ddc: "o" +0x0ddd: "oo" +0x0dde: "au" +0x0ddf: "L" +0x0de0: "[?]" +0x0de1: "[?]" +0x0de2: "[?]" +0x0de3: "[?]" +0x0de4: "[?]" +0x0de5: "[?]" +0x0de6: "[?]" +0x0de7: "[?]" +0x0de8: "[?]" +0x0de9: "[?]" +0x0dea: "[?]" +0x0deb: "[?]" +0x0dec: "[?]" +0x0ded: "[?]" +0x0dee: "[?]" +0x0def: "[?]" +0x0df0: "[?]" +0x0df1: "[?]" +0x0df2: "RR" +0x0df3: "LL" +0x0df4: " . " +0x0df5: "[?]" +0x0df6: "[?]" +0x0df7: "[?]" +0x0df8: "[?]" +0x0df9: "[?]" +0x0dfa: "[?]" +0x0dfb: "[?]" +0x0dfc: "[?]" +0x0dfd: "[?]" +0x0dfe: "[?]" +/* x00e */ +0x0e00: "[?]" +0x0e01: "k" +0x0e02: "kh" +0x0e03: "kh" +0x0e04: "kh" +0x0e05: "kh" +0x0e06: "kh" +0x0e07: "ng" +0x0e08: "cch" +0x0e09: "ch" +0x0e0a: "ch" +0x0e0b: "ch" +0x0e0c: "ch" +0x0e0d: "y" +0x0e0e: "d" +0x0e0f: "t" +0x0e10: "th" +0x0e11: "th" +0x0e12: "th" +0x0e13: "n" +0x0e14: "d" +0x0e15: "t" +0x0e16: "th" +0x0e17: "th" +0x0e18: "th" +0x0e19: "n" +0x0e1a: "b" +0x0e1b: "p" +0x0e1c: "ph" +0x0e1d: "f" +0x0e1e: "ph" +0x0e1f: "f" +0x0e20: "ph" +0x0e21: "m" +0x0e22: "y" +0x0e23: "r" +0x0e24: "R" +0x0e25: "l" +0x0e26: "L" +0x0e27: "w" +0x0e28: "s" +0x0e29: "s" +0x0e2a: "s" +0x0e2b: "h" +0x0e2c: "l" +0x0e2d: "`" +0x0e2e: "h" +0x0e2f: "~" +0x0e30: "a" +0x0e31: "a" +0x0e32: "aa" +0x0e33: "am" +0x0e34: "i" +0x0e35: "ii" +0x0e36: "ue" +0x0e37: "uue" +0x0e38: "u" +0x0e39: "uu" +0x0e3a: "'" +0x0e3b: "[?]" +0x0e3c: "[?]" +0x0e3d: "[?]" +0x0e3e: "[?]" +0x0e3f: "Bh." +0x0e40: "e" +0x0e41: "ae" +0x0e42: "o" +0x0e43: "ai" +0x0e44: "ai" +0x0e45: "ao" +0x0e46: "+" +0x0e47: "" +0x0e48: "" +0x0e49: "" +0x0e4a: "" +0x0e4b: "" +0x0e4c: "" +0x0e4d: "M" +0x0e4e: "" +0x0e4f: " * " +0x0e50: "0" +0x0e51: "1" +0x0e52: "2" +0x0e53: "3" +0x0e54: "4" +0x0e55: "5" +0x0e56: "6" +0x0e57: "7" +0x0e58: "8" +0x0e59: "9" +0x0e5a: " // " +0x0e5b: " /// " +0x0e5c: "[?]" +0x0e5d: "[?]" +0x0e5e: "[?]" +0x0e5f: "[?]" +0x0e60: "[?]" +0x0e61: "[?]" +0x0e62: "[?]" +0x0e63: "[?]" +0x0e64: "[?]" +0x0e65: "[?]" +0x0e66: "[?]" +0x0e67: "[?]" +0x0e68: "[?]" +0x0e69: "[?]" +0x0e6a: "[?]" +0x0e6b: "[?]" +0x0e6c: "[?]" +0x0e6d: "[?]" +0x0e6e: "[?]" +0x0e6f: "[?]" +0x0e70: "[?]" +0x0e71: "[?]" +0x0e72: "[?]" +0x0e73: "[?]" +0x0e74: "[?]" +0x0e75: "[?]" +0x0e76: "[?]" +0x0e77: "[?]" +0x0e78: "[?]" +0x0e79: "[?]" +0x0e7a: "[?]" +0x0e7b: "[?]" +0x0e7c: "[?]" +0x0e7d: "[?]" +0x0e7e: "[?]" +0x0e7f: "[?]" +0x0e80: "[?]" +0x0e81: "k" +0x0e82: "kh" +0x0e83: "[?]" +0x0e84: "kh" +0x0e85: "[?]" +0x0e86: "[?]" +0x0e87: "ng" +0x0e88: "ch" +0x0e89: "[?]" +0x0e8a: "s" +0x0e8b: "[?]" +0x0e8c: "[?]" +0x0e8d: "ny" +0x0e8e: "[?]" +0x0e8f: "[?]" +0x0e90: "[?]" +0x0e91: "[?]" +0x0e92: "[?]" +0x0e93: "[?]" +0x0e94: "d" +0x0e95: "h" +0x0e96: "th" +0x0e97: "th" +0x0e98: "[?]" +0x0e99: "n" +0x0e9a: "b" +0x0e9b: "p" +0x0e9c: "ph" +0x0e9d: "f" +0x0e9e: "ph" +0x0e9f: "f" +0x0ea0: "[?]" +0x0ea1: "m" +0x0ea2: "y" +0x0ea3: "r" +0x0ea4: "[?]" +0x0ea5: "l" +0x0ea6: "[?]" +0x0ea7: "w" +0x0ea8: "[?]" +0x0ea9: "[?]" +0x0eaa: "s" +0x0eab: "h" +0x0eac: "[?]" +0x0ead: "`" +0x0eae: "" +0x0eaf: "~" +0x0eb0: "a" +0x0eb1: "" +0x0eb2: "aa" +0x0eb3: "am" +0x0eb4: "i" +0x0eb5: "ii" +0x0eb6: "y" +0x0eb7: "yy" +0x0eb8: "u" +0x0eb9: "uu" +0x0eba: "[?]" +0x0ebb: "o" +0x0ebc: "l" +0x0ebd: "ny" +0x0ebe: "[?]" +0x0ebf: "[?]" +0x0ec0: "e" +0x0ec1: "ei" +0x0ec2: "o" +0x0ec3: "ay" +0x0ec4: "ai" +0x0ec5: "[?]" +0x0ec6: "+" +0x0ec7: "[?]" +0x0ec8: "" +0x0ec9: "" +0x0eca: "" +0x0ecb: "" +0x0ecc: "" +0x0ecd: "M" +0x0ece: "[?]" +0x0ecf: "[?]" +0x0ed0: "0" +0x0ed1: "1" +0x0ed2: "2" +0x0ed3: "3" +0x0ed4: "4" +0x0ed5: "5" +0x0ed6: "6" +0x0ed7: "7" +0x0ed8: "8" +0x0ed9: "9" +0x0eda: "[?]" +0x0edb: "[?]" +0x0edc: "hn" +0x0edd: "hm" +0x0ede: "[?]" +0x0edf: "[?]" +0x0ee0: "[?]" +0x0ee1: "[?]" +0x0ee2: "[?]" +0x0ee3: "[?]" +0x0ee4: "[?]" +0x0ee5: "[?]" +0x0ee6: "[?]" +0x0ee7: "[?]" +0x0ee8: "[?]" +0x0ee9: "[?]" +0x0eea: "[?]" +0x0eeb: "[?]" +0x0eec: "[?]" +0x0eed: "[?]" +0x0eee: "[?]" +0x0eef: "[?]" +0x0ef0: "[?]" +0x0ef1: "[?]" +0x0ef2: "[?]" +0x0ef3: "[?]" +0x0ef4: "[?]" +0x0ef5: "[?]" +0x0ef6: "[?]" +0x0ef7: "[?]" +0x0ef8: "[?]" +0x0ef9: "[?]" +0x0efa: "[?]" +0x0efb: "[?]" +0x0efc: "[?]" +0x0efd: "[?]" +0x0efe: "[?]" +/* x00f */ +0x0f00: "AUM" +0x0f01: "" +0x0f02: "" +0x0f03: "" +0x0f04: "" +0x0f05: "" +0x0f06: "" +0x0f07: "" +0x0f08: " // " +0x0f09: " * " +0x0f0a: "" +0x0f0b: "-" +0x0f0c: " / " +0x0f0d: " / " +0x0f0e: " // " +0x0f0f: " -/ " +0x0f10: " +/ " +0x0f11: " X/ " +0x0f12: " /XX/ " +0x0f13: " /X/ " +0x0f14: ", " +0x0f15: "" +0x0f16: "" +0x0f17: "" +0x0f18: "" +0x0f19: "" +0x0f1a: "" +0x0f1b: "" +0x0f1c: "" +0x0f1d: "" +0x0f1e: "" +0x0f1f: "" +0x0f20: "0" +0x0f21: "1" +0x0f22: "2" +0x0f23: "3" +0x0f24: "4" +0x0f25: "5" +0x0f26: "6" +0x0f27: "7" +0x0f28: "8" +0x0f29: "9" +0x0f2a: ".5" +0x0f2b: "1.5" +0x0f2c: "2.5" +0x0f2d: "3.5" +0x0f2e: "4.5" +0x0f2f: "5.5" +0x0f30: "6.5" +0x0f31: "7.5" +0x0f32: "8.5" +0x0f33: "-.5" +0x0f34: "+" +0x0f35: "*" +0x0f36: "^" +0x0f37: "_" +0x0f38: "" +0x0f39: "~" +0x0f3a: "[?]" +0x0f3b: "]" +0x0f3c: "[[" +0x0f3d: "]]" +0x0f3e: "" +0x0f3f: "" +0x0f40: "k" +0x0f41: "kh" +0x0f42: "g" +0x0f43: "gh" +0x0f44: "ng" +0x0f45: "c" +0x0f46: "ch" +0x0f47: "j" +0x0f48: "[?]" +0x0f49: "ny" +0x0f4a: "tt" +0x0f4b: "tth" +0x0f4c: "dd" +0x0f4d: "ddh" +0x0f4e: "nn" +0x0f4f: "t" +0x0f50: "th" +0x0f51: "d" +0x0f52: "dh" +0x0f53: "n" +0x0f54: "p" +0x0f55: "ph" +0x0f56: "b" +0x0f57: "bh" +0x0f58: "m" +0x0f59: "ts" +0x0f5a: "tsh" +0x0f5b: "dz" +0x0f5c: "dzh" +0x0f5d: "w" +0x0f5e: "zh" +0x0f5f: "z" +0x0f60: "'" +0x0f61: "y" +0x0f62: "r" +0x0f63: "l" +0x0f64: "sh" +0x0f65: "ssh" +0x0f66: "s" +0x0f67: "h" +0x0f68: "a" +0x0f69: "kss" +0x0f6a: "r" +0x0f6b: "[?]" +0x0f6c: "[?]" +0x0f6d: "[?]" +0x0f6e: "[?]" +0x0f6f: "[?]" +0x0f70: "[?]" +0x0f71: "aa" +0x0f72: "i" +0x0f73: "ii" +0x0f74: "u" +0x0f75: "uu" +0x0f76: "R" +0x0f77: "RR" +0x0f78: "L" +0x0f79: "LL" +0x0f7a: "e" +0x0f7b: "ee" +0x0f7c: "o" +0x0f7d: "oo" +0x0f7e: "M" +0x0f7f: "H" +0x0f80: "i" +0x0f81: "ii" +0x0f82: "" +0x0f83: "" +0x0f84: "" +0x0f85: "" +0x0f86: "" +0x0f87: "" +0x0f88: "" +0x0f89: "" +0x0f8a: "" +0x0f8b: "" +0x0f8c: "[?]" +0x0f8d: "[?]" +0x0f8e: "[?]" +0x0f8f: "[?]" +0x0f90: "k" +0x0f91: "kh" +0x0f92: "g" +0x0f93: "gh" +0x0f94: "ng" +0x0f95: "c" +0x0f96: "ch" +0x0f97: "j" +0x0f98: "[?]" +0x0f99: "ny" +0x0f9a: "tt" +0x0f9b: "tth" +0x0f9c: "dd" +0x0f9d: "ddh" +0x0f9e: "nn" +0x0f9f: "t" +0x0fa0: "th" +0x0fa1: "d" +0x0fa2: "dh" +0x0fa3: "n" +0x0fa4: "p" +0x0fa5: "ph" +0x0fa6: "b" +0x0fa7: "bh" +0x0fa8: "m" +0x0fa9: "ts" +0x0faa: "tsh" +0x0fab: "dz" +0x0fac: "dzh" +0x0fad: "w" +0x0fae: "zh" +0x0faf: "z" +0x0fb0: "'" +0x0fb1: "y" +0x0fb2: "r" +0x0fb3: "l" +0x0fb4: "sh" +0x0fb5: "ss" +0x0fb6: "s" +0x0fb7: "h" +0x0fb8: "a" +0x0fb9: "kss" +0x0fba: "w" +0x0fbb: "y" +0x0fbc: "r" +0x0fbd: "[?]" +0x0fbe: "X" +0x0fbf: " :X: " +0x0fc0: " /O/ " +0x0fc1: " /o/ " +0x0fc2: " \\o\\ " +0x0fc3: " (O) " +0x0fc4: "" +0x0fc5: "" +0x0fc6: "" +0x0fc7: "" +0x0fc8: "" +0x0fc9: "" +0x0fca: "" +0x0fcb: "" +0x0fcc: "" +0x0fcd: "[?]" +0x0fce: "[?]" +0x0fcf: "" +0x0fd0: "[?]" +0x0fd1: "[?]" +0x0fd2: "[?]" +0x0fd3: "[?]" +0x0fd4: "[?]" +0x0fd5: "[?]" +0x0fd6: "[?]" +0x0fd7: "[?]" +0x0fd8: "[?]" +0x0fd9: "[?]" +0x0fda: "[?]" +0x0fdb: "[?]" +0x0fdc: "[?]" +0x0fdd: "[?]" +0x0fde: "[?]" +0x0fdf: "[?]" +0x0fe0: "[?]" +0x0fe1: "[?]" +0x0fe2: "[?]" +0x0fe3: "[?]" +0x0fe4: "[?]" +0x0fe5: "[?]" +0x0fe6: "[?]" +0x0fe7: "[?]" +0x0fe8: "[?]" +0x0fe9: "[?]" +0x0fea: "[?]" +0x0feb: "[?]" +0x0fec: "[?]" +0x0fed: "[?]" +0x0fee: "[?]" +0x0fef: "[?]" +0x0ff0: "[?]" +0x0ff1: "[?]" +0x0ff2: "[?]" +0x0ff3: "[?]" +0x0ff4: "[?]" +0x0ff5: "[?]" +0x0ff6: "[?]" +0x0ff7: "[?]" +0x0ff8: "[?]" +0x0ff9: "[?]" +0x0ffa: "[?]" +0x0ffb: "[?]" +0x0ffc: "[?]" +0x0ffd: "[?]" +0x0ffe: "[?]" +/* x010 */ +0x1000: "k" +0x1001: "kh" +0x1002: "g" +0x1003: "gh" +0x1004: "ng" +0x1005: "c" +0x1006: "ch" +0x1007: "j" +0x1008: "jh" +0x1009: "ny" +0x100a: "nny" +0x100b: "tt" +0x100c: "tth" +0x100d: "dd" +0x100e: "ddh" +0x100f: "nn" +0x1010: "tt" +0x1011: "th" +0x1012: "d" +0x1013: "dh" +0x1014: "n" +0x1015: "p" +0x1016: "ph" +0x1017: "b" +0x1018: "bh" +0x1019: "m" +0x101a: "y" +0x101b: "r" +0x101c: "l" +0x101d: "w" +0x101e: "s" +0x101f: "h" +0x1020: "ll" +0x1021: "a" +0x1022: "[?]" +0x1023: "i" +0x1024: "ii" +0x1025: "u" +0x1026: "uu" +0x1027: "e" +0x1028: "[?]" +0x1029: "o" +0x102a: "au" +0x102b: "[?]" +0x102c: "aa" +0x102d: "i" +0x102e: "ii" +0x102f: "u" +0x1030: "uu" +0x1031: "e" +0x1032: "ai" +0x1033: "[?]" +0x1034: "[?]" +0x1035: "[?]" +0x1036: "N" +0x1037: "'" +0x1038: ":" +0x1039: "" +0x103a: "[?]" +0x103b: "[?]" +0x103c: "[?]" +0x103d: "[?]" +0x103e: "[?]" +0x103f: "[?]" +0x1040: "0" +0x1041: "1" +0x1042: "2" +0x1043: "3" +0x1044: "4" +0x1045: "5" +0x1046: "6" +0x1047: "7" +0x1048: "8" +0x1049: "9" +0x104a: " / " +0x104b: " // " +0x104c: "n*" +0x104d: "r*" +0x104e: "l*" +0x104f: "e*" +0x1050: "sh" +0x1051: "ss" +0x1052: "R" +0x1053: "RR" +0x1054: "L" +0x1055: "LL" +0x1056: "R" +0x1057: "RR" +0x1058: "L" +0x1059: "LL" +0x105a: "[?]" +0x105b: "[?]" +0x105c: "[?]" +0x105d: "[?]" +0x105e: "[?]" +0x105f: "[?]" +0x1060: "[?]" +0x1061: "[?]" +0x1062: "[?]" +0x1063: "[?]" +0x1064: "[?]" +0x1065: "[?]" +0x1066: "[?]" +0x1067: "[?]" +0x1068: "[?]" +0x1069: "[?]" +0x106a: "[?]" +0x106b: "[?]" +0x106c: "[?]" +0x106d: "[?]" +0x106e: "[?]" +0x106f: "[?]" +0x1070: "[?]" +0x1071: "[?]" +0x1072: "[?]" +0x1073: "[?]" +0x1074: "[?]" +0x1075: "[?]" +0x1076: "[?]" +0x1077: "[?]" +0x1078: "[?]" +0x1079: "[?]" +0x107a: "[?]" +0x107b: "[?]" +0x107c: "[?]" +0x107d: "[?]" +0x107e: "[?]" +0x107f: "[?]" +0x1080: "[?]" +0x1081: "[?]" +0x1082: "[?]" +0x1083: "[?]" +0x1084: "[?]" +0x1085: "[?]" +0x1086: "[?]" +0x1087: "[?]" +0x1088: "[?]" +0x1089: "[?]" +0x108a: "[?]" +0x108b: "[?]" +0x108c: "[?]" +0x108d: "[?]" +0x108e: "[?]" +0x108f: "[?]" +0x1090: "[?]" +0x1091: "[?]" +0x1092: "[?]" +0x1093: "[?]" +0x1094: "[?]" +0x1095: "[?]" +0x1096: "[?]" +0x1097: "[?]" +0x1098: "[?]" +0x1099: "[?]" +0x109a: "[?]" +0x109b: "[?]" +0x109c: "[?]" +0x109d: "[?]" +0x109e: "[?]" +0x109f: "[?]" +0x10a0: "A" +0x10a1: "B" +0x10a2: "G" +0x10a3: "D" +0x10a4: "E" +0x10a5: "V" +0x10a6: "Z" +0x10a7: "T`" +0x10a8: "I" +0x10a9: "K" +0x10aa: "L" +0x10ab: "M" +0x10ac: "N" +0x10ad: "O" +0x10ae: "P" +0x10af: "Zh" +0x10b0: "R" +0x10b1: "S" +0x10b2: "T" +0x10b3: "U" +0x10b4: "P`" +0x10b5: "K`" +0x10b6: "G'" +0x10b7: "Q" +0x10b8: "Sh" +0x10b9: "Ch`" +0x10ba: "C`" +0x10bb: "Z'" +0x10bc: "C" +0x10bd: "Ch" +0x10be: "X" +0x10bf: "J" +0x10c0: "H" +0x10c1: "E" +0x10c2: "Y" +0x10c3: "W" +0x10c4: "Xh" +0x10c5: "OE" +0x10c6: "[?]" +0x10c7: "[?]" +0x10c8: "[?]" +0x10c9: "[?]" +0x10ca: "[?]" +0x10cb: "[?]" +0x10cc: "[?]" +0x10cd: "[?]" +0x10ce: "[?]" +0x10cf: "[?]" +0x10d0: "a" +0x10d1: "b" +0x10d2: "g" +0x10d3: "d" +0x10d4: "e" +0x10d5: "v" +0x10d6: "z" +0x10d7: "t`" +0x10d8: "i" +0x10d9: "k" +0x10da: "l" +0x10db: "m" +0x10dc: "n" +0x10dd: "o" +0x10de: "p" +0x10df: "zh" +0x10e0: "r" +0x10e1: "s" +0x10e2: "t" +0x10e3: "u" +0x10e4: "p`" +0x10e5: "k`" +0x10e6: "g'" +0x10e7: "q" +0x10e8: "sh" +0x10e9: "ch`" +0x10ea: "c`" +0x10eb: "z'" +0x10ec: "c" +0x10ed: "ch" +0x10ee: "x" +0x10ef: "j" +0x10f0: "h" +0x10f1: "e" +0x10f2: "y" +0x10f3: "w" +0x10f4: "xh" +0x10f5: "oe" +0x10f6: "f" +0x10f7: "[?]" +0x10f8: "[?]" +0x10f9: "[?]" +0x10fa: "[?]" +0x10fb: " // " +0x10fc: "[?]" +0x10fd: "[?]" +0x10fe: "[?]" +/* x011 */ +0x1100: "g" +0x1101: "gg" +0x1102: "n" +0x1103: "d" +0x1104: "dd" +0x1105: "r" +0x1106: "m" +0x1107: "b" +0x1108: "bb" +0x1109: "s" +0x110a: "ss" +0x110b: "" +0x110c: "j" +0x110d: "jj" +0x110e: "c" +0x110f: "k" +0x1110: "t" +0x1111: "p" +0x1112: "h" +0x1113: "ng" +0x1114: "nn" +0x1115: "nd" +0x1116: "nb" +0x1117: "dg" +0x1118: "rn" +0x1119: "rr" +0x111a: "rh" +0x111b: "rN" +0x111c: "mb" +0x111d: "mN" +0x111e: "bg" +0x111f: "bn" +0x1120: "" +0x1121: "bs" +0x1122: "bsg" +0x1123: "bst" +0x1124: "bsb" +0x1125: "bss" +0x1126: "bsj" +0x1127: "bj" +0x1128: "bc" +0x1129: "bt" +0x112a: "bp" +0x112b: "bN" +0x112c: "bbN" +0x112d: "sg" +0x112e: "sn" +0x112f: "sd" +0x1130: "sr" +0x1131: "sm" +0x1132: "sb" +0x1133: "sbg" +0x1134: "sss" +0x1135: "s" +0x1136: "sj" +0x1137: "sc" +0x1138: "sk" +0x1139: "st" +0x113a: "sp" +0x113b: "sh" +0x113c: "" +0x113d: "" +0x113e: "" +0x113f: "" +0x1140: "Z" +0x1141: "g" +0x1142: "d" +0x1143: "m" +0x1144: "b" +0x1145: "s" +0x1146: "Z" +0x1147: "" +0x1148: "j" +0x1149: "c" +0x114a: "t" +0x114b: "p" +0x114c: "N" +0x114d: "j" +0x114e: "" +0x114f: "" +0x1150: "" +0x1151: "" +0x1152: "ck" +0x1153: "ch" +0x1154: "" +0x1155: "" +0x1156: "pb" +0x1157: "pN" +0x1158: "hh" +0x1159: "Q" +0x115a: "[?]" +0x115b: "[?]" +0x115c: "[?]" +0x115d: "[?]" +0x115e: "[?]" +0x115f: "" +0x1160: "" +0x1161: "a" +0x1162: "ae" +0x1163: "ya" +0x1164: "yae" +0x1165: "eo" +0x1166: "e" +0x1167: "yeo" +0x1168: "ye" +0x1169: "o" +0x116a: "wa" +0x116b: "wae" +0x116c: "oe" +0x116d: "yo" +0x116e: "u" +0x116f: "weo" +0x1170: "we" +0x1171: "wi" +0x1172: "yu" +0x1173: "eu" +0x1174: "yi" +0x1175: "i" +0x1176: "a-o" +0x1177: "a-u" +0x1178: "ya-o" +0x1179: "ya-yo" +0x117a: "eo-o" +0x117b: "eo-u" +0x117c: "eo-eu" +0x117d: "yeo-o" +0x117e: "yeo-u" +0x117f: "o-eo" +0x1180: "o-e" +0x1181: "o-ye" +0x1182: "o-o" +0x1183: "o-u" +0x1184: "yo-ya" +0x1185: "yo-yae" +0x1186: "yo-yeo" +0x1187: "yo-o" +0x1188: "yo-i" +0x1189: "u-a" +0x118a: "u-ae" +0x118b: "u-eo-eu" +0x118c: "u-ye" +0x118d: "u-u" +0x118e: "yu-a" +0x118f: "yu-eo" +0x1190: "yu-e" +0x1191: "yu-yeo" +0x1192: "yu-ye" +0x1193: "yu-u" +0x1194: "yu-i" +0x1195: "eu-u" +0x1196: "eu-eu" +0x1197: "yi-u" +0x1198: "i-a" +0x1199: "i-ya" +0x119a: "i-o" +0x119b: "i-u" +0x119c: "i-eu" +0x119d: "i-U" +0x119e: "U" +0x119f: "U-eo" +0x11a0: "U-u" +0x11a1: "U-i" +0x11a2: "UU" +0x11a3: "[?]" +0x11a4: "[?]" +0x11a5: "[?]" +0x11a6: "[?]" +0x11a7: "[?]" +0x11a8: "g" +0x11a9: "gg" +0x11aa: "gs" +0x11ab: "n" +0x11ac: "nj" +0x11ad: "nh" +0x11ae: "d" +0x11af: "l" +0x11b0: "lg" +0x11b1: "lm" +0x11b2: "lb" +0x11b3: "ls" +0x11b4: "lt" +0x11b5: "lp" +0x11b6: "lh" +0x11b7: "m" +0x11b8: "b" +0x11b9: "bs" +0x11ba: "s" +0x11bb: "ss" +0x11bc: "ng" +0x11bd: "j" +0x11be: "c" +0x11bf: "k" +0x11c0: "t" +0x11c1: "p" +0x11c2: "h" +0x11c3: "gl" +0x11c4: "gsg" +0x11c5: "ng" +0x11c6: "nd" +0x11c7: "ns" +0x11c8: "nZ" +0x11c9: "nt" +0x11ca: "dg" +0x11cb: "tl" +0x11cc: "lgs" +0x11cd: "ln" +0x11ce: "ld" +0x11cf: "lth" +0x11d0: "ll" +0x11d1: "lmg" +0x11d2: "lms" +0x11d3: "lbs" +0x11d4: "lbh" +0x11d5: "rNp" +0x11d6: "lss" +0x11d7: "lZ" +0x11d8: "lk" +0x11d9: "lQ" +0x11da: "mg" +0x11db: "ml" +0x11dc: "mb" +0x11dd: "ms" +0x11de: "mss" +0x11df: "mZ" +0x11e0: "mc" +0x11e1: "mh" +0x11e2: "mN" +0x11e3: "bl" +0x11e4: "bp" +0x11e5: "ph" +0x11e6: "pN" +0x11e7: "sg" +0x11e8: "sd" +0x11e9: "sl" +0x11ea: "sb" +0x11eb: "Z" +0x11ec: "g" +0x11ed: "ss" +0x11ee: "" +0x11ef: "kh" +0x11f0: "N" +0x11f1: "Ns" +0x11f2: "NZ" +0x11f3: "pb" +0x11f4: "pN" +0x11f5: "hn" +0x11f6: "hl" +0x11f7: "hm" +0x11f8: "hb" +0x11f9: "Q" +0x11fa: "[?]" +0x11fb: "[?]" +0x11fc: "[?]" +0x11fd: "[?]" +0x11fe: "[?]" +/* x012 */ +0x1200: "ha" +0x1201: "hu" +0x1202: "hi" +0x1203: "haa" +0x1204: "hee" +0x1205: "he" +0x1206: "ho" +0x1207: "[?]" +0x1208: "la" +0x1209: "lu" +0x120a: "li" +0x120b: "laa" +0x120c: "lee" +0x120d: "le" +0x120e: "lo" +0x120f: "lwa" +0x1210: "hha" +0x1211: "hhu" +0x1212: "hhi" +0x1213: "hhaa" +0x1214: "hhee" +0x1215: "hhe" +0x1216: "hho" +0x1217: "hhwa" +0x1218: "ma" +0x1219: "mu" +0x121a: "mi" +0x121b: "maa" +0x121c: "mee" +0x121d: "me" +0x121e: "mo" +0x121f: "mwa" +0x1220: "sza" +0x1221: "szu" +0x1222: "szi" +0x1223: "szaa" +0x1224: "szee" +0x1225: "sze" +0x1226: "szo" +0x1227: "szwa" +0x1228: "ra" +0x1229: "ru" +0x122a: "ri" +0x122b: "raa" +0x122c: "ree" +0x122d: "re" +0x122e: "ro" +0x122f: "rwa" +0x1230: "sa" +0x1231: "su" +0x1232: "si" +0x1233: "saa" +0x1234: "see" +0x1235: "se" +0x1236: "so" +0x1237: "swa" +0x1238: "sha" +0x1239: "shu" +0x123a: "shi" +0x123b: "shaa" +0x123c: "shee" +0x123d: "she" +0x123e: "sho" +0x123f: "shwa" +0x1240: "qa" +0x1241: "qu" +0x1242: "qi" +0x1243: "qaa" +0x1244: "qee" +0x1245: "qe" +0x1246: "qo" +0x1247: "[?]" +0x1248: "qwa" +0x1249: "[?]" +0x124a: "qwi" +0x124b: "qwaa" +0x124c: "qwee" +0x124d: "qwe" +0x124e: "[?]" +0x124f: "[?]" +0x1250: "qha" +0x1251: "qhu" +0x1252: "qhi" +0x1253: "qhaa" +0x1254: "qhee" +0x1255: "qhe" +0x1256: "qho" +0x1257: "[?]" +0x1258: "qhwa" +0x1259: "[?]" +0x125a: "qhwi" +0x125b: "qhwaa" +0x125c: "qhwee" +0x125d: "qhwe" +0x125e: "[?]" +0x125f: "[?]" +0x1260: "ba" +0x1261: "bu" +0x1262: "bi" +0x1263: "baa" +0x1264: "bee" +0x1265: "be" +0x1266: "bo" +0x1267: "bwa" +0x1268: "va" +0x1269: "vu" +0x126a: "vi" +0x126b: "vaa" +0x126c: "vee" +0x126d: "ve" +0x126e: "vo" +0x126f: "vwa" +0x1270: "ta" +0x1271: "tu" +0x1272: "ti" +0x1273: "taa" +0x1274: "tee" +0x1275: "te" +0x1276: "to" +0x1277: "twa" +0x1278: "ca" +0x1279: "cu" +0x127a: "ci" +0x127b: "caa" +0x127c: "cee" +0x127d: "ce" +0x127e: "co" +0x127f: "cwa" +0x1280: "xa" +0x1281: "xu" +0x1282: "xi" +0x1283: "xaa" +0x1284: "xee" +0x1285: "xe" +0x1286: "xo" +0x1287: "[?]" +0x1288: "xwa" +0x1289: "[?]" +0x128a: "xwi" +0x128b: "xwaa" +0x128c: "xwee" +0x128d: "xwe" +0x128e: "[?]" +0x128f: "[?]" +0x1290: "na" +0x1291: "nu" +0x1292: "ni" +0x1293: "naa" +0x1294: "nee" +0x1295: "ne" +0x1296: "no" +0x1297: "nwa" +0x1298: "nya" +0x1299: "nyu" +0x129a: "nyi" +0x129b: "nyaa" +0x129c: "nyee" +0x129d: "nye" +0x129e: "nyo" +0x129f: "nywa" +0x12a0: "'a" +0x12a1: "'u" +0x12a2: "[?]" +0x12a3: "'aa" +0x12a4: "'ee" +0x12a5: "'e" +0x12a6: "'o" +0x12a7: "'wa" +0x12a8: "ka" +0x12a9: "ku" +0x12aa: "ki" +0x12ab: "kaa" +0x12ac: "kee" +0x12ad: "ke" +0x12ae: "ko" +0x12af: "[?]" +0x12b0: "kwa" +0x12b1: "[?]" +0x12b2: "kwi" +0x12b3: "kwaa" +0x12b4: "kwee" +0x12b5: "kwe" +0x12b6: "[?]" +0x12b7: "[?]" +0x12b8: "kxa" +0x12b9: "kxu" +0x12ba: "kxi" +0x12bb: "kxaa" +0x12bc: "kxee" +0x12bd: "kxe" +0x12be: "kxo" +0x12bf: "[?]" +0x12c0: "kxwa" +0x12c1: "[?]" +0x12c2: "kxwi" +0x12c3: "kxwaa" +0x12c4: "kxwee" +0x12c5: "kxwe" +0x12c6: "[?]" +0x12c7: "[?]" +0x12c8: "wa" +0x12c9: "wu" +0x12ca: "wi" +0x12cb: "waa" +0x12cc: "wee" +0x12cd: "we" +0x12ce: "wo" +0x12cf: "[?]" +0x12d0: "`a" +0x12d1: "`u" +0x12d2: "`i" +0x12d3: "`aa" +0x12d4: "`ee" +0x12d5: "`e" +0x12d6: "`o" +0x12d7: "[?]" +0x12d8: "za" +0x12d9: "zu" +0x12da: "zi" +0x12db: "zaa" +0x12dc: "zee" +0x12dd: "ze" +0x12de: "zo" +0x12df: "zwa" +0x12e0: "zha" +0x12e1: "zhu" +0x12e2: "zhi" +0x12e3: "zhaa" +0x12e4: "zhee" +0x12e5: "zhe" +0x12e6: "zho" +0x12e7: "zhwa" +0x12e8: "ya" +0x12e9: "yu" +0x12ea: "yi" +0x12eb: "yaa" +0x12ec: "yee" +0x12ed: "ye" +0x12ee: "yo" +0x12ef: "[?]" +0x12f0: "da" +0x12f1: "du" +0x12f2: "di" +0x12f3: "daa" +0x12f4: "dee" +0x12f5: "de" +0x12f6: "do" +0x12f7: "dwa" +0x12f8: "dda" +0x12f9: "ddu" +0x12fa: "ddi" +0x12fb: "ddaa" +0x12fc: "ddee" +0x12fd: "dde" +0x12fe: "ddo" +0x12ff: "ddwa" +/* x013 */ +0x1300: "ja" +0x1301: "ju" +0x1302: "ji" +0x1303: "jaa" +0x1304: "jee" +0x1305: "je" +0x1306: "jo" +0x1307: "jwa" +0x1308: "ga" +0x1309: "gu" +0x130a: "gi" +0x130b: "gaa" +0x130c: "gee" +0x130d: "ge" +0x130e: "go" +0x130f: "[?]" +0x1310: "gwa" +0x1311: "[?]" +0x1312: "gwi" +0x1313: "gwaa" +0x1314: "gwee" +0x1315: "gwe" +0x1316: "[?]" +0x1317: "[?]" +0x1318: "gga" +0x1319: "ggu" +0x131a: "ggi" +0x131b: "ggaa" +0x131c: "ggee" +0x131d: "gge" +0x131e: "ggo" +0x131f: "[?]" +0x1320: "tha" +0x1321: "thu" +0x1322: "thi" +0x1323: "thaa" +0x1324: "thee" +0x1325: "the" +0x1326: "tho" +0x1327: "thwa" +0x1328: "cha" +0x1329: "chu" +0x132a: "chi" +0x132b: "chaa" +0x132c: "chee" +0x132d: "che" +0x132e: "cho" +0x132f: "chwa" +0x1330: "pha" +0x1331: "phu" +0x1332: "phi" +0x1333: "phaa" +0x1334: "phee" +0x1335: "phe" +0x1336: "pho" +0x1337: "phwa" +0x1338: "tsa" +0x1339: "tsu" +0x133a: "tsi" +0x133b: "tsaa" +0x133c: "tsee" +0x133d: "tse" +0x133e: "tso" +0x133f: "tswa" +0x1340: "tza" +0x1341: "tzu" +0x1342: "tzi" +0x1343: "tzaa" +0x1344: "tzee" +0x1345: "tze" +0x1346: "tzo" +0x1347: "[?]" +0x1348: "fa" +0x1349: "fu" +0x134a: "fi" +0x134b: "faa" +0x134c: "fee" +0x134d: "fe" +0x134e: "fo" +0x134f: "fwa" +0x1350: "pa" +0x1351: "pu" +0x1352: "pi" +0x1353: "paa" +0x1354: "pee" +0x1355: "pe" +0x1356: "po" +0x1357: "pwa" +0x1358: "rya" +0x1359: "mya" +0x135a: "fya" +0x135b: "[?]" +0x135c: "[?]" +0x135d: "[?]" +0x135e: "[?]" +0x135f: "[?]" +0x1360: "[?]" +0x1361: " " +0x1362: "." +0x1363: "," +0x1364: ";" +0x1365: ":" +0x1366: ":: " +0x1367: "?" +0x1368: "//" +0x1369: "1" +0x136a: "2" +0x136b: "3" +0x136c: "4" +0x136d: "5" +0x136e: "6" +0x136f: "7" +0x1370: "8" +0x1371: "9" +0x1372: "10+" +0x1373: "20+" +0x1374: "30+" +0x1375: "40+" +0x1376: "50+" +0x1377: "60+" +0x1378: "70+" +0x1379: "80+" +0x137a: "90+" +0x137b: "100+" +0x137c: "10,000+" +0x137d: "[?]" +0x137e: "[?]" +0x137f: "[?]" +0x1380: "[?]" +0x1381: "[?]" +0x1382: "[?]" +0x1383: "[?]" +0x1384: "[?]" +0x1385: "[?]" +0x1386: "[?]" +0x1387: "[?]" +0x1388: "[?]" +0x1389: "[?]" +0x138a: "[?]" +0x138b: "[?]" +0x138c: "[?]" +0x138d: "[?]" +0x138e: "[?]" +0x138f: "[?]" +0x1390: "[?]" +0x1391: "[?]" +0x1392: "[?]" +0x1393: "[?]" +0x1394: "[?]" +0x1395: "[?]" +0x1396: "[?]" +0x1397: "[?]" +0x1398: "[?]" +0x1399: "[?]" +0x139a: "[?]" +0x139b: "[?]" +0x139c: "[?]" +0x139d: "[?]" +0x139e: "[?]" +0x139f: "[?]" +0x13a0: "a" +0x13a1: "e" +0x13a2: "i" +0x13a3: "o" +0x13a4: "u" +0x13a5: "v" +0x13a6: "ga" +0x13a7: "ka" +0x13a8: "ge" +0x13a9: "gi" +0x13aa: "go" +0x13ab: "gu" +0x13ac: "gv" +0x13ad: "ha" +0x13ae: "he" +0x13af: "hi" +0x13b0: "ho" +0x13b1: "hu" +0x13b2: "hv" +0x13b3: "la" +0x13b4: "le" +0x13b5: "li" +0x13b6: "lo" +0x13b7: "lu" +0x13b8: "lv" +0x13b9: "ma" +0x13ba: "me" +0x13bb: "mi" +0x13bc: "mo" +0x13bd: "mu" +0x13be: "na" +0x13bf: "hna" +0x13c0: "nah" +0x13c1: "ne" +0x13c2: "ni" +0x13c3: "no" +0x13c4: "nu" +0x13c5: "nv" +0x13c6: "qua" +0x13c7: "que" +0x13c8: "qui" +0x13c9: "quo" +0x13ca: "quu" +0x13cb: "quv" +0x13cc: "sa" +0x13cd: "s" +0x13ce: "se" +0x13cf: "si" +0x13d0: "so" +0x13d1: "su" +0x13d2: "sv" +0x13d3: "da" +0x13d4: "ta" +0x13d5: "de" +0x13d6: "te" +0x13d7: "di" +0x13d8: "ti" +0x13d9: "do" +0x13da: "du" +0x13db: "dv" +0x13dc: "dla" +0x13dd: "tla" +0x13de: "tle" +0x13df: "tli" +0x13e0: "tlo" +0x13e1: "tlu" +0x13e2: "tlv" +0x13e3: "tsa" +0x13e4: "tse" +0x13e5: "tsi" +0x13e6: "tso" +0x13e7: "tsu" +0x13e8: "tsv" +0x13e9: "wa" +0x13ea: "we" +0x13eb: "wi" +0x13ec: "wo" +0x13ed: "wu" +0x13ee: "wv" +0x13ef: "ya" +0x13f0: "ye" +0x13f1: "yi" +0x13f2: "yo" +0x13f3: "yu" +0x13f4: "yv" +0x13f5: "[?]" +0x13f6: "[?]" +0x13f7: "[?]" +0x13f8: "[?]" +0x13f9: "[?]" +0x13fa: "[?]" +0x13fb: "[?]" +0x13fc: "[?]" +0x13fd: "[?]" +0x13fe: "[?]" +/* x014 */ +0x1400: "[?]" +0x1401: "e" +0x1402: "aai" +0x1403: "i" +0x1404: "ii" +0x1405: "o" +0x1406: "oo" +0x1407: "oo" +0x1408: "ee" +0x1409: "i" +0x140a: "a" +0x140b: "aa" +0x140c: "we" +0x140d: "we" +0x140e: "wi" +0x140f: "wi" +0x1410: "wii" +0x1411: "wii" +0x1412: "wo" +0x1413: "wo" +0x1414: "woo" +0x1415: "woo" +0x1416: "woo" +0x1417: "wa" +0x1418: "wa" +0x1419: "waa" +0x141a: "waa" +0x141b: "waa" +0x141c: "ai" +0x141d: "w" +0x141e: "'" +0x141f: "t" +0x1420: "k" +0x1421: "sh" +0x1422: "s" +0x1423: "n" +0x1424: "w" +0x1425: "n" +0x1426: "[?]" +0x1427: "w" +0x1428: "c" +0x1429: "?" +0x142a: "l" +0x142b: "en" +0x142c: "in" +0x142d: "on" +0x142e: "an" +0x142f: "pe" +0x1430: "paai" +0x1431: "pi" +0x1432: "pii" +0x1433: "po" +0x1434: "poo" +0x1435: "poo" +0x1436: "hee" +0x1437: "hi" +0x1438: "pa" +0x1439: "paa" +0x143a: "pwe" +0x143b: "pwe" +0x143c: "pwi" +0x143d: "pwi" +0x143e: "pwii" +0x143f: "pwii" +0x1440: "pwo" +0x1441: "pwo" +0x1442: "pwoo" +0x1443: "pwoo" +0x1444: "pwa" +0x1445: "pwa" +0x1446: "pwaa" +0x1447: "pwaa" +0x1448: "pwaa" +0x1449: "p" +0x144a: "p" +0x144b: "h" +0x144c: "te" +0x144d: "taai" +0x144e: "ti" +0x144f: "tii" +0x1450: "to" +0x1451: "too" +0x1452: "too" +0x1453: "dee" +0x1454: "di" +0x1455: "ta" +0x1456: "taa" +0x1457: "twe" +0x1458: "twe" +0x1459: "twi" +0x145a: "twi" +0x145b: "twii" +0x145c: "twii" +0x145d: "two" +0x145e: "two" +0x145f: "twoo" +0x1460: "twoo" +0x1461: "twa" +0x1462: "twa" +0x1463: "twaa" +0x1464: "twaa" +0x1465: "twaa" +0x1466: "t" +0x1467: "tte" +0x1468: "tti" +0x1469: "tto" +0x146a: "tta" +0x146b: "ke" +0x146c: "kaai" +0x146d: "ki" +0x146e: "kii" +0x146f: "ko" +0x1470: "koo" +0x1471: "koo" +0x1472: "ka" +0x1473: "kaa" +0x1474: "kwe" +0x1475: "kwe" +0x1476: "kwi" +0x1477: "kwi" +0x1478: "kwii" +0x1479: "kwii" +0x147a: "kwo" +0x147b: "kwo" +0x147c: "kwoo" +0x147d: "kwoo" +0x147e: "kwa" +0x147f: "kwa" +0x1480: "kwaa" +0x1481: "kwaa" +0x1482: "kwaa" +0x1483: "k" +0x1484: "kw" +0x1485: "keh" +0x1486: "kih" +0x1487: "koh" +0x1488: "kah" +0x1489: "ce" +0x148a: "caai" +0x148b: "ci" +0x148c: "cii" +0x148d: "co" +0x148e: "coo" +0x148f: "coo" +0x1490: "ca" +0x1491: "caa" +0x1492: "cwe" +0x1493: "cwe" +0x1494: "cwi" +0x1495: "cwi" +0x1496: "cwii" +0x1497: "cwii" +0x1498: "cwo" +0x1499: "cwo" +0x149a: "cwoo" +0x149b: "cwoo" +0x149c: "cwa" +0x149d: "cwa" +0x149e: "cwaa" +0x149f: "cwaa" +0x14a0: "cwaa" +0x14a1: "c" +0x14a2: "th" +0x14a3: "me" +0x14a4: "maai" +0x14a5: "mi" +0x14a6: "mii" +0x14a7: "mo" +0x14a8: "moo" +0x14a9: "moo" +0x14aa: "ma" +0x14ab: "maa" +0x14ac: "mwe" +0x14ad: "mwe" +0x14ae: "mwi" +0x14af: "mwi" +0x14b0: "mwii" +0x14b1: "mwii" +0x14b2: "mwo" +0x14b3: "mwo" +0x14b4: "mwoo" +0x14b5: "mwoo" +0x14b6: "mwa" +0x14b7: "mwa" +0x14b8: "mwaa" +0x14b9: "mwaa" +0x14ba: "mwaa" +0x14bb: "m" +0x14bc: "m" +0x14bd: "mh" +0x14be: "m" +0x14bf: "m" +0x14c0: "ne" +0x14c1: "naai" +0x14c2: "ni" +0x14c3: "nii" +0x14c4: "no" +0x14c5: "noo" +0x14c6: "noo" +0x14c7: "na" +0x14c8: "naa" +0x14c9: "nwe" +0x14ca: "nwe" +0x14cb: "nwa" +0x14cc: "nwa" +0x14cd: "nwaa" +0x14ce: "nwaa" +0x14cf: "nwaa" +0x14d0: "n" +0x14d1: "ng" +0x14d2: "nh" +0x14d3: "le" +0x14d4: "laai" +0x14d5: "li" +0x14d6: "lii" +0x14d7: "lo" +0x14d8: "loo" +0x14d9: "loo" +0x14da: "la" +0x14db: "laa" +0x14dc: "lwe" +0x14dd: "lwe" +0x14de: "lwi" +0x14df: "lwi" +0x14e0: "lwii" +0x14e1: "lwii" +0x14e2: "lwo" +0x14e3: "lwo" +0x14e4: "lwoo" +0x14e5: "lwoo" +0x14e6: "lwa" +0x14e7: "lwa" +0x14e8: "lwaa" +0x14e9: "lwaa" +0x14ea: "l" +0x14eb: "l" +0x14ec: "l" +0x14ed: "se" +0x14ee: "saai" +0x14ef: "si" +0x14f0: "sii" +0x14f1: "so" +0x14f2: "soo" +0x14f3: "soo" +0x14f4: "sa" +0x14f5: "saa" +0x14f6: "swe" +0x14f7: "swe" +0x14f8: "swi" +0x14f9: "swi" +0x14fa: "swii" +0x14fb: "swii" +0x14fc: "swo" +0x14fd: "swo" +0x14fe: "swoo" +0x14ff: "swoo" +/* x015 */ +0x1500: "swa" +0x1501: "swa" +0x1502: "swaa" +0x1503: "swaa" +0x1504: "swaa" +0x1505: "s" +0x1506: "s" +0x1507: "sw" +0x1508: "s" +0x1509: "sk" +0x150a: "skw" +0x150b: "sW" +0x150c: "spwa" +0x150d: "stwa" +0x150e: "skwa" +0x150f: "scwa" +0x1510: "she" +0x1511: "shi" +0x1512: "shii" +0x1513: "sho" +0x1514: "shoo" +0x1515: "sha" +0x1516: "shaa" +0x1517: "shwe" +0x1518: "shwe" +0x1519: "shwi" +0x151a: "shwi" +0x151b: "shwii" +0x151c: "shwii" +0x151d: "shwo" +0x151e: "shwo" +0x151f: "shwoo" +0x1520: "shwoo" +0x1521: "shwa" +0x1522: "shwa" +0x1523: "shwaa" +0x1524: "shwaa" +0x1525: "sh" +0x1526: "ye" +0x1527: "yaai" +0x1528: "yi" +0x1529: "yii" +0x152a: "yo" +0x152b: "yoo" +0x152c: "yoo" +0x152d: "ya" +0x152e: "yaa" +0x152f: "ywe" +0x1530: "ywe" +0x1531: "ywi" +0x1532: "ywi" +0x1533: "ywii" +0x1534: "ywii" +0x1535: "ywo" +0x1536: "ywo" +0x1537: "ywoo" +0x1538: "ywoo" +0x1539: "ywa" +0x153a: "ywa" +0x153b: "ywaa" +0x153c: "ywaa" +0x153d: "ywaa" +0x153e: "y" +0x153f: "y" +0x1540: "y" +0x1541: "yi" +0x1542: "re" +0x1543: "re" +0x1544: "le" +0x1545: "raai" +0x1546: "ri" +0x1547: "rii" +0x1548: "ro" +0x1549: "roo" +0x154a: "lo" +0x154b: "ra" +0x154c: "raa" +0x154d: "la" +0x154e: "rwaa" +0x154f: "rwaa" +0x1550: "r" +0x1551: "r" +0x1552: "r" +0x1553: "fe" +0x1554: "faai" +0x1555: "fi" +0x1556: "fii" +0x1557: "fo" +0x1558: "foo" +0x1559: "fa" +0x155a: "faa" +0x155b: "fwaa" +0x155c: "fwaa" +0x155d: "f" +0x155e: "the" +0x155f: "the" +0x1560: "thi" +0x1561: "thi" +0x1562: "thii" +0x1563: "thii" +0x1564: "tho" +0x1565: "thoo" +0x1566: "tha" +0x1567: "thaa" +0x1568: "thwaa" +0x1569: "thwaa" +0x156a: "th" +0x156b: "tthe" +0x156c: "tthi" +0x156d: "ttho" +0x156e: "ttha" +0x156f: "tth" +0x1570: "tye" +0x1571: "tyi" +0x1572: "tyo" +0x1573: "tya" +0x1574: "he" +0x1575: "hi" +0x1576: "hii" +0x1577: "ho" +0x1578: "hoo" +0x1579: "ha" +0x157a: "haa" +0x157b: "h" +0x157c: "h" +0x157d: "hk" +0x157e: "qaai" +0x157f: "qi" +0x1580: "qii" +0x1581: "qo" +0x1582: "qoo" +0x1583: "qa" +0x1584: "qaa" +0x1585: "q" +0x1586: "tlhe" +0x1587: "tlhi" +0x1588: "tlho" +0x1589: "tlha" +0x158a: "re" +0x158b: "ri" +0x158c: "ro" +0x158d: "ra" +0x158e: "ngaai" +0x158f: "ngi" +0x1590: "ngii" +0x1591: "ngo" +0x1592: "ngoo" +0x1593: "nga" +0x1594: "ngaa" +0x1595: "ng" +0x1596: "nng" +0x1597: "she" +0x1598: "shi" +0x1599: "sho" +0x159a: "sha" +0x159b: "the" +0x159c: "thi" +0x159d: "tho" +0x159e: "tha" +0x159f: "th" +0x15a0: "lhi" +0x15a1: "lhii" +0x15a2: "lho" +0x15a3: "lhoo" +0x15a4: "lha" +0x15a5: "lhaa" +0x15a6: "lh" +0x15a7: "the" +0x15a8: "thi" +0x15a9: "thii" +0x15aa: "tho" +0x15ab: "thoo" +0x15ac: "tha" +0x15ad: "thaa" +0x15ae: "th" +0x15af: "b" +0x15b0: "e" +0x15b1: "i" +0x15b2: "o" +0x15b3: "a" +0x15b4: "we" +0x15b5: "wi" +0x15b6: "wo" +0x15b7: "wa" +0x15b8: "ne" +0x15b9: "ni" +0x15ba: "no" +0x15bb: "na" +0x15bc: "ke" +0x15bd: "ki" +0x15be: "ko" +0x15bf: "ka" +0x15c0: "he" +0x15c1: "hi" +0x15c2: "ho" +0x15c3: "ha" +0x15c4: "ghu" +0x15c5: "gho" +0x15c6: "ghe" +0x15c7: "ghee" +0x15c8: "ghi" +0x15c9: "gha" +0x15ca: "ru" +0x15cb: "ro" +0x15cc: "re" +0x15cd: "ree" +0x15ce: "ri" +0x15cf: "ra" +0x15d0: "wu" +0x15d1: "wo" +0x15d2: "we" +0x15d3: "wee" +0x15d4: "wi" +0x15d5: "wa" +0x15d6: "hwu" +0x15d7: "hwo" +0x15d8: "hwe" +0x15d9: "hwee" +0x15da: "hwi" +0x15db: "hwa" +0x15dc: "thu" +0x15dd: "tho" +0x15de: "the" +0x15df: "thee" +0x15e0: "thi" +0x15e1: "tha" +0x15e2: "ttu" +0x15e3: "tto" +0x15e4: "tte" +0x15e5: "ttee" +0x15e6: "tti" +0x15e7: "tta" +0x15e8: "pu" +0x15e9: "po" +0x15ea: "pe" +0x15eb: "pee" +0x15ec: "pi" +0x15ed: "pa" +0x15ee: "p" +0x15ef: "gu" +0x15f0: "go" +0x15f1: "ge" +0x15f2: "gee" +0x15f3: "gi" +0x15f4: "ga" +0x15f5: "khu" +0x15f6: "kho" +0x15f7: "khe" +0x15f8: "khee" +0x15f9: "khi" +0x15fa: "kha" +0x15fb: "kku" +0x15fc: "kko" +0x15fd: "kke" +0x15fe: "kkee" +0x15ff: "kki" +/* x016 */ +0x1600: "kka" +0x1601: "kk" +0x1602: "nu" +0x1603: "no" +0x1604: "ne" +0x1605: "nee" +0x1606: "ni" +0x1607: "na" +0x1608: "mu" +0x1609: "mo" +0x160a: "me" +0x160b: "mee" +0x160c: "mi" +0x160d: "ma" +0x160e: "yu" +0x160f: "yo" +0x1610: "ye" +0x1611: "yee" +0x1612: "yi" +0x1613: "ya" +0x1614: "ju" +0x1615: "ju" +0x1616: "jo" +0x1617: "je" +0x1618: "jee" +0x1619: "ji" +0x161a: "ji" +0x161b: "ja" +0x161c: "jju" +0x161d: "jjo" +0x161e: "jje" +0x161f: "jjee" +0x1620: "jji" +0x1621: "jja" +0x1622: "lu" +0x1623: "lo" +0x1624: "le" +0x1625: "lee" +0x1626: "li" +0x1627: "la" +0x1628: "dlu" +0x1629: "dlo" +0x162a: "dle" +0x162b: "dlee" +0x162c: "dli" +0x162d: "dla" +0x162e: "lhu" +0x162f: "lho" +0x1630: "lhe" +0x1631: "lhee" +0x1632: "lhi" +0x1633: "lha" +0x1634: "tlhu" +0x1635: "tlho" +0x1636: "tlhe" +0x1637: "tlhee" +0x1638: "tlhi" +0x1639: "tlha" +0x163a: "tlu" +0x163b: "tlo" +0x163c: "tle" +0x163d: "tlee" +0x163e: "tli" +0x163f: "tla" +0x1640: "zu" +0x1641: "zo" +0x1642: "ze" +0x1643: "zee" +0x1644: "zi" +0x1645: "za" +0x1646: "z" +0x1647: "z" +0x1648: "dzu" +0x1649: "dzo" +0x164a: "dze" +0x164b: "dzee" +0x164c: "dzi" +0x164d: "dza" +0x164e: "su" +0x164f: "so" +0x1650: "se" +0x1651: "see" +0x1652: "si" +0x1653: "sa" +0x1654: "shu" +0x1655: "sho" +0x1656: "she" +0x1657: "shee" +0x1658: "shi" +0x1659: "sha" +0x165a: "sh" +0x165b: "tsu" +0x165c: "tso" +0x165d: "tse" +0x165e: "tsee" +0x165f: "tsi" +0x1660: "tsa" +0x1661: "chu" +0x1662: "cho" +0x1663: "che" +0x1664: "chee" +0x1665: "chi" +0x1666: "cha" +0x1667: "ttsu" +0x1668: "ttso" +0x1669: "ttse" +0x166a: "ttsee" +0x166b: "ttsi" +0x166c: "ttsa" +0x166d: "X" +0x166e: "." +0x166f: "qai" +0x1670: "ngai" +0x1671: "nngi" +0x1672: "nngii" +0x1673: "nngo" +0x1674: "nngoo" +0x1675: "nnga" +0x1676: "nngaa" +0x1677: "[?]" +0x1678: "[?]" +0x1679: "[?]" +0x167a: "[?]" +0x167b: "[?]" +0x167c: "[?]" +0x167d: "[?]" +0x167e: "[?]" +0x167f: "[?]" +0x1680: " " +0x1681: "b" +0x1682: "l" +0x1683: "f" +0x1684: "s" +0x1685: "n" +0x1686: "h" +0x1687: "d" +0x1688: "t" +0x1689: "c" +0x168a: "q" +0x168b: "m" +0x168c: "g" +0x168d: "ng" +0x168e: "z" +0x168f: "r" +0x1690: "a" +0x1691: "o" +0x1692: "u" +0x1693: "e" +0x1694: "i" +0x1695: "ch" +0x1696: "th" +0x1697: "ph" +0x1698: "p" +0x1699: "x" +0x169a: "p" +0x169b: "<" +0x169c: ">" +0x169d: "[?]" +0x169e: "[?]" +0x169f: "[?]" +0x16a0: "f" +0x16a1: "v" +0x16a2: "u" +0x16a3: "yr" +0x16a4: "y" +0x16a5: "w" +0x16a6: "th" +0x16a7: "th" +0x16a8: "a" +0x16a9: "o" +0x16aa: "ac" +0x16ab: "ae" +0x16ac: "o" +0x16ad: "o" +0x16ae: "o" +0x16af: "oe" +0x16b0: "on" +0x16b1: "r" +0x16b2: "k" +0x16b3: "c" +0x16b4: "k" +0x16b5: "g" +0x16b6: "ng" +0x16b7: "g" +0x16b8: "g" +0x16b9: "w" +0x16ba: "h" +0x16bb: "h" +0x16bc: "h" +0x16bd: "h" +0x16be: "n" +0x16bf: "n" +0x16c0: "n" +0x16c1: "i" +0x16c2: "e" +0x16c3: "j" +0x16c4: "g" +0x16c5: "ae" +0x16c6: "a" +0x16c7: "eo" +0x16c8: "p" +0x16c9: "z" +0x16ca: "s" +0x16cb: "s" +0x16cc: "s" +0x16cd: "c" +0x16ce: "z" +0x16cf: "t" +0x16d0: "t" +0x16d1: "d" +0x16d2: "b" +0x16d3: "b" +0x16d4: "p" +0x16d5: "p" +0x16d6: "e" +0x16d7: "m" +0x16d8: "m" +0x16d9: "m" +0x16da: "l" +0x16db: "l" +0x16dc: "ng" +0x16dd: "ng" +0x16de: "d" +0x16df: "o" +0x16e0: "ear" +0x16e1: "ior" +0x16e2: "qu" +0x16e3: "qu" +0x16e4: "qu" +0x16e5: "s" +0x16e6: "yr" +0x16e7: "yr" +0x16e8: "yr" +0x16e9: "q" +0x16ea: "x" +0x16eb: "." +0x16ec: ":" +0x16ed: "+" +0x16ee: "17" +0x16ef: "18" +0x16f0: "19" +0x16f1: "[?]" +0x16f2: "[?]" +0x16f3: "[?]" +0x16f4: "[?]" +0x16f5: "[?]" +0x16f6: "[?]" +0x16f7: "[?]" +0x16f8: "[?]" +0x16f9: "[?]" +0x16fa: "[?]" +0x16fb: "[?]" +0x16fc: "[?]" +0x16fd: "[?]" +0x16fe: "[?]" +/* x017 */ +0x1700: "[?]" +0x1701: "[?]" +0x1702: "[?]" +0x1703: "[?]" +0x1704: "[?]" +0x1705: "[?]" +0x1706: "[?]" +0x1707: "[?]" +0x1708: "[?]" +0x1709: "[?]" +0x170a: "[?]" +0x170b: "[?]" +0x170c: "[?]" +0x170d: "[?]" +0x170e: "[?]" +0x170f: "[?]" +0x1710: "[?]" +0x1711: "[?]" +0x1712: "[?]" +0x1713: "[?]" +0x1714: "[?]" +0x1715: "[?]" +0x1716: "[?]" +0x1717: "[?]" +0x1718: "[?]" +0x1719: "[?]" +0x171a: "[?]" +0x171b: "[?]" +0x171c: "[?]" +0x171d: "[?]" +0x171e: "[?]" +0x171f: "[?]" +0x1720: "[?]" +0x1721: "[?]" +0x1722: "[?]" +0x1723: "[?]" +0x1724: "[?]" +0x1725: "[?]" +0x1726: "[?]" +0x1727: "[?]" +0x1728: "[?]" +0x1729: "[?]" +0x172a: "[?]" +0x172b: "[?]" +0x172c: "[?]" +0x172d: "[?]" +0x172e: "[?]" +0x172f: "[?]" +0x1730: "[?]" +0x1731: "[?]" +0x1732: "[?]" +0x1733: "[?]" +0x1734: "[?]" +0x1735: "[?]" +0x1736: "[?]" +0x1737: "[?]" +0x1738: "[?]" +0x1739: "[?]" +0x173a: "[?]" +0x173b: "[?]" +0x173c: "[?]" +0x173d: "[?]" +0x173e: "[?]" +0x173f: "[?]" +0x1740: "[?]" +0x1741: "[?]" +0x1742: "[?]" +0x1743: "[?]" +0x1744: "[?]" +0x1745: "[?]" +0x1746: "[?]" +0x1747: "[?]" +0x1748: "[?]" +0x1749: "[?]" +0x174a: "[?]" +0x174b: "[?]" +0x174c: "[?]" +0x174d: "[?]" +0x174e: "[?]" +0x174f: "[?]" +0x1750: "[?]" +0x1751: "[?]" +0x1752: "[?]" +0x1753: "[?]" +0x1754: "[?]" +0x1755: "[?]" +0x1756: "[?]" +0x1757: "[?]" +0x1758: "[?]" +0x1759: "[?]" +0x175a: "[?]" +0x175b: "[?]" +0x175c: "[?]" +0x175d: "[?]" +0x175e: "[?]" +0x175f: "[?]" +0x1760: "[?]" +0x1761: "[?]" +0x1762: "[?]" +0x1763: "[?]" +0x1764: "[?]" +0x1765: "[?]" +0x1766: "[?]" +0x1767: "[?]" +0x1768: "[?]" +0x1769: "[?]" +0x176a: "[?]" +0x176b: "[?]" +0x176c: "[?]" +0x176d: "[?]" +0x176e: "[?]" +0x176f: "[?]" +0x1770: "[?]" +0x1771: "[?]" +0x1772: "[?]" +0x1773: "[?]" +0x1774: "[?]" +0x1775: "[?]" +0x1776: "[?]" +0x1777: "[?]" +0x1778: "[?]" +0x1779: "[?]" +0x177a: "[?]" +0x177b: "[?]" +0x177c: "[?]" +0x177d: "[?]" +0x177e: "[?]" +0x177f: "[?]" +0x1780: "k" +0x1781: "kh" +0x1782: "g" +0x1783: "gh" +0x1784: "ng" +0x1785: "c" +0x1786: "ch" +0x1787: "j" +0x1788: "jh" +0x1789: "ny" +0x178a: "t" +0x178b: "tth" +0x178c: "d" +0x178d: "ddh" +0x178e: "nn" +0x178f: "t" +0x1790: "th" +0x1791: "d" +0x1792: "dh" +0x1793: "n" +0x1794: "p" +0x1795: "ph" +0x1796: "b" +0x1797: "bh" +0x1798: "m" +0x1799: "y" +0x179a: "r" +0x179b: "l" +0x179c: "v" +0x179d: "sh" +0x179e: "ss" +0x179f: "s" +0x17a0: "h" +0x17a1: "l" +0x17a2: "q" +0x17a3: "a" +0x17a4: "aa" +0x17a5: "i" +0x17a6: "ii" +0x17a7: "u" +0x17a8: "uk" +0x17a9: "uu" +0x17aa: "uuv" +0x17ab: "ry" +0x17ac: "ryy" +0x17ad: "ly" +0x17ae: "lyy" +0x17af: "e" +0x17b0: "ai" +0x17b1: "oo" +0x17b2: "oo" +0x17b3: "au" +0x17b4: "a" +0x17b5: "aa" +0x17b6: "aa" +0x17b7: "i" +0x17b8: "ii" +0x17b9: "y" +0x17ba: "yy" +0x17bb: "u" +0x17bc: "uu" +0x17bd: "ua" +0x17be: "oe" +0x17bf: "ya" +0x17c0: "ie" +0x17c1: "e" +0x17c2: "ae" +0x17c3: "ai" +0x17c4: "oo" +0x17c5: "au" +0x17c6: "M" +0x17c7: "H" +0x17c8: "a`" +0x17c9: "" +0x17ca: "" +0x17cb: "" +0x17cc: "r" +0x17cd: "" +0x17ce: "!" +0x17cf: "" +0x17d0: "" +0x17d1: "" +0x17d2: "" +0x17d3: "" +0x17d4: "." +0x17d5: " // " +0x17d6: ":" +0x17d7: "+" +0x17d8: "++" +0x17d9: " * " +0x17da: " /// " +0x17db: "KR" +0x17dc: "'" +0x17dd: "[?]" +0x17de: "[?]" +0x17df: "[?]" +0x17e0: "0" +0x17e1: "1" +0x17e2: "2" +0x17e3: "3" +0x17e4: "4" +0x17e5: "5" +0x17e6: "6" +0x17e7: "7" +0x17e8: "8" +0x17e9: "9" +0x17ea: "[?]" +0x17eb: "[?]" +0x17ec: "[?]" +0x17ed: "[?]" +0x17ee: "[?]" +0x17ef: "[?]" +0x17f0: "[?]" +0x17f1: "[?]" +0x17f2: "[?]" +0x17f3: "[?]" +0x17f4: "[?]" +0x17f5: "[?]" +0x17f6: "[?]" +0x17f7: "[?]" +0x17f8: "[?]" +0x17f9: "[?]" +0x17fa: "[?]" +0x17fb: "[?]" +0x17fc: "[?]" +0x17fd: "[?]" +0x17fe: "[?]" +/* x018 */ +0x1800: " @ " +0x1801: " ... " +0x1802: ", " +0x1803: ". " +0x1804: ": " +0x1805: " // " +0x1806: "" +0x1807: "-" +0x1808: ", " +0x1809: ". " +0x180a: "" +0x180b: "" +0x180c: "" +0x180d: "" +0x180e: "" +0x180f: "[?]" +0x1810: "0" +0x1811: "1" +0x1812: "2" +0x1813: "3" +0x1814: "4" +0x1815: "5" +0x1816: "6" +0x1817: "7" +0x1818: "8" +0x1819: "9" +0x181a: "[?]" +0x181b: "[?]" +0x181c: "[?]" +0x181d: "[?]" +0x181e: "[?]" +0x181f: "[?]" +0x1820: "a" +0x1821: "e" +0x1822: "i" +0x1823: "o" +0x1824: "u" +0x1825: "O" +0x1826: "U" +0x1827: "ee" +0x1828: "n" +0x1829: "ng" +0x182a: "b" +0x182b: "p" +0x182c: "q" +0x182d: "g" +0x182e: "m" +0x182f: "l" +0x1830: "s" +0x1831: "sh" +0x1832: "t" +0x1833: "d" +0x1834: "ch" +0x1835: "j" +0x1836: "y" +0x1837: "r" +0x1838: "w" +0x1839: "f" +0x183a: "k" +0x183b: "kha" +0x183c: "ts" +0x183d: "z" +0x183e: "h" +0x183f: "zr" +0x1840: "lh" +0x1841: "zh" +0x1842: "ch" +0x1843: "-" +0x1844: "e" +0x1845: "i" +0x1846: "o" +0x1847: "u" +0x1848: "O" +0x1849: "U" +0x184a: "ng" +0x184b: "b" +0x184c: "p" +0x184d: "q" +0x184e: "g" +0x184f: "m" +0x1850: "t" +0x1851: "d" +0x1852: "ch" +0x1853: "j" +0x1854: "ts" +0x1855: "y" +0x1856: "w" +0x1857: "k" +0x1858: "g" +0x1859: "h" +0x185a: "jy" +0x185b: "ny" +0x185c: "dz" +0x185d: "e" +0x185e: "i" +0x185f: "iy" +0x1860: "U" +0x1861: "u" +0x1862: "ng" +0x1863: "k" +0x1864: "g" +0x1865: "h" +0x1866: "p" +0x1867: "sh" +0x1868: "t" +0x1869: "d" +0x186a: "j" +0x186b: "f" +0x186c: "g" +0x186d: "h" +0x186e: "ts" +0x186f: "z" +0x1870: "r" +0x1871: "ch" +0x1872: "zh" +0x1873: "i" +0x1874: "k" +0x1875: "r" +0x1876: "f" +0x1877: "zh" +0x1878: "[?]" +0x1879: "[?]" +0x187a: "[?]" +0x187b: "[?]" +0x187c: "[?]" +0x187d: "[?]" +0x187e: "[?]" +0x187f: "[?]" +0x1880: "[?]" +0x1881: "H" +0x1882: "X" +0x1883: "W" +0x1884: "M" +0x1885: " 3 " +0x1886: " 333 " +0x1887: "a" +0x1888: "i" +0x1889: "k" +0x188a: "ng" +0x188b: "c" +0x188c: "tt" +0x188d: "tth" +0x188e: "dd" +0x188f: "nn" +0x1890: "t" +0x1891: "d" +0x1892: "p" +0x1893: "ph" +0x1894: "ss" +0x1895: "zh" +0x1896: "z" +0x1897: "a" +0x1898: "t" +0x1899: "zh" +0x189a: "gh" +0x189b: "ng" +0x189c: "c" +0x189d: "jh" +0x189e: "tta" +0x189f: "ddh" +0x18a0: "t" +0x18a1: "dh" +0x18a2: "ss" +0x18a3: "cy" +0x18a4: "zh" +0x18a5: "z" +0x18a6: "u" +0x18a7: "y" +0x18a8: "bh" +0x18a9: "'" +0x18aa: "[?]" +0x18ab: "[?]" +0x18ac: "[?]" +0x18ad: "[?]" +0x18ae: "[?]" +0x18af: "[?]" +0x18b0: "[?]" +0x18b1: "[?]" +0x18b2: "[?]" +0x18b3: "[?]" +0x18b4: "[?]" +0x18b5: "[?]" +0x18b6: "[?]" +0x18b7: "[?]" +0x18b8: "[?]" +0x18b9: "[?]" +0x18ba: "[?]" +0x18bb: "[?]" +0x18bc: "[?]" +0x18bd: "[?]" +0x18be: "[?]" +0x18bf: "[?]" +0x18c0: "[?]" +0x18c1: "[?]" +0x18c2: "[?]" +0x18c3: "[?]" +0x18c4: "[?]" +0x18c5: "[?]" +0x18c6: "[?]" +0x18c7: "[?]" +0x18c8: "[?]" +0x18c9: "[?]" +0x18ca: "[?]" +0x18cb: "[?]" +0x18cc: "[?]" +0x18cd: "[?]" +0x18ce: "[?]" +0x18cf: "[?]" +0x18d0: "[?]" +0x18d1: "[?]" +0x18d2: "[?]" +0x18d3: "[?]" +0x18d4: "[?]" +0x18d5: "[?]" +0x18d6: "[?]" +0x18d7: "[?]" +0x18d8: "[?]" +0x18d9: "[?]" +0x18da: "[?]" +0x18db: "[?]" +0x18dc: "[?]" +0x18dd: "[?]" +0x18de: "[?]" +0x18df: "[?]" +0x18e0: "[?]" +0x18e1: "[?]" +0x18e2: "[?]" +0x18e3: "[?]" +0x18e4: "[?]" +0x18e5: "[?]" +0x18e6: "[?]" +0x18e7: "[?]" +0x18e8: "[?]" +0x18e9: "[?]" +0x18ea: "[?]" +0x18eb: "[?]" +0x18ec: "[?]" +0x18ed: "[?]" +0x18ee: "[?]" +0x18ef: "[?]" +0x18f0: "[?]" +0x18f1: "[?]" +0x18f2: "[?]" +0x18f3: "[?]" +0x18f4: "[?]" +0x18f5: "[?]" +0x18f6: "[?]" +0x18f7: "[?]" +0x18f8: "[?]" +0x18f9: "[?]" +0x18fa: "[?]" +0x18fb: "[?]" +0x18fc: "[?]" +0x18fd: "[?]" +0x18fe: "[?]" +/* x01d */ +0x1d00: "" +0x1d01: "" +0x1d02: "" +0x1d03: "" +0x1d04: "" +0x1d05: "" +0x1d06: "" +0x1d07: "" +0x1d08: "" +0x1d09: "" +0x1d0a: "" +0x1d0b: "" +0x1d0c: "" +0x1d0d: "" +0x1d0e: "" +0x1d0f: "" +0x1d10: "" +0x1d11: "" +0x1d12: "" +0x1d13: "" +0x1d14: "" +0x1d15: "" +0x1d16: "" +0x1d17: "" +0x1d18: "" +0x1d19: "" +0x1d1a: "" +0x1d1b: "" +0x1d1c: "" +0x1d1d: "" +0x1d1e: "" +0x1d1f: "" +0x1d20: "" +0x1d21: "" +0x1d22: "" +0x1d23: "" +0x1d24: "" +0x1d25: "" +0x1d26: "" +0x1d27: "" +0x1d28: "" +0x1d29: "" +0x1d2a: "" +0x1d2b: "" +0x1d2c: "" +0x1d2d: "" +0x1d2e: "" +0x1d2f: "" +0x1d30: "" +0x1d31: "" +0x1d32: "" +0x1d33: "" +0x1d34: "" +0x1d35: "" +0x1d36: "" +0x1d37: "" +0x1d38: "" +0x1d39: "" +0x1d3a: "" +0x1d3b: "" +0x1d3c: "" +0x1d3d: "" +0x1d3e: "" +0x1d3f: "" +0x1d40: "" +0x1d41: "" +0x1d42: "" +0x1d43: "" +0x1d44: "" +0x1d45: "" +0x1d46: "" +0x1d47: "" +0x1d48: "" +0x1d49: "" +0x1d4a: "" +0x1d4b: "" +0x1d4c: "" +0x1d4d: "" +0x1d4e: "" +0x1d4f: "" +0x1d50: "" +0x1d51: "" +0x1d52: "" +0x1d53: "" +0x1d54: "" +0x1d55: "" +0x1d56: "" +0x1d57: "" +0x1d58: "" +0x1d59: "" +0x1d5a: "" +0x1d5b: "" +0x1d5c: "" +0x1d5d: "" +0x1d5e: "" +0x1d5f: "" +0x1d60: "" +0x1d61: "" +0x1d62: "" +0x1d63: "" +0x1d64: "" +0x1d65: "" +0x1d66: "" +0x1d67: "" +0x1d68: "" +0x1d69: "" +0x1d6a: "" +0x1d6b: "" +0x1d6c: "b" +0x1d6d: "d" +0x1d6e: "f" +0x1d6f: "m" +0x1d70: "n" +0x1d71: "p" +0x1d72: "r" +0x1d73: "r" +0x1d74: "s" +0x1d75: "t" +0x1d76: "z" +0x1d77: "g" +0x1d78: "" +0x1d79: "" +0x1d7a: "" +0x1d7b: "" +0x1d7c: "" +0x1d7d: "p" +0x1d7e: "" +0x1d7f: "" +0x1d80: "b" +0x1d81: "d" +0x1d82: "f" +0x1d83: "g" +0x1d84: "k" +0x1d85: "l" +0x1d86: "m" +0x1d87: "n" +0x1d88: "p" +0x1d89: "r" +0x1d8a: "s" +0x1d8b: "" +0x1d8c: "v" +0x1d8d: "x" +0x1d8e: "z" +0x1d8f: "" +0x1d90: "" +0x1d91: "" +0x1d92: "" +0x1d93: "" +0x1d94: "" +0x1d95: "" +0x1d96: "" +0x1d97: "" +0x1d98: "" +0x1d99: "" +0x1d9a: "" +0x1d9b: "" +0x1d9c: "" +0x1d9d: "" +0x1d9e: "" +0x1d9f: "" +0x1da0: "" +0x1da1: "" +0x1da2: "" +0x1da3: "" +0x1da4: "" +0x1da5: "" +0x1da6: "" +0x1da7: "" +0x1da8: "" +0x1da9: "" +0x1daa: "" +0x1dab: "" +0x1dac: "" +0x1dad: "" +0x1dae: "" +0x1daf: "" +0x1db0: "" +0x1db1: "" +0x1db2: "" +0x1db3: "" +0x1db4: "" +0x1db5: "" +0x1db6: "" +0x1db7: "" +0x1db8: "" +0x1db9: "" +0x1dba: "" +0x1dbb: "" +0x1dbc: "" +0x1dbd: "" +0x1dbe: "" +0x1dbf: "" +0x1dc0: "" +0x1dc1: "" +0x1dc2: "" +0x1dc3: "" +0x1dc4: "" +0x1dc5: "" +0x1dc6: "" +0x1dc7: "" +0x1dc8: "" +0x1dc9: "" +0x1dca: "" +0x1dcb: "" +0x1dcc: "" +0x1dcd: "" +0x1dce: "" +0x1dcf: "" +0x1dd0: "" +0x1dd1: "" +0x1dd2: "" +0x1dd3: "" +0x1dd4: "" +0x1dd5: "" +0x1dd6: "" +0x1dd7: "" +0x1dd8: "" +0x1dd9: "" +0x1dda: "" +0x1ddb: "" +0x1ddc: "" +0x1ddd: "" +0x1dde: "" +0x1ddf: "" +0x1de0: "" +0x1de1: "" +0x1de2: "" +0x1de3: "" +0x1de4: "" +0x1de5: "" +0x1de6: "" +0x1de7: "" +0x1de8: "" +0x1de9: "" +0x1dea: "" +0x1deb: "" +0x1dec: "" +0x1ded: "" +0x1dee: "" +0x1def: "" +0x1df0: "" +0x1df1: "" +0x1df2: "" +0x1df3: "" +0x1df4: "" +0x1df5: "" +0x1df6: "" +0x1df7: "" +0x1df8: "" +0x1df9: "" +0x1dfa: "" +0x1dfb: "" +0x1dfc: "" +0x1dfd: "" +0x1dfe: "" +/* x01e */ +0x1e00: "A" +0x1e01: "a" +0x1e02: "B" +0x1e03: "b" +0x1e04: "B" +0x1e05: "b" +0x1e06: "B" +0x1e07: "b" +0x1e08: "C" +0x1e09: "c" +0x1e0a: "D" +0x1e0b: "d" +0x1e0c: "D" +0x1e0d: "d" +0x1e0e: "D" +0x1e0f: "d" +0x1e10: "D" +0x1e11: "d" +0x1e12: "D" +0x1e13: "d" +0x1e14: "E" +0x1e15: "e" +0x1e16: "E" +0x1e17: "e" +0x1e18: "E" +0x1e19: "e" +0x1e1a: "E" +0x1e1b: "e" +0x1e1c: "E" +0x1e1d: "e" +0x1e1e: "F" +0x1e1f: "f" +0x1e20: "G" +0x1e21: "g" +0x1e22: "H" +0x1e23: "h" +0x1e24: "H" +0x1e25: "h" +0x1e26: "H" +0x1e27: "h" +0x1e28: "H" +0x1e29: "h" +0x1e2a: "H" +0x1e2b: "h" +0x1e2c: "I" +0x1e2d: "i" +0x1e2e: "I" +0x1e2f: "i" +0x1e30: "K" +0x1e31: "k" +0x1e32: "K" +0x1e33: "k" +0x1e34: "K" +0x1e35: "k" +0x1e36: "L" +0x1e37: "l" +0x1e38: "L" +0x1e39: "l" +0x1e3a: "L" +0x1e3b: "l" +0x1e3c: "L" +0x1e3d: "l" +0x1e3e: "M" +0x1e3f: "m" +0x1e40: "M" +0x1e41: "m" +0x1e42: "M" +0x1e43: "m" +0x1e44: "N" +0x1e45: "n" +0x1e46: "N" +0x1e47: "n" +0x1e48: "N" +0x1e49: "n" +0x1e4a: "N" +0x1e4b: "n" +0x1e4c: "O" +0x1e4d: "o" +0x1e4e: "O" +0x1e4f: "o" +0x1e50: "O" +0x1e51: "o" +0x1e52: "O" +0x1e53: "o" +0x1e54: "P" +0x1e55: "p" +0x1e56: "P" +0x1e57: "p" +0x1e58: "R" +0x1e59: "r" +0x1e5a: "R" +0x1e5b: "r" +0x1e5c: "R" +0x1e5d: "r" +0x1e5e: "R" +0x1e5f: "r" +0x1e60: "S" +0x1e61: "s" +0x1e62: "S" +0x1e63: "s" +0x1e64: "S" +0x1e65: "s" +0x1e66: "S" +0x1e67: "s" +0x1e68: "S" +0x1e69: "s" +0x1e6a: "T" +0x1e6b: "t" +0x1e6c: "T" +0x1e6d: "t" +0x1e6e: "T" +0x1e6f: "t" +0x1e70: "T" +0x1e71: "t" +0x1e72: "U" +0x1e73: "u" +0x1e74: "U" +0x1e75: "u" +0x1e76: "U" +0x1e77: "u" +0x1e78: "U" +0x1e79: "u" +0x1e7a: "U" +0x1e7b: "u" +0x1e7c: "V" +0x1e7d: "v" +0x1e7e: "V" +0x1e7f: "v" +0x1e80: "W" +0x1e81: "w" +0x1e82: "W" +0x1e83: "w" +0x1e84: "W" +0x1e85: "w" +0x1e86: "W" +0x1e87: "w" +0x1e88: "W" +0x1e89: "w" +0x1e8a: "X" +0x1e8b: "x" +0x1e8c: "X" +0x1e8d: "x" +0x1e8e: "Y" +0x1e8f: "y" +0x1e90: "Z" +0x1e91: "z" +0x1e92: "Z" +0x1e93: "z" +0x1e94: "Z" +0x1e95: "z" +0x1e96: "h" +0x1e97: "t" +0x1e98: "w" +0x1e99: "y" +0x1e9a: "a" +0x1e9b: "S" +0x1e9c: "[?]" +0x1e9d: "[?]" +0x1e9e: "Ss" +0x1e9f: "[?]" +0x1ea0: "A" +0x1ea1: "a" +0x1ea2: "A" +0x1ea3: "a" +0x1ea4: "A" +0x1ea5: "a" +0x1ea6: "A" +0x1ea7: "a" +0x1ea8: "A" +0x1ea9: "a" +0x1eaa: "A" +0x1eab: "a" +0x1eac: "A" +0x1ead: "a" +0x1eae: "A" +0x1eaf: "a" +0x1eb0: "A" +0x1eb1: "a" +0x1eb2: "A" +0x1eb3: "a" +0x1eb4: "A" +0x1eb5: "a" +0x1eb6: "A" +0x1eb7: "a" +0x1eb8: "E" +0x1eb9: "e" +0x1eba: "E" +0x1ebb: "e" +0x1ebc: "E" +0x1ebd: "e" +0x1ebe: "E" +0x1ebf: "e" +0x1ec0: "E" +0x1ec1: "e" +0x1ec2: "E" +0x1ec3: "e" +0x1ec4: "E" +0x1ec5: "e" +0x1ec6: "E" +0x1ec7: "e" +0x1ec8: "I" +0x1ec9: "i" +0x1eca: "I" +0x1ecb: "i" +0x1ecc: "O" +0x1ecd: "o" +0x1ece: "O" +0x1ecf: "o" +0x1ed0: "O" +0x1ed1: "o" +0x1ed2: "O" +0x1ed3: "o" +0x1ed4: "O" +0x1ed5: "o" +0x1ed6: "O" +0x1ed7: "o" +0x1ed8: "O" +0x1ed9: "o" +0x1eda: "O" +0x1edb: "o" +0x1edc: "O" +0x1edd: "o" +0x1ede: "O" +0x1edf: "o" +0x1ee0: "O" +0x1ee1: "o" +0x1ee2: "O" +0x1ee3: "o" +0x1ee4: "U" +0x1ee5: "u" +0x1ee6: "U" +0x1ee7: "u" +0x1ee8: "U" +0x1ee9: "u" +0x1eea: "U" +0x1eeb: "u" +0x1eec: "U" +0x1eed: "u" +0x1eee: "U" +0x1eef: "u" +0x1ef0: "U" +0x1ef1: "u" +0x1ef2: "Y" +0x1ef3: "y" +0x1ef4: "Y" +0x1ef5: "y" +0x1ef6: "Y" +0x1ef7: "y" +0x1ef8: "Y" +0x1ef9: "y" +0x1efa: "[?]" +0x1efb: "[?]" +0x1efc: "[?]" +0x1efd: "[?]" +0x1efe: "[?]" +/* x01f */ +0x1f00: "a" +0x1f01: "a" +0x1f02: "a" +0x1f03: "a" +0x1f04: "a" +0x1f05: "a" +0x1f06: "a" +0x1f07: "a" +0x1f08: "A" +0x1f09: "A" +0x1f0a: "A" +0x1f0b: "A" +0x1f0c: "A" +0x1f0d: "A" +0x1f0e: "A" +0x1f0f: "A" +0x1f10: "e" +0x1f11: "e" +0x1f12: "e" +0x1f13: "e" +0x1f14: "e" +0x1f15: "e" +0x1f16: "[?]" +0x1f17: "[?]" +0x1f18: "E" +0x1f19: "E" +0x1f1a: "E" +0x1f1b: "E" +0x1f1c: "E" +0x1f1d: "E" +0x1f1e: "[?]" +0x1f1f: "[?]" +0x1f20: "e" +0x1f21: "e" +0x1f22: "e" +0x1f23: "e" +0x1f24: "e" +0x1f25: "e" +0x1f26: "e" +0x1f27: "e" +0x1f28: "E" +0x1f29: "E" +0x1f2a: "E" +0x1f2b: "E" +0x1f2c: "E" +0x1f2d: "E" +0x1f2e: "E" +0x1f2f: "E" +0x1f30: "i" +0x1f31: "i" +0x1f32: "i" +0x1f33: "i" +0x1f34: "i" +0x1f35: "i" +0x1f36: "i" +0x1f37: "i" +0x1f38: "I" +0x1f39: "I" +0x1f3a: "I" +0x1f3b: "I" +0x1f3c: "I" +0x1f3d: "I" +0x1f3e: "I" +0x1f3f: "I" +0x1f40: "o" +0x1f41: "o" +0x1f42: "o" +0x1f43: "o" +0x1f44: "o" +0x1f45: "o" +0x1f46: "[?]" +0x1f47: "[?]" +0x1f48: "O" +0x1f49: "O" +0x1f4a: "O" +0x1f4b: "O" +0x1f4c: "O" +0x1f4d: "O" +0x1f4e: "[?]" +0x1f4f: "[?]" +0x1f50: "u" +0x1f51: "u" +0x1f52: "u" +0x1f53: "u" +0x1f54: "u" +0x1f55: "u" +0x1f56: "u" +0x1f57: "u" +0x1f58: "[?]" +0x1f59: "U" +0x1f5a: "[?]" +0x1f5b: "U" +0x1f5c: "[?]" +0x1f5d: "U" +0x1f5e: "[?]" +0x1f5f: "U" +0x1f60: "o" +0x1f61: "o" +0x1f62: "o" +0x1f63: "o" +0x1f64: "o" +0x1f65: "o" +0x1f66: "o" +0x1f67: "o" +0x1f68: "O" +0x1f69: "O" +0x1f6a: "O" +0x1f6b: "O" +0x1f6c: "O" +0x1f6d: "O" +0x1f6e: "O" +0x1f6f: "O" +0x1f70: "a" +0x1f71: "a" +0x1f72: "e" +0x1f73: "e" +0x1f74: "e" +0x1f75: "e" +0x1f76: "i" +0x1f77: "i" +0x1f78: "o" +0x1f79: "o" +0x1f7a: "u" +0x1f7b: "u" +0x1f7c: "o" +0x1f7d: "o" +0x1f7e: "[?]" +0x1f7f: "[?]" +0x1f80: "a" +0x1f81: "a" +0x1f82: "a" +0x1f83: "a" +0x1f84: "a" +0x1f85: "a" +0x1f86: "a" +0x1f87: "a" +0x1f88: "A" +0x1f89: "A" +0x1f8a: "A" +0x1f8b: "A" +0x1f8c: "A" +0x1f8d: "A" +0x1f8e: "A" +0x1f8f: "A" +0x1f90: "e" +0x1f91: "e" +0x1f92: "e" +0x1f93: "e" +0x1f94: "e" +0x1f95: "e" +0x1f96: "e" +0x1f97: "e" +0x1f98: "E" +0x1f99: "E" +0x1f9a: "E" +0x1f9b: "E" +0x1f9c: "E" +0x1f9d: "E" +0x1f9e: "E" +0x1f9f: "E" +0x1fa0: "o" +0x1fa1: "o" +0x1fa2: "o" +0x1fa3: "o" +0x1fa4: "o" +0x1fa5: "o" +0x1fa6: "o" +0x1fa7: "o" +0x1fa8: "O" +0x1fa9: "O" +0x1faa: "O" +0x1fab: "O" +0x1fac: "O" +0x1fad: "O" +0x1fae: "O" +0x1faf: "O" +0x1fb0: "a" +0x1fb1: "a" +0x1fb2: "a" +0x1fb3: "a" +0x1fb4: "a" +0x1fb5: "[?]" +0x1fb6: "a" +0x1fb7: "a" +0x1fb8: "A" +0x1fb9: "A" +0x1fba: "A" +0x1fbb: "A" +0x1fbc: "A" +0x1fbd: "'" +0x1fbe: "i" +0x1fbf: "'" +0x1fc0: "~" +0x1fc1: "\"~" +0x1fc2: "e" +0x1fc3: "e" +0x1fc4: "e" +0x1fc5: "[?]" +0x1fc6: "e" +0x1fc7: "e" +0x1fc8: "E" +0x1fc9: "E" +0x1fca: "E" +0x1fcb: "E" +0x1fcc: "E" +0x1fcd: "'`" +0x1fce: "''" +0x1fcf: "'~" +0x1fd0: "i" +0x1fd1: "i" +0x1fd2: "i" +0x1fd3: "i" +0x1fd4: "[?]" +0x1fd5: "[?]" +0x1fd6: "i" +0x1fd7: "i" +0x1fd8: "I" +0x1fd9: "I" +0x1fda: "I" +0x1fdb: "I" +0x1fdc: "[?]" +0x1fdd: "`'" +0x1fde: "`'" +0x1fdf: "`~" +0x1fe0: "u" +0x1fe1: "u" +0x1fe2: "u" +0x1fe3: "u" +0x1fe4: "R" +0x1fe5: "R" +0x1fe6: "u" +0x1fe7: "u" +0x1fe8: "U" +0x1fe9: "U" +0x1fea: "U" +0x1feb: "U" +0x1fec: "R" +0x1fed: "\"`" +0x1fee: "\"'" +0x1fef: "`" +0x1ff0: "[?]" +0x1ff1: "[?]" +0x1ff2: "o" +0x1ff3: "o" +0x1ff4: "o" +0x1ff5: "[?]" +0x1ff6: "o" +0x1ff7: "o" +0x1ff8: "O" +0x1ff9: "O" +0x1ffa: "O" +0x1ffb: "O" +0x1ffc: "O" +0x1ffd: "'" +0x1ffe: "`" +/* x020 */ +0x2000: " " +0x2001: " " +0x2002: " " +0x2003: " " +0x2004: " " +0x2005: " " +0x2006: " " +0x2007: " " +0x2008: " " +0x2009: " " +0x200a: " " +0x200b: " " +0x200c: "" +0x200d: "" +0x200e: "" +0x200f: "" +0x2010: "-" +0x2011: "-" +0x2012: "-" +0x2013: "-" +0x2014: "--" +0x2015: "--" +0x2016: "||" +0x2017: "_" +0x2018: "'" +0x2019: "'" +0x201a: "," +0x201b: "'" +0x201c: "\"" +0x201d: "\"" +0x201e: ",," +0x201f: "\"" +0x2020: "+" +0x2021: "++" +0x2022: "*" +0x2023: "*>" +0x2024: "." +0x2025: ".." +0x2026: "..." +0x2027: "." +0x2028: "\n" +0x2029: "\n\n" +0x202a: "" +0x202b: "" +0x202c: "" +0x202d: "" +0x202e: "" +0x202f: " " +0x2030: "%0" +0x2031: "%00" +0x2032: "'" +0x2033: "''" +0x2034: "'''" +0x2035: "`" +0x2036: "``" +0x2037: "```" +0x2038: "^" +0x2039: "<" +0x203a: ">" +0x203b: "*" +0x203c: "!!" +0x203d: "!?" +0x203e: "-" +0x203f: "_" +0x2040: "-" +0x2041: "^" +0x2042: "***" +0x2043: "--" +0x2044: "/" +0x2045: "-[" +0x2046: "]-" +0x2047: "[?]" +0x2048: "?!" +0x2049: "!?" +0x204a: "7" +0x204b: "PP" +0x204c: "(]" +0x204d: "[)" +0x204e: "[?]" +0x204f: "[?]" +0x2050: "[?]" +0x2051: "[?]" +0x2052: "[?]" +0x2053: "[?]" +0x2054: "[?]" +0x2055: "[?]" +0x2056: "[?]" +0x2057: "[?]" +0x2058: "[?]" +0x2059: "[?]" +0x205a: "[?]" +0x205b: "[?]" +0x205c: "[?]" +0x205d: "[?]" +0x205e: "[?]" +0x205f: "[?]" +0x2060: "[?]" +0x2061: "[?]" +0x2062: "[?]" +0x2063: "[?]" +0x2064: "[?]" +0x2065: "[?]" +0x2066: "[?]" +0x2067: "[?]" +0x2068: "[?]" +0x2069: "[?]" +0x206a: "" +0x206b: "" +0x206c: "" +0x206d: "" +0x206e: "" +0x206f: "" +0x2070: "0" +0x2071: "" +0x2072: "" +0x2073: "" +0x2074: "4" +0x2075: "5" +0x2076: "6" +0x2077: "7" +0x2078: "8" +0x2079: "9" +0x207a: "+" +0x207b: "-" +0x207c: "=" +0x207d: "(" +0x207e: ")" +0x207f: "n" +0x2080: "0" +0x2081: "1" +0x2082: "2" +0x2083: "3" +0x2084: "4" +0x2085: "5" +0x2086: "6" +0x2087: "7" +0x2088: "8" +0x2089: "9" +0x208a: "+" +0x208b: "-" +0x208c: "=" +0x208d: "(" +0x208e: ")" +0x208f: "[?]" +0x2090: "[?]" +0x2091: "[?]" +0x2092: "[?]" +0x2093: "[?]" +0x2094: "[?]" +0x2095: "[?]" +0x2096: "[?]" +0x2097: "[?]" +0x2098: "[?]" +0x2099: "[?]" +0x209a: "[?]" +0x209b: "[?]" +0x209c: "[?]" +0x209d: "[?]" +0x209e: "[?]" +0x209f: "[?]" +0x20a0: "ECU" +0x20a1: "CL" +0x20a2: "Cr" +0x20a3: "FF" +0x20a4: "L" +0x20a5: "mil" +0x20a6: "N" +0x20a7: "Pts" +0x20a8: "Rs" +0x20a9: "W" +0x20aa: "NS" +0x20ab: "D" +0x20ac: "EU" +0x20ad: "K" +0x20ae: "T" +0x20af: "Dr" +0x20b0: "[?]" +0x20b1: "[?]" +0x20b2: "[?]" +0x20b3: "[?]" +0x20b4: "[?]" +0x20b5: "[?]" +0x20b6: "[?]" +0x20b7: "[?]" +0x20b8: "[?]" +0x20b9: "[?]" +0x20ba: "[?]" +0x20bb: "[?]" +0x20bc: "[?]" +0x20bd: "[?]" +0x20be: "[?]" +0x20bf: "[?]" +0x20c0: "[?]" +0x20c1: "[?]" +0x20c2: "[?]" +0x20c3: "[?]" +0x20c4: "[?]" +0x20c5: "[?]" +0x20c6: "[?]" +0x20c7: "[?]" +0x20c8: "[?]" +0x20c9: "[?]" +0x20ca: "[?]" +0x20cb: "[?]" +0x20cc: "[?]" +0x20cd: "[?]" +0x20ce: "[?]" +0x20cf: "[?]" +0x20d0: "" +0x20d1: "" +0x20d2: "" +0x20d3: "" +0x20d4: "" +0x20d5: "" +0x20d6: "" +0x20d7: "" +0x20d8: "" +0x20d9: "" +0x20da: "" +0x20db: "" +0x20dc: "" +0x20dd: "" +0x20de: "" +0x20df: "" +0x20e0: "" +0x20e1: "" +0x20e2: "" +0x20e3: "" +0x20e4: "[?]" +0x20e5: "[?]" +0x20e6: "[?]" +0x20e7: "[?]" +0x20e8: "[?]" +0x20e9: "[?]" +0x20ea: "[?]" +0x20eb: "[?]" +0x20ec: "[?]" +0x20ed: "[?]" +0x20ee: "[?]" +0x20ef: "[?]" +0x20f0: "[?]" +0x20f1: "[?]" +0x20f2: "[?]" +0x20f3: "[?]" +0x20f4: "[?]" +0x20f5: "[?]" +0x20f6: "[?]" +0x20f7: "[?]" +0x20f8: "[?]" +0x20f9: "[?]" +0x20fa: "[?]" +0x20fb: "[?]" +0x20fc: "[?]" +0x20fd: "[?]" +0x20fe: "[?]" +/* x021 */ +0x2100: "" +0x2101: "" +0x2102: "" +0x2103: "" +0x2104: "" +0x2105: "" +0x2106: "" +0x2107: "" +0x2108: "" +0x2109: "" +0x210a: "" +0x210b: "" +0x210c: "" +0x210d: "" +0x210e: "" +0x210f: "" +0x2110: "" +0x2111: "" +0x2112: "" +0x2113: "" +0x2114: "" +0x2115: "" +0x2116: "" +0x2117: "" +0x2118: "" +0x2119: "" +0x211a: "" +0x211b: "" +0x211c: "" +0x211d: "" +0x211e: "" +0x211f: "" +0x2120: "" +0x2121: "" +0x2122: "" +0x2123: "" +0x2124: "" +0x2125: "" +0x2126: "" +0x2127: "" +0x2128: "" +0x2129: "" +0x212a: "K" +0x212b: "A" +0x212c: "" +0x212d: "" +0x212e: "" +0x212f: "" +0x2130: "" +0x2131: "" +0x2132: "F" +0x2133: "" +0x2134: "" +0x2135: "" +0x2136: "" +0x2137: "" +0x2138: "" +0x2139: "" +0x213a: "" +0x213b: "[?]" +0x213c: "[?]" +0x213d: "[?]" +0x213e: "[?]" +0x213f: "[?]" +0x2140: "[?]" +0x2141: "[?]" +0x2142: "[?]" +0x2143: "[?]" +0x2144: "[?]" +0x2145: "[?]" +0x2146: "[?]" +0x2147: "[?]" +0x2148: "[?]" +0x2149: "[?]" +0x214a: "[?]" +0x214b: "[?]" +0x214c: "[?]" +0x214d: "[?]" +0x214e: "F" +0x214f: "[?]" +0x2150: "[?]" +0x2151: "[?]" +0x2152: "[?]" +0x2153: " 1/3 " +0x2154: " 2/3 " +0x2155: " 1/5 " +0x2156: " 2/5 " +0x2157: " 3/5 " +0x2158: " 4/5 " +0x2159: " 1/6 " +0x215a: " 5/6 " +0x215b: " 1/8 " +0x215c: " 3/8 " +0x215d: " 5/8 " +0x215e: " 7/8 " +0x215f: " 1/" +0x2160: "I" +0x2161: "II" +0x2162: "III" +0x2163: "IV" +0x2164: "V" +0x2165: "VI" +0x2166: "VII" +0x2167: "VIII" +0x2168: "IX" +0x2169: "X" +0x216a: "XI" +0x216b: "XII" +0x216c: "L" +0x216d: "C" +0x216e: "D" +0x216f: "M" +0x2170: "i" +0x2171: "ii" +0x2172: "iii" +0x2173: "iv" +0x2174: "v" +0x2175: "vi" +0x2176: "vii" +0x2177: "viii" +0x2178: "ix" +0x2179: "x" +0x217a: "xi" +0x217b: "xii" +0x217c: "l" +0x217d: "c" +0x217e: "d" +0x217f: "m" +0x2180: "(D" +0x2181: "D)" +0x2182: "((|))" +0x2183: ")" +0x2184: "[?]" +0x2185: "[?]" +0x2186: "[?]" +0x2187: "[?]" +0x2188: "[?]" +0x2189: "[?]" +0x218a: "[?]" +0x218b: "[?]" +0x218c: "[?]" +0x218d: "[?]" +0x218e: "[?]" +0x218f: "[?]" +0x2190: "-" +0x2191: "|" +0x2192: "-" +0x2193: "|" +0x2194: "-" +0x2195: "|" +0x2196: "\\" +0x2197: "/" +0x2198: "\\" +0x2199: "/" +0x219a: "-" +0x219b: "-" +0x219c: "~" +0x219d: "~" +0x219e: "-" +0x219f: "|" +0x21a0: "-" +0x21a1: "|" +0x21a2: "-" +0x21a3: "-" +0x21a4: "-" +0x21a5: "|" +0x21a6: "-" +0x21a7: "|" +0x21a8: "|" +0x21a9: "-" +0x21aa: "-" +0x21ab: "-" +0x21ac: "-" +0x21ad: "-" +0x21ae: "-" +0x21af: "|" +0x21b0: "|" +0x21b1: "|" +0x21b2: "|" +0x21b3: "|" +0x21b4: "|" +0x21b5: "|" +0x21b6: "^" +0x21b7: "V" +0x21b8: "\\" +0x21b9: "=" +0x21ba: "V" +0x21bb: "^" +0x21bc: "-" +0x21bd: "-" +0x21be: "|" +0x21bf: "|" +0x21c0: "-" +0x21c1: "-" +0x21c2: "|" +0x21c3: "|" +0x21c4: "=" +0x21c5: "|" +0x21c6: "=" +0x21c7: "=" +0x21c8: "|" +0x21c9: "=" +0x21ca: "|" +0x21cb: "=" +0x21cc: "=" +0x21cd: "=" +0x21ce: "=" +0x21cf: "=" +0x21d0: "=" +0x21d1: "|" +0x21d2: "=" +0x21d3: "|" +0x21d4: "=" +0x21d5: "|" +0x21d6: "\\" +0x21d7: "/" +0x21d8: "\\" +0x21d9: "/" +0x21da: "=" +0x21db: "=" +0x21dc: "~" +0x21dd: "~" +0x21de: "|" +0x21df: "|" +0x21e0: "-" +0x21e1: "|" +0x21e2: "-" +0x21e3: "|" +0x21e4: "-" +0x21e5: "-" +0x21e6: "-" +0x21e7: "|" +0x21e8: "-" +0x21e9: "|" +0x21ea: "|" +0x21eb: "|" +0x21ec: "|" +0x21ed: "|" +0x21ee: "|" +0x21ef: "|" +0x21f0: "-" +0x21f1: "\\" +0x21f2: "\\" +0x21f3: "|" +0x21f4: "[?]" +0x21f5: "[?]" +0x21f6: "[?]" +0x21f7: "[?]" +0x21f8: "[?]" +0x21f9: "[?]" +0x21fa: "[?]" +0x21fb: "[?]" +0x21fc: "[?]" +0x21fd: "[?]" +0x21fe: "[?]" +/* x022 */ +0x2200: "[?]" +0x2201: "[?]" +0x2202: "[?]" +0x2203: "[?]" +0x2204: "[?]" +0x2205: "[?]" +0x2206: "[?]" +0x2207: "[?]" +0x2208: "[?]" +0x2209: "[?]" +0x220a: "[?]" +0x220b: "[?]" +0x220c: "[?]" +0x220d: "[?]" +0x220e: "[?]" +0x220f: "[?]" +0x2210: "[?]" +0x2211: "[?]" +0x2212: "[?]" +0x2213: "[?]" +0x2214: "[?]" +0x2215: "[?]" +0x2216: "[?]" +0x2217: "[?]" +0x2218: "[?]" +0x2219: "[?]" +0x221a: "[?]" +0x221b: "[?]" +0x221c: "[?]" +0x221d: "[?]" +0x221e: "[?]" +0x221f: "[?]" +0x2220: "[?]" +0x2221: "[?]" +0x2222: "[?]" +0x2223: "[?]" +0x2224: "[?]" +0x2225: "[?]" +0x2226: "[?]" +0x2227: "[?]" +0x2228: "[?]" +0x2229: "[?]" +0x222a: "[?]" +0x222b: "[?]" +0x222c: "[?]" +0x222d: "[?]" +0x222e: "[?]" +0x222f: "[?]" +0x2230: "[?]" +0x2231: "[?]" +0x2232: "[?]" +0x2233: "[?]" +0x2234: "[?]" +0x2235: "[?]" +0x2236: "[?]" +0x2237: "[?]" +0x2238: "[?]" +0x2239: "[?]" +0x223a: "[?]" +0x223b: "[?]" +0x223c: "[?]" +0x223d: "[?]" +0x223e: "[?]" +0x223f: "[?]" +0x2240: "[?]" +0x2241: "[?]" +0x2242: "[?]" +0x2243: "[?]" +0x2244: "[?]" +0x2245: "[?]" +0x2246: "[?]" +0x2247: "[?]" +0x2248: "[?]" +0x2249: "[?]" +0x224a: "[?]" +0x224b: "[?]" +0x224c: "[?]" +0x224d: "[?]" +0x224e: "[?]" +0x224f: "[?]" +0x2250: "[?]" +0x2251: "[?]" +0x2252: "[?]" +0x2253: "[?]" +0x2254: "[?]" +0x2255: "[?]" +0x2256: "[?]" +0x2257: "[?]" +0x2258: "[?]" +0x2259: "[?]" +0x225a: "[?]" +0x225b: "[?]" +0x225c: "[?]" +0x225d: "[?]" +0x225e: "[?]" +0x225f: "[?]" +0x2260: "[?]" +0x2261: "[?]" +0x2262: "[?]" +0x2263: "[?]" +0x2264: "[?]" +0x2265: "[?]" +0x2266: "[?]" +0x2267: "[?]" +0x2268: "[?]" +0x2269: "[?]" +0x226a: "[?]" +0x226b: "[?]" +0x226c: "[?]" +0x226d: "[?]" +0x226e: "[?]" +0x226f: "[?]" +0x2270: "[?]" +0x2271: "[?]" +0x2272: "[?]" +0x2273: "[?]" +0x2274: "[?]" +0x2275: "[?]" +0x2276: "[?]" +0x2277: "[?]" +0x2278: "[?]" +0x2279: "[?]" +0x227a: "[?]" +0x227b: "[?]" +0x227c: "[?]" +0x227d: "[?]" +0x227e: "[?]" +0x227f: "[?]" +0x2280: "[?]" +0x2281: "[?]" +0x2282: "[?]" +0x2283: "[?]" +0x2284: "[?]" +0x2285: "[?]" +0x2286: "[?]" +0x2287: "[?]" +0x2288: "[?]" +0x2289: "[?]" +0x228a: "[?]" +0x228b: "[?]" +0x228c: "[?]" +0x228d: "[?]" +0x228e: "[?]" +0x228f: "[?]" +0x2290: "[?]" +0x2291: "[?]" +0x2292: "[?]" +0x2293: "[?]" +0x2294: "[?]" +0x2295: "[?]" +0x2296: "[?]" +0x2297: "[?]" +0x2298: "[?]" +0x2299: "[?]" +0x229a: "[?]" +0x229b: "[?]" +0x229c: "[?]" +0x229d: "[?]" +0x229e: "[?]" +0x229f: "[?]" +0x22a0: "[?]" +0x22a1: "[?]" +0x22a2: "[?]" +0x22a3: "[?]" +0x22a4: "[?]" +0x22a5: "[?]" +0x22a6: "[?]" +0x22a7: "[?]" +0x22a8: "[?]" +0x22a9: "[?]" +0x22aa: "[?]" +0x22ab: "[?]" +0x22ac: "[?]" +0x22ad: "[?]" +0x22ae: "[?]" +0x22af: "[?]" +0x22b0: "[?]" +0x22b1: "[?]" +0x22b2: "[?]" +0x22b3: "[?]" +0x22b4: "[?]" +0x22b5: "[?]" +0x22b6: "[?]" +0x22b7: "[?]" +0x22b8: "[?]" +0x22b9: "[?]" +0x22ba: "[?]" +0x22bb: "[?]" +0x22bc: "[?]" +0x22bd: "[?]" +0x22be: "[?]" +0x22bf: "[?]" +0x22c0: "[?]" +0x22c1: "[?]" +0x22c2: "[?]" +0x22c3: "[?]" +0x22c4: "[?]" +0x22c5: "[?]" +0x22c6: "[?]" +0x22c7: "[?]" +0x22c8: "[?]" +0x22c9: "[?]" +0x22ca: "[?]" +0x22cb: "[?]" +0x22cc: "[?]" +0x22cd: "[?]" +0x22ce: "[?]" +0x22cf: "[?]" +0x22d0: "[?]" +0x22d1: "[?]" +0x22d2: "[?]" +0x22d3: "[?]" +0x22d4: "[?]" +0x22d5: "[?]" +0x22d6: "[?]" +0x22d7: "[?]" +0x22d8: "[?]" +0x22d9: "[?]" +0x22da: "[?]" +0x22db: "[?]" +0x22dc: "[?]" +0x22dd: "[?]" +0x22de: "[?]" +0x22df: "[?]" +0x22e0: "[?]" +0x22e1: "[?]" +0x22e2: "[?]" +0x22e3: "[?]" +0x22e4: "[?]" +0x22e5: "[?]" +0x22e6: "[?]" +0x22e7: "[?]" +0x22e8: "[?]" +0x22e9: "[?]" +0x22ea: "[?]" +0x22eb: "[?]" +0x22ec: "[?]" +0x22ed: "[?]" +0x22ee: "[?]" +0x22ef: "[?]" +0x22f0: "[?]" +0x22f1: "[?]" +0x22f2: "[?]" +0x22f3: "[?]" +0x22f4: "[?]" +0x22f5: "[?]" +0x22f6: "[?]" +0x22f7: "[?]" +0x22f8: "[?]" +0x22f9: "[?]" +0x22fa: "[?]" +0x22fb: "[?]" +0x22fc: "[?]" +0x22fd: "[?]" +0x22fe: "[?]" +/* x023 */ +0x2300: "[?]" +0x2301: "[?]" +0x2302: "[?]" +0x2303: "[?]" +0x2304: "[?]" +0x2305: "[?]" +0x2306: "[?]" +0x2307: "[?]" +0x2308: "[?]" +0x2309: "[?]" +0x230a: "[?]" +0x230b: "[?]" +0x230c: "[?]" +0x230d: "[?]" +0x230e: "[?]" +0x230f: "[?]" +0x2310: "[?]" +0x2311: "[?]" +0x2312: "[?]" +0x2313: "[?]" +0x2314: "[?]" +0x2315: "[?]" +0x2316: "[?]" +0x2317: "[?]" +0x2318: "[?]" +0x2319: "[?]" +0x231a: "[?]" +0x231b: "[?]" +0x231c: "[?]" +0x231d: "[?]" +0x231e: "[?]" +0x231f: "[?]" +0x2320: "[?]" +0x2321: "[?]" +0x2322: "[?]" +0x2323: "[?]" +0x2324: "[?]" +0x2325: "[?]" +0x2326: "[?]" +0x2327: "[?]" +0x2328: "[?]" +0x2329: "[?]" +0x232a: "[?]" +0x232b: "[?]" +0x232c: "[?]" +0x232d: "[?]" +0x232e: "[?]" +0x232f: "[?]" +0x2330: "[?]" +0x2331: "[?]" +0x2332: "[?]" +0x2333: "[?]" +0x2334: "[?]" +0x2335: "[?]" +0x2336: "[?]" +0x2337: "[?]" +0x2338: "[?]" +0x2339: "[?]" +0x233a: "[?]" +0x233b: "[?]" +0x233c: "[?]" +0x233d: "[?]" +0x233e: "[?]" +0x233f: "[?]" +0x2340: "[?]" +0x2341: "[?]" +0x2342: "[?]" +0x2343: "[?]" +0x2344: "[?]" +0x2345: "[?]" +0x2346: "[?]" +0x2347: "[?]" +0x2348: "[?]" +0x2349: "[?]" +0x234a: "[?]" +0x234b: "[?]" +0x234c: "[?]" +0x234d: "[?]" +0x234e: "[?]" +0x234f: "[?]" +0x2350: "[?]" +0x2351: "[?]" +0x2352: "[?]" +0x2353: "[?]" +0x2354: "[?]" +0x2355: "[?]" +0x2356: "[?]" +0x2357: "[?]" +0x2358: "[?]" +0x2359: "[?]" +0x235a: "[?]" +0x235b: "[?]" +0x235c: "[?]" +0x235d: "[?]" +0x235e: "[?]" +0x235f: "[?]" +0x2360: "[?]" +0x2361: "[?]" +0x2362: "[?]" +0x2363: "[?]" +0x2364: "[?]" +0x2365: "[?]" +0x2366: "[?]" +0x2367: "[?]" +0x2368: "[?]" +0x2369: "[?]" +0x236a: "[?]" +0x236b: "[?]" +0x236c: "[?]" +0x236d: "[?]" +0x236e: "[?]" +0x236f: "[?]" +0x2370: "[?]" +0x2371: "[?]" +0x2372: "[?]" +0x2373: "[?]" +0x2374: "[?]" +0x2375: "[?]" +0x2376: "[?]" +0x2377: "[?]" +0x2378: "[?]" +0x2379: "[?]" +0x237a: "[?]" +0x237b: "[?]" +0x237c: "[?]" +0x237d: "[?]" +0x237e: "[?]" +0x237f: "[?]" +0x2380: "[?]" +0x2381: "[?]" +0x2382: "[?]" +0x2383: "[?]" +0x2384: "[?]" +0x2385: "[?]" +0x2386: "[?]" +0x2387: "[?]" +0x2388: "[?]" +0x2389: "[?]" +0x238a: "[?]" +0x238b: "[?]" +0x238c: "[?]" +0x238d: "[?]" +0x238e: "[?]" +0x238f: "[?]" +0x2390: "[?]" +0x2391: "[?]" +0x2392: "[?]" +0x2393: "[?]" +0x2394: "[?]" +0x2395: "[?]" +0x2396: "[?]" +0x2397: "[?]" +0x2398: "[?]" +0x2399: "[?]" +0x239a: "[?]" +0x239b: "[?]" +0x239c: "[?]" +0x239d: "[?]" +0x239e: "[?]" +0x239f: "[?]" +0x23a0: "[?]" +0x23a1: "[?]" +0x23a2: "[?]" +0x23a3: "[?]" +0x23a4: "[?]" +0x23a5: "[?]" +0x23a6: "[?]" +0x23a7: "[?]" +0x23a8: "[?]" +0x23a9: "[?]" +0x23aa: "[?]" +0x23ab: "[?]" +0x23ac: "[?]" +0x23ad: "[?]" +0x23ae: "[?]" +0x23af: "[?]" +0x23b0: "[?]" +0x23b1: "[?]" +0x23b2: "[?]" +0x23b3: "[?]" +0x23b4: "[?]" +0x23b5: "[?]" +0x23b6: "[?]" +0x23b7: "[?]" +0x23b8: "[?]" +0x23b9: "[?]" +0x23ba: "[?]" +0x23bb: "[?]" +0x23bc: "[?]" +0x23bd: "[?]" +0x23be: "[?]" +0x23bf: "[?]" +0x23c0: "[?]" +0x23c1: "[?]" +0x23c2: "[?]" +0x23c3: "[?]" +0x23c4: "[?]" +0x23c5: "[?]" +0x23c6: "[?]" +0x23c7: "[?]" +0x23c8: "[?]" +0x23c9: "[?]" +0x23ca: "[?]" +0x23cb: "[?]" +0x23cc: "[?]" +0x23cd: "[?]" +0x23ce: "[?]" +0x23cf: "[?]" +0x23d0: "[?]" +0x23d1: "[?]" +0x23d2: "[?]" +0x23d3: "[?]" +0x23d4: "[?]" +0x23d5: "[?]" +0x23d6: "[?]" +0x23d7: "[?]" +0x23d8: "[?]" +0x23d9: "[?]" +0x23da: "[?]" +0x23db: "[?]" +0x23dc: "[?]" +0x23dd: "[?]" +0x23de: "[?]" +0x23df: "[?]" +0x23e0: "[?]" +0x23e1: "[?]" +0x23e2: "[?]" +0x23e3: "[?]" +0x23e4: "[?]" +0x23e5: "[?]" +0x23e6: "[?]" +0x23e7: "[?]" +0x23e8: "[?]" +0x23e9: "[?]" +0x23ea: "[?]" +0x23eb: "[?]" +0x23ec: "[?]" +0x23ed: "[?]" +0x23ee: "[?]" +0x23ef: "[?]" +0x23f0: "[?]" +0x23f1: "[?]" +0x23f2: "[?]" +0x23f3: "[?]" +0x23f4: "[?]" +0x23f5: "[?]" +0x23f6: "[?]" +0x23f7: "[?]" +0x23f8: "[?]" +0x23f9: "[?]" +0x23fa: "[?]" +0x23fb: "[?]" +0x23fc: "[?]" +0x23fd: "[?]" +0x23fe: "[?]" +/* x024 */ +0x2400: "" +0x2401: "" +0x2402: "" +0x2403: "" +0x2404: "" +0x2405: "" +0x2406: "" +0x2407: "" +0x2408: "" +0x2409: "" +0x240a: "" +0x240b: "" +0x240c: "" +0x240d: "" +0x240e: "" +0x240f: "" +0x2410: "" +0x2411: "" +0x2412: "" +0x2413: "" +0x2414: "" +0x2415: "" +0x2416: "" +0x2417: "" +0x2418: "" +0x2419: "" +0x241a: "" +0x241b: "" +0x241c: "" +0x241d: "" +0x241e: "" +0x241f: "" +0x2420: "" +0x2421: "" +0x2422: "" +0x2423: "" +0x2424: "" +0x2425: "" +0x2426: "" +0x2427: "[?]" +0x2428: "[?]" +0x2429: "[?]" +0x242a: "[?]" +0x242b: "[?]" +0x242c: "[?]" +0x242d: "[?]" +0x242e: "[?]" +0x242f: "[?]" +0x2430: "[?]" +0x2431: "[?]" +0x2432: "[?]" +0x2433: "[?]" +0x2434: "[?]" +0x2435: "[?]" +0x2436: "[?]" +0x2437: "[?]" +0x2438: "[?]" +0x2439: "[?]" +0x243a: "[?]" +0x243b: "[?]" +0x243c: "[?]" +0x243d: "[?]" +0x243e: "[?]" +0x243f: "[?]" +0x2440: "" +0x2441: "" +0x2442: "" +0x2443: "" +0x2444: "" +0x2445: "" +0x2446: "" +0x2447: "" +0x2448: "" +0x2449: "" +0x244a: "" +0x244b: "[?]" +0x244c: "[?]" +0x244d: "[?]" +0x244e: "[?]" +0x244f: "[?]" +0x2450: "[?]" +0x2451: "[?]" +0x2452: "[?]" +0x2453: "[?]" +0x2454: "[?]" +0x2455: "[?]" +0x2456: "[?]" +0x2457: "[?]" +0x2458: "[?]" +0x2459: "[?]" +0x245a: "[?]" +0x245b: "[?]" +0x245c: "[?]" +0x245d: "[?]" +0x245e: "[?]" +0x245f: "[?]" +0x2460: "" +0x2461: "" +0x2462: "" +0x2463: "" +0x2464: "" +0x2465: "" +0x2466: "" +0x2467: "" +0x2468: "" +0x2469: "" +0x246a: "" +0x246b: "" +0x246c: "" +0x246d: "" +0x246e: "" +0x246f: "" +0x2470: "" +0x2471: "" +0x2472: "" +0x2473: "" +0x2474: "" +0x2475: "" +0x2476: "" +0x2477: "" +0x2478: "" +0x2479: "" +0x247a: "" +0x247b: "" +0x247c: "" +0x247d: "" +0x247e: "" +0x247f: "" +0x2480: "" +0x2481: "" +0x2482: "" +0x2483: "" +0x2484: "" +0x2485: "" +0x2486: "" +0x2487: "" +0x2488: "" +0x2489: "" +0x248a: "" +0x248b: "" +0x248c: "" +0x248d: "" +0x248e: "" +0x248f: "" +0x2490: "" +0x2491: "" +0x2492: "" +0x2493: "" +0x2494: "" +0x2495: "" +0x2496: "" +0x2497: "" +0x2498: "" +0x2499: "" +0x249a: "" +0x249b: "" +0x249c: "" +0x249d: "" +0x249e: "" +0x249f: "" +0x24a0: "" +0x24a1: "" +0x24a2: "" +0x24a3: "" +0x24a4: "" +0x24a5: "" +0x24a6: "" +0x24a7: "" +0x24a8: "" +0x24a9: "" +0x24aa: "" +0x24ab: "" +0x24ac: "" +0x24ad: "" +0x24ae: "" +0x24af: "" +0x24b0: "" +0x24b1: "" +0x24b2: "" +0x24b3: "" +0x24b4: "" +0x24b5: "" +0x24b6: "" +0x24b7: "" +0x24b8: "" +0x24b9: "" +0x24ba: "" +0x24bb: "" +0x24bc: "" +0x24bd: "" +0x24be: "" +0x24bf: "" +0x24c0: "" +0x24c1: "" +0x24c2: "" +0x24c3: "" +0x24c4: "" +0x24c5: "" +0x24c6: "" +0x24c7: "" +0x24c8: "" +0x24c9: "" +0x24ca: "" +0x24cb: "" +0x24cc: "" +0x24cd: "" +0x24ce: "" +0x24cf: "" +0x24d0: "a" +0x24d1: "b" +0x24d2: "c" +0x24d3: "d" +0x24d4: "e" +0x24d5: "f" +0x24d6: "g" +0x24d7: "h" +0x24d8: "i" +0x24d9: "j" +0x24da: "k" +0x24db: "l" +0x24dc: "m" +0x24dd: "n" +0x24de: "o" +0x24df: "p" +0x24e0: "q" +0x24e1: "r" +0x24e2: "s" +0x24e3: "t" +0x24e4: "u" +0x24e5: "v" +0x24e6: "w" +0x24e7: "x" +0x24e8: "y" +0x24e9: "z" +0x24ea: "0" +0x24eb: "[?]" +0x24ec: "[?]" +0x24ed: "[?]" +0x24ee: "[?]" +0x24ef: "[?]" +0x24f0: "[?]" +0x24f1: "[?]" +0x24f2: "[?]" +0x24f3: "[?]" +0x24f4: "[?]" +0x24f5: "[?]" +0x24f6: "[?]" +0x24f7: "[?]" +0x24f8: "[?]" +0x24f9: "[?]" +0x24fa: "[?]" +0x24fb: "[?]" +0x24fc: "[?]" +0x24fd: "[?]" +0x24fe: "[?]" +/* x025 */ +0x2500: "-" +0x2501: "-" +0x2502: "|" +0x2503: "|" +0x2504: "-" +0x2505: "-" +0x2506: "|" +0x2507: "|" +0x2508: "-" +0x2509: "-" +0x250a: "|" +0x250b: "|" +0x250c: "+" +0x250d: "+" +0x250e: "+" +0x250f: "+" +0x2510: "+" +0x2511: "+" +0x2512: "+" +0x2513: "+" +0x2514: "+" +0x2515: "+" +0x2516: "+" +0x2517: "+" +0x2518: "+" +0x2519: "+" +0x251a: "+" +0x251b: "+" +0x251c: "+" +0x251d: "+" +0x251e: "+" +0x251f: "+" +0x2520: "+" +0x2521: "+" +0x2522: "+" +0x2523: "+" +0x2524: "+" +0x2525: "+" +0x2526: "+" +0x2527: "+" +0x2528: "+" +0x2529: "+" +0x252a: "+" +0x252b: "+" +0x252c: "+" +0x252d: "+" +0x252e: "+" +0x252f: "+" +0x2530: "+" +0x2531: "+" +0x2532: "+" +0x2533: "+" +0x2534: "+" +0x2535: "+" +0x2536: "+" +0x2537: "+" +0x2538: "+" +0x2539: "+" +0x253a: "+" +0x253b: "+" +0x253c: "+" +0x253d: "+" +0x253e: "+" +0x253f: "+" +0x2540: "+" +0x2541: "+" +0x2542: "+" +0x2543: "+" +0x2544: "+" +0x2545: "+" +0x2546: "+" +0x2547: "+" +0x2548: "+" +0x2549: "+" +0x254a: "+" +0x254b: "+" +0x254c: "-" +0x254d: "-" +0x254e: "|" +0x254f: "|" +0x2550: "-" +0x2551: "|" +0x2552: "+" +0x2553: "+" +0x2554: "+" +0x2555: "+" +0x2556: "+" +0x2557: "+" +0x2558: "+" +0x2559: "+" +0x255a: "+" +0x255b: "+" +0x255c: "+" +0x255d: "+" +0x255e: "+" +0x255f: "+" +0x2560: "+" +0x2561: "+" +0x2562: "+" +0x2563: "+" +0x2564: "+" +0x2565: "+" +0x2566: "+" +0x2567: "+" +0x2568: "+" +0x2569: "+" +0x256a: "+" +0x256b: "+" +0x256c: "+" +0x256d: "+" +0x256e: "+" +0x256f: "+" +0x2570: "+" +0x2571: "/" +0x2572: "\\" +0x2573: "X" +0x2574: "-" +0x2575: "|" +0x2576: "-" +0x2577: "|" +0x2578: "-" +0x2579: "|" +0x257a: "-" +0x257b: "|" +0x257c: "-" +0x257d: "|" +0x257e: "-" +0x257f: "|" +0x2580: "#" +0x2581: "#" +0x2582: "#" +0x2583: "#" +0x2584: "#" +0x2585: "#" +0x2586: "#" +0x2587: "#" +0x2588: "#" +0x2589: "#" +0x258a: "#" +0x258b: "#" +0x258c: "#" +0x258d: "#" +0x258e: "#" +0x258f: "#" +0x2590: "#" +0x2591: "#" +0x2592: "#" +0x2593: "#" +0x2594: "-" +0x2595: "|" +0x2596: "[?]" +0x2597: "[?]" +0x2598: "[?]" +0x2599: "[?]" +0x259a: "[?]" +0x259b: "[?]" +0x259c: "[?]" +0x259d: "[?]" +0x259e: "[?]" +0x259f: "[?]" +0x25a0: "#" +0x25a1: "#" +0x25a2: "#" +0x25a3: "#" +0x25a4: "#" +0x25a5: "#" +0x25a6: "#" +0x25a7: "#" +0x25a8: "#" +0x25a9: "#" +0x25aa: "#" +0x25ab: "#" +0x25ac: "#" +0x25ad: "#" +0x25ae: "#" +0x25af: "#" +0x25b0: "#" +0x25b1: "#" +0x25b2: "^" +0x25b3: "^" +0x25b4: "^" +0x25b5: "^" +0x25b6: ">" +0x25b7: ">" +0x25b8: ">" +0x25b9: ">" +0x25ba: ">" +0x25bb: ">" +0x25bc: "V" +0x25bd: "V" +0x25be: "V" +0x25bf: "V" +0x25c0: "<" +0x25c1: "<" +0x25c2: "<" +0x25c3: "<" +0x25c4: "<" +0x25c5: "<" +0x25c6: "*" +0x25c7: "*" +0x25c8: "*" +0x25c9: "*" +0x25ca: "*" +0x25cb: "*" +0x25cc: "*" +0x25cd: "*" +0x25ce: "*" +0x25cf: "*" +0x25d0: "*" +0x25d1: "*" +0x25d2: "*" +0x25d3: "*" +0x25d4: "*" +0x25d5: "*" +0x25d6: "*" +0x25d7: "*" +0x25d8: "*" +0x25d9: "*" +0x25da: "*" +0x25db: "*" +0x25dc: "*" +0x25dd: "*" +0x25de: "*" +0x25df: "*" +0x25e0: "*" +0x25e1: "*" +0x25e2: "*" +0x25e3: "*" +0x25e4: "*" +0x25e5: "*" +0x25e6: "*" +0x25e7: "#" +0x25e8: "#" +0x25e9: "#" +0x25ea: "#" +0x25eb: "#" +0x25ec: "^" +0x25ed: "^" +0x25ee: "^" +0x25ef: "O" +0x25f0: "#" +0x25f1: "#" +0x25f2: "#" +0x25f3: "#" +0x25f4: "#" +0x25f5: "#" +0x25f6: "#" +0x25f7: "#" +0x25f8: "[?]" +0x25f9: "[?]" +0x25fa: "[?]" +0x25fb: "[?]" +0x25fc: "[?]" +0x25fd: "[?]" +0x25fe: "[?]" +/* x026 */ +0x2600: "" +0x2601: "" +0x2602: "" +0x2603: "" +0x2604: "" +0x2605: "" +0x2606: "" +0x2607: "" +0x2608: "" +0x2609: "" +0x260a: "" +0x260b: "" +0x260c: "" +0x260d: "" +0x260e: "" +0x260f: "" +0x2610: "" +0x2611: "" +0x2612: "" +0x2613: "" +0x2614: "[?]" +0x2615: "[?]" +0x2616: "[?]" +0x2617: "[?]" +0x2618: "[?]" +0x2619: "" +0x261a: "" +0x261b: "" +0x261c: "" +0x261d: "" +0x261e: "" +0x261f: "" +0x2620: "" +0x2621: "" +0x2622: "" +0x2623: "" +0x2624: "" +0x2625: "" +0x2626: "" +0x2627: "" +0x2628: "" +0x2629: "" +0x262a: "" +0x262b: "" +0x262c: "" +0x262d: "" +0x262e: "" +0x262f: "" +0x2630: "" +0x2631: "" +0x2632: "" +0x2633: "" +0x2634: "" +0x2635: "" +0x2636: "" +0x2637: "" +0x2638: "" +0x2639: "" +0x263a: "" +0x263b: "" +0x263c: "" +0x263d: "" +0x263e: "" +0x263f: "" +0x2640: "" +0x2641: "" +0x2642: "" +0x2643: "" +0x2644: "" +0x2645: "" +0x2646: "" +0x2647: "" +0x2648: "" +0x2649: "" +0x264a: "" +0x264b: "" +0x264c: "" +0x264d: "" +0x264e: "" +0x264f: "" +0x2650: "" +0x2651: "" +0x2652: "" +0x2653: "" +0x2654: "" +0x2655: "" +0x2656: "" +0x2657: "" +0x2658: "" +0x2659: "" +0x265a: "" +0x265b: "" +0x265c: "" +0x265d: "" +0x265e: "" +0x265f: "" +0x2660: "" +0x2661: "" +0x2662: "" +0x2663: "" +0x2664: "" +0x2665: "" +0x2666: "" +0x2667: "" +0x2668: "" +0x2669: "" +0x266a: "" +0x266b: "" +0x266c: "" +0x266d: "" +0x266e: "" +0x266f: "" +0x2670: "" +0x2671: "" +0x2672: "[?]" +0x2673: "[?]" +0x2674: "[?]" +0x2675: "[?]" +0x2676: "[?]" +0x2677: "[?]" +0x2678: "[?]" +0x2679: "[?]" +0x267a: "[?]" +0x267b: "[?]" +0x267c: "[?]" +0x267d: "[?]" +0x267e: "[?]" +0x267f: "[?]" +0x2680: "[?]" +0x2681: "[?]" +0x2682: "[?]" +0x2683: "[?]" +0x2684: "[?]" +0x2685: "[?]" +0x2686: "[?]" +0x2687: "[?]" +0x2688: "[?]" +0x2689: "[?]" +0x268a: "[?]" +0x268b: "[?]" +0x268c: "[?]" +0x268d: "[?]" +0x268e: "[?]" +0x268f: "[?]" +0x2690: "[?]" +0x2691: "[?]" +0x2692: "[?]" +0x2693: "[?]" +0x2694: "[?]" +0x2695: "[?]" +0x2696: "[?]" +0x2697: "[?]" +0x2698: "[?]" +0x2699: "[?]" +0x269a: "[?]" +0x269b: "[?]" +0x269c: "[?]" +0x269d: "[?]" +0x269e: "[?]" +0x269f: "[?]" +0x26a0: "[?]" +0x26a1: "[?]" +0x26a2: "[?]" +0x26a3: "[?]" +0x26a4: "[?]" +0x26a5: "[?]" +0x26a6: "[?]" +0x26a7: "[?]" +0x26a8: "[?]" +0x26a9: "[?]" +0x26aa: "[?]" +0x26ab: "[?]" +0x26ac: "[?]" +0x26ad: "[?]" +0x26ae: "[?]" +0x26af: "[?]" +0x26b0: "[?]" +0x26b1: "[?]" +0x26b2: "[?]" +0x26b3: "[?]" +0x26b4: "[?]" +0x26b5: "[?]" +0x26b6: "[?]" +0x26b7: "[?]" +0x26b8: "[?]" +0x26b9: "[?]" +0x26ba: "[?]" +0x26bb: "[?]" +0x26bc: "[?]" +0x26bd: "[?]" +0x26be: "[?]" +0x26bf: "[?]" +0x26c0: "[?]" +0x26c1: "[?]" +0x26c2: "[?]" +0x26c3: "[?]" +0x26c4: "[?]" +0x26c5: "[?]" +0x26c6: "[?]" +0x26c7: "[?]" +0x26c8: "[?]" +0x26c9: "[?]" +0x26ca: "[?]" +0x26cb: "[?]" +0x26cc: "[?]" +0x26cd: "[?]" +0x26ce: "[?]" +0x26cf: "[?]" +0x26d0: "[?]" +0x26d1: "[?]" +0x26d2: "[?]" +0x26d3: "[?]" +0x26d4: "[?]" +0x26d5: "[?]" +0x26d6: "[?]" +0x26d7: "[?]" +0x26d8: "[?]" +0x26d9: "[?]" +0x26da: "[?]" +0x26db: "[?]" +0x26dc: "[?]" +0x26dd: "[?]" +0x26de: "[?]" +0x26df: "[?]" +0x26e0: "[?]" +0x26e1: "[?]" +0x26e2: "[?]" +0x26e3: "[?]" +0x26e4: "[?]" +0x26e5: "[?]" +0x26e6: "[?]" +0x26e7: "[?]" +0x26e8: "[?]" +0x26e9: "[?]" +0x26ea: "[?]" +0x26eb: "[?]" +0x26ec: "[?]" +0x26ed: "[?]" +0x26ee: "[?]" +0x26ef: "[?]" +0x26f0: "[?]" +0x26f1: "[?]" +0x26f2: "[?]" +0x26f3: "[?]" +0x26f4: "[?]" +0x26f5: "[?]" +0x26f6: "[?]" +0x26f7: "[?]" +0x26f8: "[?]" +0x26f9: "[?]" +0x26fa: "[?]" +0x26fb: "[?]" +0x26fc: "[?]" +0x26fd: "[?]" +0x26fe: "[?]" +/* x027 */ +0x2700: "[?]" +0x2701: "" +0x2702: "" +0x2703: "" +0x2704: "" +0x2705: "" +0x2706: "" +0x2707: "" +0x2708: "" +0x2709: "" +0x270a: "" +0x270b: "" +0x270c: "" +0x270d: "" +0x270e: "" +0x270f: "" +0x2710: "" +0x2711: "" +0x2712: "" +0x2713: "" +0x2714: "" +0x2715: "" +0x2716: "" +0x2717: "" +0x2718: "" +0x2719: "" +0x271a: "" +0x271b: "" +0x271c: "" +0x271d: "" +0x271e: "" +0x271f: "" +0x2720: "" +0x2721: "" +0x2722: "" +0x2723: "" +0x2724: "" +0x2725: "" +0x2726: "" +0x2727: "" +0x2728: "" +0x2729: "" +0x272a: "" +0x272b: "" +0x272c: "" +0x272d: "" +0x272e: "" +0x272f: "" +0x2730: "" +0x2731: "" +0x2732: "" +0x2733: "" +0x2734: "" +0x2735: "" +0x2736: "" +0x2737: "" +0x2738: "" +0x2739: "" +0x273a: "" +0x273b: "" +0x273c: "" +0x273d: "" +0x273e: "" +0x273f: "" +0x2740: "" +0x2741: "" +0x2742: "" +0x2743: "" +0x2744: "" +0x2745: "" +0x2746: "" +0x2747: "" +0x2748: "" +0x2749: "" +0x274a: "" +0x274b: "" +0x274c: "" +0x274d: "" +0x274e: "" +0x274f: "" +0x2750: "" +0x2751: "" +0x2752: "" +0x2753: "" +0x2754: "" +0x2755: "" +0x2756: "" +0x2757: "" +0x2758: "" +0x2759: "" +0x275a: "" +0x275b: "" +0x275c: "" +0x275d: "" +0x275e: "" +0x275f: "[?]" +0x2760: "[?]" +0x2761: "" +0x2762: "" +0x2763: "" +0x2764: "" +0x2765: "" +0x2766: "" +0x2767: "" +0x2768: "" +0x2769: "" +0x276a: "" +0x276b: "" +0x276c: "" +0x276d: "" +0x276e: "" +0x276f: "" +0x2770: "" +0x2771: "" +0x2772: "" +0x2773: "" +0x2774: "" +0x2775: "" +0x2776: "" +0x2777: "" +0x2778: "" +0x2779: "" +0x277a: "" +0x277b: "" +0x277c: "" +0x277d: "" +0x277e: "" +0x277f: "" +0x2780: "" +0x2781: "" +0x2782: "" +0x2783: "" +0x2784: "" +0x2785: "" +0x2786: "" +0x2787: "" +0x2788: "" +0x2789: "" +0x278a: "" +0x278b: "" +0x278c: "" +0x278d: "" +0x278e: "" +0x278f: "" +0x2790: "" +0x2791: "" +0x2792: "" +0x2793: "" +0x2794: "" +0x2795: "" +0x2796: "" +0x2797: "" +0x2798: "" +0x2799: "" +0x279a: "" +0x279b: "" +0x279c: "" +0x279d: "" +0x279e: "" +0x279f: "" +0x27a0: "" +0x27a1: "" +0x27a2: "" +0x27a3: "" +0x27a4: "" +0x27a5: "" +0x27a6: "" +0x27a7: "" +0x27a8: "" +0x27a9: "" +0x27aa: "" +0x27ab: "" +0x27ac: "" +0x27ad: "" +0x27ae: "" +0x27af: "" +0x27b0: "[?]" +0x27b1: "" +0x27b2: "" +0x27b3: "" +0x27b4: "" +0x27b5: "" +0x27b6: "" +0x27b7: "" +0x27b8: "" +0x27b9: "" +0x27ba: "" +0x27bb: "" +0x27bc: "" +0x27bd: "" +0x27be: "" +0x27bf: "[?]" +0x27c0: "[?]" +0x27c1: "[?]" +0x27c2: "[?]" +0x27c3: "[?]" +0x27c4: "[?]" +0x27c5: "[?]" +0x27c6: "[?]" +0x27c7: "[?]" +0x27c8: "[?]" +0x27c9: "[?]" +0x27ca: "[?]" +0x27cb: "[?]" +0x27cc: "[?]" +0x27cd: "[?]" +0x27ce: "[?]" +0x27cf: "[?]" +0x27d0: "[?]" +0x27d1: "[?]" +0x27d2: "[?]" +0x27d3: "[?]" +0x27d4: "[?]" +0x27d5: "[?]" +0x27d6: "[?]" +0x27d7: "[?]" +0x27d8: "[?]" +0x27d9: "[?]" +0x27da: "[?]" +0x27db: "[?]" +0x27dc: "[?]" +0x27dd: "[?]" +0x27de: "[?]" +0x27df: "[?]" +0x27e0: "[?]" +0x27e1: "[?]" +0x27e2: "[?]" +0x27e3: "[?]" +0x27e4: "[?]" +0x27e5: "[?]" +0x27e6: "[?]" +0x27e7: "[?]" +0x27e8: "[?]" +0x27e9: "[?]" +0x27ea: "[?]" +0x27eb: "[?]" +0x27ec: "[?]" +0x27ed: "[?]" +0x27ee: "[?]" +0x27ef: "[?]" +0x27f0: "[?]" +0x27f1: "[?]" +0x27f2: "[?]" +0x27f3: "[?]" +0x27f4: "[?]" +0x27f5: "[?]" +0x27f6: "[?]" +0x27f7: "[?]" +0x27f8: "[?]" +0x27f9: "[?]" +0x27fa: "[?]" +0x27fb: "[?]" +0x27fc: "[?]" +0x27fd: "[?]" +0x27fe: "[?]" +/* x028 */ +0x2800: " " +0x2801: "a" +0x2802: "1" +0x2803: "b" +0x2804: "'" +0x2805: "k" +0x2806: "2" +0x2807: "l" +0x2808: "@" +0x2809: "c" +0x280a: "i" +0x280b: "f" +0x280c: "/" +0x280d: "m" +0x280e: "s" +0x280f: "p" +0x2810: "\"" +0x2811: "e" +0x2812: "3" +0x2813: "h" +0x2814: "9" +0x2815: "o" +0x2816: "6" +0x2817: "r" +0x2818: "^" +0x2819: "d" +0x281a: "j" +0x281b: "g" +0x281c: ">" +0x281d: "n" +0x281e: "t" +0x281f: "q" +0x2820: "," +0x2821: "*" +0x2822: "5" +0x2823: "<" +0x2824: "-" +0x2825: "u" +0x2826: "8" +0x2827: "v" +0x2828: "." +0x2829: "%" +0x282a: "[" +0x282b: "$" +0x282c: "+" +0x282d: "x" +0x282e: "!" +0x282f: "&" +0x2830: ";" +0x2831: ":" +0x2832: "4" +0x2833: "\\" +0x2834: "0" +0x2835: "z" +0x2836: "7" +0x2837: "(" +0x2838: "_" +0x2839: "?" +0x283a: "w" +0x283b: "]" +0x283c: "#" +0x283d: "y" +0x283e: ")" +0x283f: "=" +0x2840: "[d7]" +0x2841: "[d17]" +0x2842: "[d27]" +0x2843: "[d127]" +0x2844: "[d37]" +0x2845: "[d137]" +0x2846: "[d237]" +0x2847: "[d1237]" +0x2848: "[d47]" +0x2849: "[d147]" +0x284a: "[d247]" +0x284b: "[d1247]" +0x284c: "[d347]" +0x284d: "[d1347]" +0x284e: "[d2347]" +0x284f: "[d12347]" +0x2850: "[d57]" +0x2851: "[d157]" +0x2852: "[d257]" +0x2853: "[d1257]" +0x2854: "[d357]" +0x2855: "[d1357]" +0x2856: "[d2357]" +0x2857: "[d12357]" +0x2858: "[d457]" +0x2859: "[d1457]" +0x285a: "[d2457]" +0x285b: "[d12457]" +0x285c: "[d3457]" +0x285d: "[d13457]" +0x285e: "[d23457]" +0x285f: "[d123457]" +0x2860: "[d67]" +0x2861: "[d167]" +0x2862: "[d267]" +0x2863: "[d1267]" +0x2864: "[d367]" +0x2865: "[d1367]" +0x2866: "[d2367]" +0x2867: "[d12367]" +0x2868: "[d467]" +0x2869: "[d1467]" +0x286a: "[d2467]" +0x286b: "[d12467]" +0x286c: "[d3467]" +0x286d: "[d13467]" +0x286e: "[d23467]" +0x286f: "[d123467]" +0x2870: "[d567]" +0x2871: "[d1567]" +0x2872: "[d2567]" +0x2873: "[d12567]" +0x2874: "[d3567]" +0x2875: "[d13567]" +0x2876: "[d23567]" +0x2877: "[d123567]" +0x2878: "[d4567]" +0x2879: "[d14567]" +0x287a: "[d24567]" +0x287b: "[d124567]" +0x287c: "[d34567]" +0x287d: "[d134567]" +0x287e: "[d234567]" +0x287f: "[d1234567]" +0x2880: "[d8]" +0x2881: "[d18]" +0x2882: "[d28]" +0x2883: "[d128]" +0x2884: "[d38]" +0x2885: "[d138]" +0x2886: "[d238]" +0x2887: "[d1238]" +0x2888: "[d48]" +0x2889: "[d148]" +0x288a: "[d248]" +0x288b: "[d1248]" +0x288c: "[d348]" +0x288d: "[d1348]" +0x288e: "[d2348]" +0x288f: "[d12348]" +0x2890: "[d58]" +0x2891: "[d158]" +0x2892: "[d258]" +0x2893: "[d1258]" +0x2894: "[d358]" +0x2895: "[d1358]" +0x2896: "[d2358]" +0x2897: "[d12358]" +0x2898: "[d458]" +0x2899: "[d1458]" +0x289a: "[d2458]" +0x289b: "[d12458]" +0x289c: "[d3458]" +0x289d: "[d13458]" +0x289e: "[d23458]" +0x289f: "[d123458]" +0x28a0: "[d68]" +0x28a1: "[d168]" +0x28a2: "[d268]" +0x28a3: "[d1268]" +0x28a4: "[d368]" +0x28a5: "[d1368]" +0x28a6: "[d2368]" +0x28a7: "[d12368]" +0x28a8: "[d468]" +0x28a9: "[d1468]" +0x28aa: "[d2468]" +0x28ab: "[d12468]" +0x28ac: "[d3468]" +0x28ad: "[d13468]" +0x28ae: "[d23468]" +0x28af: "[d123468]" +0x28b0: "[d568]" +0x28b1: "[d1568]" +0x28b2: "[d2568]" +0x28b3: "[d12568]" +0x28b4: "[d3568]" +0x28b5: "[d13568]" +0x28b6: "[d23568]" +0x28b7: "[d123568]" +0x28b8: "[d4568]" +0x28b9: "[d14568]" +0x28ba: "[d24568]" +0x28bb: "[d124568]" +0x28bc: "[d34568]" +0x28bd: "[d134568]" +0x28be: "[d234568]" +0x28bf: "[d1234568]" +0x28c0: "[d78]" +0x28c1: "[d178]" +0x28c2: "[d278]" +0x28c3: "[d1278]" +0x28c4: "[d378]" +0x28c5: "[d1378]" +0x28c6: "[d2378]" +0x28c7: "[d12378]" +0x28c8: "[d478]" +0x28c9: "[d1478]" +0x28ca: "[d2478]" +0x28cb: "[d12478]" +0x28cc: "[d3478]" +0x28cd: "[d13478]" +0x28ce: "[d23478]" +0x28cf: "[d123478]" +0x28d0: "[d578]" +0x28d1: "[d1578]" +0x28d2: "[d2578]" +0x28d3: "[d12578]" +0x28d4: "[d3578]" +0x28d5: "[d13578]" +0x28d6: "[d23578]" +0x28d7: "[d123578]" +0x28d8: "[d4578]" +0x28d9: "[d14578]" +0x28da: "[d24578]" +0x28db: "[d124578]" +0x28dc: "[d34578]" +0x28dd: "[d134578]" +0x28de: "[d234578]" +0x28df: "[d1234578]" +0x28e0: "[d678]" +0x28e1: "[d1678]" +0x28e2: "[d2678]" +0x28e3: "[d12678]" +0x28e4: "[d3678]" +0x28e5: "[d13678]" +0x28e6: "[d23678]" +0x28e7: "[d123678]" +0x28e8: "[d4678]" +0x28e9: "[d14678]" +0x28ea: "[d24678]" +0x28eb: "[d124678]" +0x28ec: "[d34678]" +0x28ed: "[d134678]" +0x28ee: "[d234678]" +0x28ef: "[d1234678]" +0x28f0: "[d5678]" +0x28f1: "[d15678]" +0x28f2: "[d25678]" +0x28f3: "[d125678]" +0x28f4: "[d35678]" +0x28f5: "[d135678]" +0x28f6: "[d235678]" +0x28f7: "[d1235678]" +0x28f8: "[d45678]" +0x28f9: "[d145678]" +0x28fa: "[d245678]" +0x28fb: "[d1245678]" +0x28fc: "[d345678]" +0x28fd: "[d1345678]" +0x28fe: "[d2345678]" +0x28ff: "[d12345678]" +/* x02c */ +0x2c00: "" +0x2c01: "" +0x2c02: "" +0x2c03: "" +0x2c04: "" +0x2c05: "" +0x2c06: "" +0x2c07: "" +0x2c08: "" +0x2c09: "" +0x2c0a: "" +0x2c0b: "" +0x2c0c: "" +0x2c0d: "" +0x2c0e: "" +0x2c0f: "" +0x2c10: "" +0x2c11: "" +0x2c12: "" +0x2c13: "" +0x2c14: "" +0x2c15: "" +0x2c16: "" +0x2c17: "" +0x2c18: "" +0x2c19: "" +0x2c1a: "" +0x2c1b: "" +0x2c1c: "" +0x2c1d: "" +0x2c1e: "" +0x2c1f: "" +0x2c20: "" +0x2c21: "" +0x2c22: "" +0x2c23: "" +0x2c24: "" +0x2c25: "" +0x2c26: "" +0x2c27: "" +0x2c28: "" +0x2c29: "" +0x2c2a: "" +0x2c2b: "" +0x2c2c: "" +0x2c2d: "" +0x2c2e: "" +0x2c2f: "" +0x2c30: "" +0x2c31: "" +0x2c32: "" +0x2c33: "" +0x2c34: "" +0x2c35: "" +0x2c36: "" +0x2c37: "" +0x2c38: "" +0x2c39: "" +0x2c3a: "" +0x2c3b: "" +0x2c3c: "" +0x2c3d: "" +0x2c3e: "" +0x2c3f: "" +0x2c40: "" +0x2c41: "" +0x2c42: "" +0x2c43: "" +0x2c44: "" +0x2c45: "" +0x2c46: "" +0x2c47: "" +0x2c48: "" +0x2c49: "" +0x2c4a: "" +0x2c4b: "" +0x2c4c: "" +0x2c4d: "" +0x2c4e: "" +0x2c4f: "" +0x2c50: "" +0x2c51: "" +0x2c52: "" +0x2c53: "" +0x2c54: "" +0x2c55: "" +0x2c56: "" +0x2c57: "" +0x2c58: "" +0x2c59: "" +0x2c5a: "" +0x2c5b: "" +0x2c5c: "" +0x2c5d: "" +0x2c5e: "" +0x2c5f: "" +0x2c60: "L" +0x2c61: "l" +0x2c62: "L" +0x2c63: "P" +0x2c64: "R" +0x2c65: "a" +0x2c66: "t" +0x2c67: "H" +0x2c68: "h" +0x2c69: "K" +0x2c6a: "k" +0x2c6b: "Z" +0x2c6c: "z" +0x2c6d: "" +0x2c6e: "M" +0x2c6f: "A" +0x2c70: "" +0x2c71: "" +0x2c72: "" +0x2c73: "" +0x2c74: "" +0x2c75: "" +0x2c76: "" +0x2c77: "" +0x2c78: "" +0x2c79: "" +0x2c7a: "" +0x2c7b: "" +0x2c7c: "" +0x2c7d: "" +0x2c7e: "" +0x2c7f: "" +0x2c80: "" +0x2c81: "" +0x2c82: "" +0x2c83: "" +0x2c84: "" +0x2c85: "" +0x2c86: "" +0x2c87: "" +0x2c88: "" +0x2c89: "" +0x2c8a: "" +0x2c8b: "" +0x2c8c: "" +0x2c8d: "" +0x2c8e: "" +0x2c8f: "" +0x2c90: "" +0x2c91: "" +0x2c92: "" +0x2c93: "" +0x2c94: "" +0x2c95: "" +0x2c96: "" +0x2c97: "" +0x2c98: "" +0x2c99: "" +0x2c9a: "" +0x2c9b: "" +0x2c9c: "" +0x2c9d: "" +0x2c9e: "" +0x2c9f: "" +0x2ca0: "" +0x2ca1: "" +0x2ca2: "" +0x2ca3: "" +0x2ca4: "" +0x2ca5: "" +0x2ca6: "" +0x2ca7: "" +0x2ca8: "" +0x2ca9: "" +0x2caa: "" +0x2cab: "" +0x2cac: "" +0x2cad: "" +0x2cae: "" +0x2caf: "" +0x2cb0: "" +0x2cb1: "" +0x2cb2: "" +0x2cb3: "" +0x2cb4: "" +0x2cb5: "" +0x2cb6: "" +0x2cb7: "" +0x2cb8: "" +0x2cb9: "" +0x2cba: "" +0x2cbb: "" +0x2cbc: "" +0x2cbd: "" +0x2cbe: "" +0x2cbf: "" +0x2cc0: "" +0x2cc1: "" +0x2cc2: "" +0x2cc3: "" +0x2cc4: "" +0x2cc5: "" +0x2cc6: "" +0x2cc7: "" +0x2cc8: "" +0x2cc9: "" +0x2cca: "" +0x2ccb: "" +0x2ccc: "" +0x2ccd: "" +0x2cce: "" +0x2ccf: "" +0x2cd0: "" +0x2cd1: "" +0x2cd2: "" +0x2cd3: "" +0x2cd4: "" +0x2cd5: "" +0x2cd6: "" +0x2cd7: "" +0x2cd8: "" +0x2cd9: "" +0x2cda: "" +0x2cdb: "" +0x2cdc: "" +0x2cdd: "" +0x2cde: "" +0x2cdf: "" +0x2ce0: "" +0x2ce1: "" +0x2ce2: "" +0x2ce3: "" +0x2ce4: "" +0x2ce5: "" +0x2ce6: "" +0x2ce7: "" +0x2ce8: "" +0x2ce9: "" +0x2cea: "" +0x2ceb: "" +0x2cec: "" +0x2ced: "" +0x2cee: "" +0x2cef: "" +0x2cf0: "" +0x2cf1: "" +0x2cf2: "" +0x2cf3: "" +0x2cf4: "" +0x2cf5: "" +0x2cf6: "" +0x2cf7: "" +0x2cf8: "" +0x2cf9: "" +0x2cfa: "" +0x2cfb: "" +0x2cfc: "" +0x2cfd: "" +0x2cfe: "" +/* x02e */ +0x2e00: "[?]" +0x2e01: "[?]" +0x2e02: "[?]" +0x2e03: "[?]" +0x2e04: "[?]" +0x2e05: "[?]" +0x2e06: "[?]" +0x2e07: "[?]" +0x2e08: "[?]" +0x2e09: "[?]" +0x2e0a: "[?]" +0x2e0b: "[?]" +0x2e0c: "[?]" +0x2e0d: "[?]" +0x2e0e: "[?]" +0x2e0f: "[?]" +0x2e10: "[?]" +0x2e11: "[?]" +0x2e12: "[?]" +0x2e13: "[?]" +0x2e14: "[?]" +0x2e15: "[?]" +0x2e16: "[?]" +0x2e17: "[?]" +0x2e18: "[?]" +0x2e19: "[?]" +0x2e1a: "[?]" +0x2e1b: "[?]" +0x2e1c: "[?]" +0x2e1d: "[?]" +0x2e1e: "[?]" +0x2e1f: "[?]" +0x2e20: "[?]" +0x2e21: "[?]" +0x2e22: "[?]" +0x2e23: "[?]" +0x2e24: "[?]" +0x2e25: "[?]" +0x2e26: "[?]" +0x2e27: "[?]" +0x2e28: "[?]" +0x2e29: "[?]" +0x2e2a: "[?]" +0x2e2b: "[?]" +0x2e2c: "[?]" +0x2e2d: "[?]" +0x2e2e: "[?]" +0x2e2f: "[?]" +0x2e30: "[?]" +0x2e31: "[?]" +0x2e32: "[?]" +0x2e33: "[?]" +0x2e34: "[?]" +0x2e35: "[?]" +0x2e36: "[?]" +0x2e37: "[?]" +0x2e38: "[?]" +0x2e39: "[?]" +0x2e3a: "[?]" +0x2e3b: "[?]" +0x2e3c: "[?]" +0x2e3d: "[?]" +0x2e3e: "[?]" +0x2e3f: "[?]" +0x2e40: "[?]" +0x2e41: "[?]" +0x2e42: "[?]" +0x2e43: "[?]" +0x2e44: "[?]" +0x2e45: "[?]" +0x2e46: "[?]" +0x2e47: "[?]" +0x2e48: "[?]" +0x2e49: "[?]" +0x2e4a: "[?]" +0x2e4b: "[?]" +0x2e4c: "[?]" +0x2e4d: "[?]" +0x2e4e: "[?]" +0x2e4f: "[?]" +0x2e50: "[?]" +0x2e51: "[?]" +0x2e52: "[?]" +0x2e53: "[?]" +0x2e54: "[?]" +0x2e55: "[?]" +0x2e56: "[?]" +0x2e57: "[?]" +0x2e58: "[?]" +0x2e59: "[?]" +0x2e5a: "[?]" +0x2e5b: "[?]" +0x2e5c: "[?]" +0x2e5d: "[?]" +0x2e5e: "[?]" +0x2e5f: "[?]" +0x2e60: "[?]" +0x2e61: "[?]" +0x2e62: "[?]" +0x2e63: "[?]" +0x2e64: "[?]" +0x2e65: "[?]" +0x2e66: "[?]" +0x2e67: "[?]" +0x2e68: "[?]" +0x2e69: "[?]" +0x2e6a: "[?]" +0x2e6b: "[?]" +0x2e6c: "[?]" +0x2e6d: "[?]" +0x2e6e: "[?]" +0x2e6f: "[?]" +0x2e70: "[?]" +0x2e71: "[?]" +0x2e72: "[?]" +0x2e73: "[?]" +0x2e74: "[?]" +0x2e75: "[?]" +0x2e76: "[?]" +0x2e77: "[?]" +0x2e78: "[?]" +0x2e79: "[?]" +0x2e7a: "[?]" +0x2e7b: "[?]" +0x2e7c: "[?]" +0x2e7d: "[?]" +0x2e7e: "[?]" +0x2e7f: "[?]" +0x2e80: "[?] " +0x2e81: "[?] " +0x2e82: "[?] " +0x2e83: "[?] " +0x2e84: "[?] " +0x2e85: "[?] " +0x2e86: "[?] " +0x2e87: "[?] " +0x2e88: "[?] " +0x2e89: "[?] " +0x2e8a: "[?] " +0x2e8b: "[?] " +0x2e8c: "[?] " +0x2e8d: "[?] " +0x2e8e: "[?] " +0x2e8f: "[?] " +0x2e90: "[?] " +0x2e91: "[?] " +0x2e92: "[?] " +0x2e93: "[?] " +0x2e94: "[?] " +0x2e95: "[?] " +0x2e96: "[?] " +0x2e97: "[?] " +0x2e98: "[?] " +0x2e99: "[?] " +0x2e9a: "[?]" +0x2e9b: "[?] " +0x2e9c: "[?] " +0x2e9d: "[?] " +0x2e9e: "[?] " +0x2e9f: "[?] " +0x2ea0: "[?] " +0x2ea1: "[?] " +0x2ea2: "[?] " +0x2ea3: "[?] " +0x2ea4: "[?] " +0x2ea5: "[?] " +0x2ea6: "[?] " +0x2ea7: "[?] " +0x2ea8: "[?] " +0x2ea9: "[?] " +0x2eaa: "[?] " +0x2eab: "[?] " +0x2eac: "[?] " +0x2ead: "[?] " +0x2eae: "[?] " +0x2eaf: "[?] " +0x2eb0: "[?] " +0x2eb1: "[?] " +0x2eb2: "[?] " +0x2eb3: "[?] " +0x2eb4: "[?] " +0x2eb5: "[?] " +0x2eb6: "[?] " +0x2eb7: "[?] " +0x2eb8: "[?] " +0x2eb9: "[?] " +0x2eba: "[?] " +0x2ebb: "[?] " +0x2ebc: "[?] " +0x2ebd: "[?] " +0x2ebe: "[?] " +0x2ebf: "[?] " +0x2ec0: "[?] " +0x2ec1: "[?] " +0x2ec2: "[?] " +0x2ec3: "[?] " +0x2ec4: "[?] " +0x2ec5: "[?] " +0x2ec6: "[?] " +0x2ec7: "[?] " +0x2ec8: "[?] " +0x2ec9: "[?] " +0x2eca: "[?] " +0x2ecb: "[?] " +0x2ecc: "[?] " +0x2ecd: "[?] " +0x2ece: "[?] " +0x2ecf: "[?] " +0x2ed0: "[?] " +0x2ed1: "[?] " +0x2ed2: "[?] " +0x2ed3: "[?] " +0x2ed4: "[?] " +0x2ed5: "[?] " +0x2ed6: "[?] " +0x2ed7: "[?] " +0x2ed8: "[?] " +0x2ed9: "[?] " +0x2eda: "[?] " +0x2edb: "[?] " +0x2edc: "[?] " +0x2edd: "[?] " +0x2ede: "[?] " +0x2edf: "[?] " +0x2ee0: "[?] " +0x2ee1: "[?] " +0x2ee2: "[?] " +0x2ee3: "[?] " +0x2ee4: "[?] " +0x2ee5: "[?] " +0x2ee6: "[?] " +0x2ee7: "[?] " +0x2ee8: "[?] " +0x2ee9: "[?] " +0x2eea: "[?] " +0x2eeb: "[?] " +0x2eec: "[?] " +0x2eed: "[?] " +0x2eee: "[?] " +0x2eef: "[?] " +0x2ef0: "[?] " +0x2ef1: "[?] " +0x2ef2: "[?] " +0x2ef3: "[?] " +0x2ef4: "[?]" +0x2ef5: "[?]" +0x2ef6: "[?]" +0x2ef7: "[?]" +0x2ef8: "[?]" +0x2ef9: "[?]" +0x2efa: "[?]" +0x2efb: "[?]" +0x2efc: "[?]" +0x2efd: "[?]" +0x2efe: "[?]" +/* x02f */ +0x2f00: "[?] " +0x2f01: "[?] " +0x2f02: "[?] " +0x2f03: "[?] " +0x2f04: "[?] " +0x2f05: "[?] " +0x2f06: "[?] " +0x2f07: "[?] " +0x2f08: "[?] " +0x2f09: "[?] " +0x2f0a: "[?] " +0x2f0b: "[?] " +0x2f0c: "[?] " +0x2f0d: "[?] " +0x2f0e: "[?] " +0x2f0f: "[?] " +0x2f10: "[?] " +0x2f11: "[?] " +0x2f12: "[?] " +0x2f13: "[?] " +0x2f14: "[?] " +0x2f15: "[?] " +0x2f16: "[?] " +0x2f17: "[?] " +0x2f18: "[?] " +0x2f19: "[?] " +0x2f1a: "[?] " +0x2f1b: "[?] " +0x2f1c: "[?] " +0x2f1d: "[?] " +0x2f1e: "[?] " +0x2f1f: "[?] " +0x2f20: "[?] " +0x2f21: "[?] " +0x2f22: "[?] " +0x2f23: "[?] " +0x2f24: "[?] " +0x2f25: "[?] " +0x2f26: "[?] " +0x2f27: "[?] " +0x2f28: "[?] " +0x2f29: "[?] " +0x2f2a: "[?] " +0x2f2b: "[?] " +0x2f2c: "[?] " +0x2f2d: "[?] " +0x2f2e: "[?] " +0x2f2f: "[?] " +0x2f30: "[?] " +0x2f31: "[?] " +0x2f32: "[?] " +0x2f33: "[?] " +0x2f34: "[?] " +0x2f35: "[?] " +0x2f36: "[?] " +0x2f37: "[?] " +0x2f38: "[?] " +0x2f39: "[?] " +0x2f3a: "[?] " +0x2f3b: "[?] " +0x2f3c: "[?] " +0x2f3d: "[?] " +0x2f3e: "[?] " +0x2f3f: "[?] " +0x2f40: "[?] " +0x2f41: "[?] " +0x2f42: "[?] " +0x2f43: "[?] " +0x2f44: "[?] " +0x2f45: "[?] " +0x2f46: "[?] " +0x2f47: "[?] " +0x2f48: "[?] " +0x2f49: "[?] " +0x2f4a: "[?] " +0x2f4b: "[?] " +0x2f4c: "[?] " +0x2f4d: "[?] " +0x2f4e: "[?] " +0x2f4f: "[?] " +0x2f50: "[?] " +0x2f51: "[?] " +0x2f52: "[?] " +0x2f53: "[?] " +0x2f54: "[?] " +0x2f55: "[?] " +0x2f56: "[?] " +0x2f57: "[?] " +0x2f58: "[?] " +0x2f59: "[?] " +0x2f5a: "[?] " +0x2f5b: "[?] " +0x2f5c: "[?] " +0x2f5d: "[?] " +0x2f5e: "[?] " +0x2f5f: "[?] " +0x2f60: "[?] " +0x2f61: "[?] " +0x2f62: "[?] " +0x2f63: "[?] " +0x2f64: "[?] " +0x2f65: "[?] " +0x2f66: "[?] " +0x2f67: "[?] " +0x2f68: "[?] " +0x2f69: "[?] " +0x2f6a: "[?] " +0x2f6b: "[?] " +0x2f6c: "[?] " +0x2f6d: "[?] " +0x2f6e: "[?] " +0x2f6f: "[?] " +0x2f70: "[?] " +0x2f71: "[?] " +0x2f72: "[?] " +0x2f73: "[?] " +0x2f74: "[?] " +0x2f75: "[?] " +0x2f76: "[?] " +0x2f77: "[?] " +0x2f78: "[?] " +0x2f79: "[?] " +0x2f7a: "[?] " +0x2f7b: "[?] " +0x2f7c: "[?] " +0x2f7d: "[?] " +0x2f7e: "[?] " +0x2f7f: "[?] " +0x2f80: "[?] " +0x2f81: "[?] " +0x2f82: "[?] " +0x2f83: "[?] " +0x2f84: "[?] " +0x2f85: "[?] " +0x2f86: "[?] " +0x2f87: "[?] " +0x2f88: "[?] " +0x2f89: "[?] " +0x2f8a: "[?] " +0x2f8b: "[?] " +0x2f8c: "[?] " +0x2f8d: "[?] " +0x2f8e: "[?] " +0x2f8f: "[?] " +0x2f90: "[?] " +0x2f91: "[?] " +0x2f92: "[?] " +0x2f93: "[?] " +0x2f94: "[?] " +0x2f95: "[?] " +0x2f96: "[?] " +0x2f97: "[?] " +0x2f98: "[?] " +0x2f99: "[?] " +0x2f9a: "[?] " +0x2f9b: "[?] " +0x2f9c: "[?] " +0x2f9d: "[?] " +0x2f9e: "[?] " +0x2f9f: "[?] " +0x2fa0: "[?] " +0x2fa1: "[?] " +0x2fa2: "[?] " +0x2fa3: "[?] " +0x2fa4: "[?] " +0x2fa5: "[?] " +0x2fa6: "[?] " +0x2fa7: "[?] " +0x2fa8: "[?] " +0x2fa9: "[?] " +0x2faa: "[?] " +0x2fab: "[?] " +0x2fac: "[?] " +0x2fad: "[?] " +0x2fae: "[?] " +0x2faf: "[?] " +0x2fb0: "[?] " +0x2fb1: "[?] " +0x2fb2: "[?] " +0x2fb3: "[?] " +0x2fb4: "[?] " +0x2fb5: "[?] " +0x2fb6: "[?] " +0x2fb7: "[?] " +0x2fb8: "[?] " +0x2fb9: "[?] " +0x2fba: "[?] " +0x2fbb: "[?] " +0x2fbc: "[?] " +0x2fbd: "[?] " +0x2fbe: "[?] " +0x2fbf: "[?] " +0x2fc0: "[?] " +0x2fc1: "[?] " +0x2fc2: "[?] " +0x2fc3: "[?] " +0x2fc4: "[?] " +0x2fc5: "[?] " +0x2fc6: "[?] " +0x2fc7: "[?] " +0x2fc8: "[?] " +0x2fc9: "[?] " +0x2fca: "[?] " +0x2fcb: "[?] " +0x2fcc: "[?] " +0x2fcd: "[?] " +0x2fce: "[?] " +0x2fcf: "[?] " +0x2fd0: "[?] " +0x2fd1: "[?] " +0x2fd2: "[?] " +0x2fd3: "[?] " +0x2fd4: "[?] " +0x2fd5: "[?] " +0x2fd6: "[?]" +0x2fd7: "[?]" +0x2fd8: "[?]" +0x2fd9: "[?]" +0x2fda: "[?]" +0x2fdb: "[?]" +0x2fdc: "[?]" +0x2fdd: "[?]" +0x2fde: "[?]" +0x2fdf: "[?]" +0x2fe0: "[?]" +0x2fe1: "[?]" +0x2fe2: "[?]" +0x2fe3: "[?]" +0x2fe4: "[?]" +0x2fe5: "[?]" +0x2fe6: "[?]" +0x2fe7: "[?]" +0x2fe8: "[?]" +0x2fe9: "[?]" +0x2fea: "[?]" +0x2feb: "[?]" +0x2fec: "[?]" +0x2fed: "[?]" +0x2fee: "[?]" +0x2fef: "[?]" +0x2ff0: "[?] " +0x2ff1: "[?] " +0x2ff2: "[?] " +0x2ff3: "[?] " +0x2ff4: "[?] " +0x2ff5: "[?] " +0x2ff6: "[?] " +0x2ff7: "[?] " +0x2ff8: "[?] " +0x2ff9: "[?] " +0x2ffa: "[?] " +0x2ffb: "[?] " +0x2ffc: "[?]" +0x2ffd: "[?]" +0x2ffe: "[?]" +/* x030 */ +0x3000: " " +0x3001: ", " +0x3002: ". " +0x3003: "\"" +0x3004: "[JIS]" +0x3005: "\"" +0x3006: "/" +0x3007: "0" +0x3008: "<" +0x3009: "> " +0x300a: "<<" +0x300b: ">> " +0x300c: "[" +0x300d: "] " +0x300e: "{" +0x300f: "} " +0x3010: "[(" +0x3011: ")] " +0x3012: "@" +0x3013: "X " +0x3014: "[" +0x3015: "] " +0x3016: "[[" +0x3017: "]] " +0x3018: "((" +0x3019: ")) " +0x301a: "[[" +0x301b: "]] " +0x301c: "~ " +0x301d: "``" +0x301e: "''" +0x301f: ",," +0x3020: "@" +0x3021: "1" +0x3022: "2" +0x3023: "3" +0x3024: "4" +0x3025: "5" +0x3026: "6" +0x3027: "7" +0x3028: "8" +0x3029: "9" +0x302a: "" +0x302b: "" +0x302c: "" +0x302d: "" +0x302e: "" +0x302f: "" +0x3030: "~" +0x3031: "+" +0x3032: "+" +0x3033: "+" +0x3034: "+" +0x3035: "" +0x3036: "@" +0x3037: " // " +0x3038: "+10+" +0x3039: "+20+" +0x303a: "+30+" +0x303b: "[?]" +0x303c: "[?]" +0x303d: "[?]" +0x303e: "" +0x303f: "" +0x3040: "[?]" +0x3041: "a" +0x3042: "a" +0x3043: "i" +0x3044: "i" +0x3045: "u" +0x3046: "u" +0x3047: "e" +0x3048: "e" +0x3049: "o" +0x304a: "o" +0x304b: "ka" +0x304c: "ga" +0x304d: "ki" +0x304e: "gi" +0x304f: "ku" +0x3050: "gu" +0x3051: "ke" +0x3052: "ge" +0x3053: "ko" +0x3054: "go" +0x3055: "sa" +0x3056: "za" +0x3057: "shi" +0x3058: "zi" +0x3059: "su" +0x305a: "zu" +0x305b: "se" +0x305c: "ze" +0x305d: "so" +0x305e: "zo" +0x305f: "ta" +0x3060: "da" +0x3061: "chi" +0x3062: "di" +0x3063: "tsu" +0x3064: "tsu" +0x3065: "du" +0x3066: "te" +0x3067: "de" +0x3068: "to" +0x3069: "do" +0x306a: "na" +0x306b: "ni" +0x306c: "nu" +0x306d: "ne" +0x306e: "no" +0x306f: "ha" +0x3070: "ba" +0x3071: "pa" +0x3072: "hi" +0x3073: "bi" +0x3074: "pi" +0x3075: "hu" +0x3076: "bu" +0x3077: "pu" +0x3078: "he" +0x3079: "be" +0x307a: "pe" +0x307b: "ho" +0x307c: "bo" +0x307d: "po" +0x307e: "ma" +0x307f: "mi" +0x3080: "mu" +0x3081: "me" +0x3082: "mo" +0x3083: "ya" +0x3084: "ya" +0x3085: "yu" +0x3086: "yu" +0x3087: "yo" +0x3088: "yo" +0x3089: "ra" +0x308a: "ri" +0x308b: "ru" +0x308c: "re" +0x308d: "ro" +0x308e: "wa" +0x308f: "wa" +0x3090: "wi" +0x3091: "we" +0x3092: "wo" +0x3093: "n" +0x3094: "vu" +0x3095: "[?]" +0x3096: "[?]" +0x3097: "[?]" +0x3098: "[?]" +0x3099: "" +0x309a: "" +0x309b: "" +0x309c: "" +0x309d: "\"" +0x309e: "\"" +0x309f: "[?]" +0x30a0: "[?]" +0x30a1: "a" +0x30a2: "a" +0x30a3: "i" +0x30a4: "i" +0x30a5: "u" +0x30a6: "u" +0x30a7: "e" +0x30a8: "e" +0x30a9: "o" +0x30aa: "o" +0x30ab: "ka" +0x30ac: "ga" +0x30ad: "ki" +0x30ae: "gi" +0x30af: "ku" +0x30b0: "gu" +0x30b1: "ke" +0x30b2: "ge" +0x30b3: "ko" +0x30b4: "go" +0x30b5: "sa" +0x30b6: "za" +0x30b7: "shi" +0x30b8: "zi" +0x30b9: "su" +0x30ba: "zu" +0x30bb: "se" +0x30bc: "ze" +0x30bd: "so" +0x30be: "zo" +0x30bf: "ta" +0x30c0: "da" +0x30c1: "chi" +0x30c2: "di" +0x30c3: "tsu" +0x30c4: "tsu" +0x30c5: "du" +0x30c6: "te" +0x30c7: "de" +0x30c8: "to" +0x30c9: "do" +0x30ca: "na" +0x30cb: "ni" +0x30cc: "nu" +0x30cd: "ne" +0x30ce: "no" +0x30cf: "ha" +0x30d0: "ba" +0x30d1: "pa" +0x30d2: "hi" +0x30d3: "bi" +0x30d4: "pi" +0x30d5: "hu" +0x30d6: "bu" +0x30d7: "pu" +0x30d8: "he" +0x30d9: "be" +0x30da: "pe" +0x30db: "ho" +0x30dc: "bo" +0x30dd: "po" +0x30de: "ma" +0x30df: "mi" +0x30e0: "mu" +0x30e1: "me" +0x30e2: "mo" +0x30e3: "ya" +0x30e4: "ya" +0x30e5: "yu" +0x30e6: "yu" +0x30e7: "yo" +0x30e8: "yo" +0x30e9: "ra" +0x30ea: "ri" +0x30eb: "ru" +0x30ec: "re" +0x30ed: "ro" +0x30ee: "wa" +0x30ef: "wa" +0x30f0: "wi" +0x30f1: "we" +0x30f2: "wo" +0x30f3: "n" +0x30f4: "vu" +0x30f5: "ka" +0x30f6: "ke" +0x30f7: "va" +0x30f8: "vi" +0x30f9: "ve" +0x30fa: "vo" +0x30fb: "" +0x30fc: "" +0x30fd: "\"" +0x30fe: "\"" +/* x031 */ +0x3100: "[?]" +0x3101: "[?]" +0x3102: "[?]" +0x3103: "[?]" +0x3104: "[?]" +0x3105: "B" +0x3106: "P" +0x3107: "M" +0x3108: "F" +0x3109: "D" +0x310a: "T" +0x310b: "N" +0x310c: "L" +0x310d: "G" +0x310e: "K" +0x310f: "H" +0x3110: "J" +0x3111: "Q" +0x3112: "X" +0x3113: "ZH" +0x3114: "CH" +0x3115: "SH" +0x3116: "R" +0x3117: "Z" +0x3118: "C" +0x3119: "S" +0x311a: "A" +0x311b: "O" +0x311c: "E" +0x311d: "EH" +0x311e: "AI" +0x311f: "EI" +0x3120: "AU" +0x3121: "OU" +0x3122: "AN" +0x3123: "EN" +0x3124: "ANG" +0x3125: "ENG" +0x3126: "ER" +0x3127: "I" +0x3128: "U" +0x3129: "IU" +0x312a: "V" +0x312b: "NG" +0x312c: "GN" +0x312d: "[?]" +0x312e: "[?]" +0x312f: "[?]" +0x3130: "[?]" +0x3131: "g" +0x3132: "gg" +0x3133: "gs" +0x3134: "n" +0x3135: "nj" +0x3136: "nh" +0x3137: "d" +0x3138: "dd" +0x3139: "r" +0x313a: "lg" +0x313b: "lm" +0x313c: "lb" +0x313d: "ls" +0x313e: "lt" +0x313f: "lp" +0x3140: "rh" +0x3141: "m" +0x3142: "b" +0x3143: "bb" +0x3144: "bs" +0x3145: "s" +0x3146: "ss" +0x3147: "" +0x3148: "j" +0x3149: "jj" +0x314a: "c" +0x314b: "k" +0x314c: "t" +0x314d: "p" +0x314e: "h" +0x314f: "a" +0x3150: "ae" +0x3151: "ya" +0x3152: "yae" +0x3153: "eo" +0x3154: "e" +0x3155: "yeo" +0x3156: "ye" +0x3157: "o" +0x3158: "wa" +0x3159: "wae" +0x315a: "oe" +0x315b: "yo" +0x315c: "u" +0x315d: "weo" +0x315e: "we" +0x315f: "wi" +0x3160: "yu" +0x3161: "eu" +0x3162: "yi" +0x3163: "i" +0x3164: "" +0x3165: "nn" +0x3166: "nd" +0x3167: "ns" +0x3168: "nZ" +0x3169: "lgs" +0x316a: "ld" +0x316b: "lbs" +0x316c: "lZ" +0x316d: "lQ" +0x316e: "mb" +0x316f: "ms" +0x3170: "mZ" +0x3171: "mN" +0x3172: "bg" +0x3173: "" +0x3174: "bsg" +0x3175: "bst" +0x3176: "bj" +0x3177: "bt" +0x3178: "bN" +0x3179: "bbN" +0x317a: "sg" +0x317b: "sn" +0x317c: "sd" +0x317d: "sb" +0x317e: "sj" +0x317f: "Z" +0x3180: "" +0x3181: "N" +0x3182: "Ns" +0x3183: "NZ" +0x3184: "pN" +0x3185: "hh" +0x3186: "Q" +0x3187: "yo-ya" +0x3188: "yo-yae" +0x3189: "yo-i" +0x318a: "yu-yeo" +0x318b: "yu-ye" +0x318c: "yu-i" +0x318d: "U" +0x318e: "U-i" +0x318f: "[?]" +0x3190: "" +0x3191: "" +0x3192: "" +0x3193: "" +0x3194: "" +0x3195: "" +0x3196: "" +0x3197: "" +0x3198: "" +0x3199: "" +0x319a: "" +0x319b: "" +0x319c: "" +0x319d: "" +0x319e: "" +0x319f: "" +0x31a0: "BU" +0x31a1: "ZI" +0x31a2: "JI" +0x31a3: "GU" +0x31a4: "EE" +0x31a5: "ENN" +0x31a6: "OO" +0x31a7: "ONN" +0x31a8: "IR" +0x31a9: "ANN" +0x31aa: "INN" +0x31ab: "UNN" +0x31ac: "IM" +0x31ad: "NGG" +0x31ae: "AINN" +0x31af: "AUNN" +0x31b0: "AM" +0x31b1: "OM" +0x31b2: "ONG" +0x31b3: "INNN" +0x31b4: "P" +0x31b5: "T" +0x31b6: "K" +0x31b7: "H" +0x31b8: "[?]" +0x31b9: "[?]" +0x31ba: "[?]" +0x31bb: "[?]" +0x31bc: "[?]" +0x31bd: "[?]" +0x31be: "[?]" +0x31bf: "[?]" +0x31c0: "[?]" +0x31c1: "[?]" +0x31c2: "[?]" +0x31c3: "[?]" +0x31c4: "[?]" +0x31c5: "[?]" +0x31c6: "[?]" +0x31c7: "[?]" +0x31c8: "[?]" +0x31c9: "[?]" +0x31ca: "[?]" +0x31cb: "[?]" +0x31cc: "[?]" +0x31cd: "[?]" +0x31ce: "[?]" +0x31cf: "[?]" +0x31d0: "[?]" +0x31d1: "[?]" +0x31d2: "[?]" +0x31d3: "[?]" +0x31d4: "[?]" +0x31d5: "[?]" +0x31d6: "[?]" +0x31d7: "[?]" +0x31d8: "[?]" +0x31d9: "[?]" +0x31da: "[?]" +0x31db: "[?]" +0x31dc: "[?]" +0x31dd: "[?]" +0x31de: "[?]" +0x31df: "[?]" +0x31e0: "[?]" +0x31e1: "[?]" +0x31e2: "[?]" +0x31e3: "[?]" +0x31e4: "[?]" +0x31e5: "[?]" +0x31e6: "[?]" +0x31e7: "[?]" +0x31e8: "[?]" +0x31e9: "[?]" +0x31ea: "[?]" +0x31eb: "[?]" +0x31ec: "[?]" +0x31ed: "[?]" +0x31ee: "[?]" +0x31ef: "[?]" +0x31f0: "[?]" +0x31f1: "[?]" +0x31f2: "[?]" +0x31f3: "[?]" +0x31f4: "[?]" +0x31f5: "[?]" +0x31f6: "[?]" +0x31f7: "[?]" +0x31f8: "[?]" +0x31f9: "[?]" +0x31fa: "[?]" +0x31fb: "[?]" +0x31fc: "[?]" +0x31fd: "[?]" +0x31fe: "[?]" +/* x032 */ +0x3200: "(g)" +0x3201: "(n)" +0x3202: "(d)" +0x3203: "(r)" +0x3204: "(m)" +0x3205: "(b)" +0x3206: "(s)" +0x3207: "()" +0x3208: "(j)" +0x3209: "(c)" +0x320a: "(k)" +0x320b: "(t)" +0x320c: "(p)" +0x320d: "(h)" +0x320e: "(ga)" +0x320f: "(na)" +0x3210: "(da)" +0x3211: "(ra)" +0x3212: "(ma)" +0x3213: "(ba)" +0x3214: "(sa)" +0x3215: "(a)" +0x3216: "(ja)" +0x3217: "(ca)" +0x3218: "(ka)" +0x3219: "(ta)" +0x321a: "(pa)" +0x321b: "(ha)" +0x321c: "(ju)" +0x321d: "[?]" +0x321e: "[?]" +0x321f: "[?]" +0x3220: "(1) " +0x3221: "(2) " +0x3222: "(3) " +0x3223: "(4) " +0x3224: "(5) " +0x3225: "(6) " +0x3226: "(7) " +0x3227: "(8) " +0x3228: "(9) " +0x3229: "(10) " +0x322a: "(Yue) " +0x322b: "(Huo) " +0x322c: "(Shui) " +0x322d: "(Mu) " +0x322e: "(Jin) " +0x322f: "(Tu) " +0x3230: "(Ri) " +0x3231: "(Zhu) " +0x3232: "(You) " +0x3233: "(She) " +0x3234: "(Ming) " +0x3235: "(Te) " +0x3236: "(Cai) " +0x3237: "(Zhu) " +0x3238: "(Lao) " +0x3239: "(Dai) " +0x323a: "(Hu) " +0x323b: "(Xue) " +0x323c: "(Jian) " +0x323d: "(Qi) " +0x323e: "(Zi) " +0x323f: "(Xie) " +0x3240: "(Ji) " +0x3241: "(Xiu) " +0x3242: "<<" +0x3243: ">>" +0x3244: "[?]" +0x3245: "[?]" +0x3246: "[?]" +0x3247: "[?]" +0x3248: "[?]" +0x3249: "[?]" +0x324a: "[?]" +0x324b: "[?]" +0x324c: "[?]" +0x324d: "[?]" +0x324e: "[?]" +0x324f: "[?]" +0x3250: "[?]" +0x3251: "[?]" +0x3252: "[?]" +0x3253: "[?]" +0x3254: "[?]" +0x3255: "[?]" +0x3256: "[?]" +0x3257: "[?]" +0x3258: "[?]" +0x3259: "[?]" +0x325a: "[?]" +0x325b: "[?]" +0x325c: "[?]" +0x325d: "[?]" +0x325e: "[?]" +0x325f: "[?]" +0x3260: "(g)" +0x3261: "(n)" +0x3262: "(d)" +0x3263: "(r)" +0x3264: "(m)" +0x3265: "(b)" +0x3266: "(s)" +0x3267: "()" +0x3268: "(j)" +0x3269: "(c)" +0x326a: "(k)" +0x326b: "(t)" +0x326c: "(p)" +0x326d: "(h)" +0x326e: "(ga)" +0x326f: "(na)" +0x3270: "(da)" +0x3271: "(ra)" +0x3272: "(ma)" +0x3273: "(ba)" +0x3274: "(sa)" +0x3275: "(a)" +0x3276: "(ja)" +0x3277: "(ca)" +0x3278: "(ka)" +0x3279: "(ta)" +0x327a: "(pa)" +0x327b: "(ha)" +0x327c: "[?]" +0x327d: "[?]" +0x327e: "[?]" +0x327f: "KIS " +0x3280: "(1) " +0x3281: "(2) " +0x3282: "(3) " +0x3283: "(4) " +0x3284: "(5) " +0x3285: "(6) " +0x3286: "(7) " +0x3287: "(8) " +0x3288: "(9) " +0x3289: "(10) " +0x328a: "(Yue) " +0x328b: "(Huo) " +0x328c: "(Shui) " +0x328d: "(Mu) " +0x328e: "(Jin) " +0x328f: "(Tu) " +0x3290: "(Ri) " +0x3291: "(Zhu) " +0x3292: "(You) " +0x3293: "(She) " +0x3294: "(Ming) " +0x3295: "(Te) " +0x3296: "(Cai) " +0x3297: "(Zhu) " +0x3298: "(Lao) " +0x3299: "(Mi) " +0x329a: "(Nan) " +0x329b: "(Nu) " +0x329c: "(Shi) " +0x329d: "(You) " +0x329e: "(Yin) " +0x329f: "(Zhu) " +0x32a0: "(Xiang) " +0x32a1: "(Xiu) " +0x32a2: "(Xie) " +0x32a3: "(Zheng) " +0x32a4: "(Shang) " +0x32a5: "(Zhong) " +0x32a6: "(Xia) " +0x32a7: "(Zuo) " +0x32a8: "(You) " +0x32a9: "(Yi) " +0x32aa: "(Zong) " +0x32ab: "(Xue) " +0x32ac: "(Jian) " +0x32ad: "(Qi) " +0x32ae: "(Zi) " +0x32af: "(Xie) " +0x32b0: "(Ye) " +0x32b1: "[?]" +0x32b2: "[?]" +0x32b3: "[?]" +0x32b4: "[?]" +0x32b5: "[?]" +0x32b6: "[?]" +0x32b7: "[?]" +0x32b8: "[?]" +0x32b9: "[?]" +0x32ba: "[?]" +0x32bb: "[?]" +0x32bc: "[?]" +0x32bd: "[?]" +0x32be: "[?]" +0x32bf: "[?]" +0x32c0: "1M" +0x32c1: "2M" +0x32c2: "3M" +0x32c3: "4M" +0x32c4: "5M" +0x32c5: "6M" +0x32c6: "7M" +0x32c7: "8M" +0x32c8: "9M" +0x32c9: "10M" +0x32ca: "11M" +0x32cb: "12M" +0x32cc: "[?]" +0x32cd: "[?]" +0x32ce: "[?]" +0x32cf: "[?]" +0x32d0: "a" +0x32d1: "i" +0x32d2: "u" +0x32d3: "u" +0x32d4: "o" +0x32d5: "ka" +0x32d6: "ki" +0x32d7: "ku" +0x32d8: "ke" +0x32d9: "ko" +0x32da: "sa" +0x32db: "si" +0x32dc: "su" +0x32dd: "se" +0x32de: "so" +0x32df: "ta" +0x32e0: "ti" +0x32e1: "tu" +0x32e2: "te" +0x32e3: "to" +0x32e4: "na" +0x32e5: "ni" +0x32e6: "nu" +0x32e7: "ne" +0x32e8: "no" +0x32e9: "ha" +0x32ea: "hi" +0x32eb: "hu" +0x32ec: "he" +0x32ed: "ho" +0x32ee: "ma" +0x32ef: "mi" +0x32f0: "mu" +0x32f1: "me" +0x32f2: "mo" +0x32f3: "ya" +0x32f4: "yu" +0x32f5: "yo" +0x32f6: "ra" +0x32f7: "ri" +0x32f8: "ru" +0x32f9: "re" +0x32fa: "ro" +0x32fb: "wa" +0x32fc: "wi" +0x32fd: "we" +0x32fe: "wo" +/* x033 */ +0x3300: "apartment" +0x3301: "alpha" +0x3302: "ampere" +0x3303: "are" +0x3304: "inning" +0x3305: "inch" +0x3306: "won" +0x3307: "escudo" +0x3308: "acre" +0x3309: "ounce" +0x330a: "ohm" +0x330b: "kai-ri" +0x330c: "carat" +0x330d: "calorie" +0x330e: "gallon" +0x330f: "gamma" +0x3310: "giga" +0x3311: "guinea" +0x3312: "curie" +0x3313: "guilder" +0x3314: "kilo" +0x3315: "kilogram" +0x3316: "kilometer" +0x3317: "kilowatt" +0x3318: "gram" +0x3319: "gram ton" +0x331a: "cruzeiro" +0x331b: "krone" +0x331c: "case" +0x331d: "koruna" +0x331e: "co-op" +0x331f: "cycle" +0x3320: "centime" +0x3321: "shilling" +0x3322: "centi" +0x3323: "cent" +0x3324: "dozen" +0x3325: "desi" +0x3326: "dollar" +0x3327: "ton" +0x3328: "nano" +0x3329: "knot" +0x332a: "heights" +0x332b: "percent" +0x332c: "parts" +0x332d: "barrel" +0x332e: "piaster" +0x332f: "picul" +0x3330: "pico" +0x3331: "building" +0x3332: "farad" +0x3333: "feet" +0x3334: "bushel" +0x3335: "franc" +0x3336: "hectare" +0x3337: "peso" +0x3338: "pfennig" +0x3339: "hertz" +0x333a: "pence" +0x333b: "page" +0x333c: "beta" +0x333d: "point" +0x333e: "volt" +0x333f: "hon" +0x3340: "pound" +0x3341: "hall" +0x3342: "horn" +0x3343: "micro" +0x3344: "mile" +0x3345: "mach" +0x3346: "mark" +0x3347: "mansion" +0x3348: "micron" +0x3349: "milli" +0x334a: "millibar" +0x334b: "mega" +0x334c: "megaton" +0x334d: "meter" +0x334e: "yard" +0x334f: "yard" +0x3350: "yuan" +0x3351: "liter" +0x3352: "lira" +0x3353: "rupee" +0x3354: "ruble" +0x3355: "rem" +0x3356: "roentgen" +0x3357: "watt" +0x3358: "0h" +0x3359: "1h" +0x335a: "2h" +0x335b: "3h" +0x335c: "4h" +0x335d: "5h" +0x335e: "6h" +0x335f: "7h" +0x3360: "8h" +0x3361: "9h" +0x3362: "10h" +0x3363: "11h" +0x3364: "12h" +0x3365: "13h" +0x3366: "14h" +0x3367: "15h" +0x3368: "16h" +0x3369: "17h" +0x336a: "18h" +0x336b: "19h" +0x336c: "20h" +0x336d: "21h" +0x336e: "22h" +0x336f: "23h" +0x3370: "24h" +0x3371: "HPA" +0x3372: "da" +0x3373: "AU" +0x3374: "bar" +0x3375: "oV" +0x3376: "pc" +0x3377: "[?]" +0x3378: "[?]" +0x3379: "[?]" +0x337a: "[?]" +0x337b: "Heisei" +0x337c: "Syouwa" +0x337d: "Taisyou" +0x337e: "Meiji" +0x337f: "Inc." +0x3380: "pA" +0x3381: "nA" +0x3382: "microamp" +0x3383: "mA" +0x3384: "kA" +0x3385: "kB" +0x3386: "MB" +0x3387: "GB" +0x3388: "cal" +0x3389: "kcal" +0x338a: "pF" +0x338b: "nF" +0x338c: "microFarad" +0x338d: "microgram" +0x338e: "mg" +0x338f: "kg" +0x3390: "Hz" +0x3391: "kHz" +0x3392: "MHz" +0x3393: "GHz" +0x3394: "THz" +0x3395: "microliter" +0x3396: "ml" +0x3397: "dl" +0x3398: "kl" +0x3399: "fm" +0x339a: "nm" +0x339b: "micrometer" +0x339c: "mm" +0x339d: "cm" +0x339e: "km" +0x339f: "mm^2" +0x33a0: "cm^2" +0x33a1: "m^2" +0x33a2: "km^2" +0x33a3: "mm^4" +0x33a4: "cm^3" +0x33a5: "m^3" +0x33a6: "km^3" +0x33a7: "m/s" +0x33a8: "m/s^2" +0x33a9: "Pa" +0x33aa: "kPa" +0x33ab: "MPa" +0x33ac: "GPa" +0x33ad: "rad" +0x33ae: "rad/s" +0x33af: "rad/s^2" +0x33b0: "ps" +0x33b1: "ns" +0x33b2: "microsecond" +0x33b3: "ms" +0x33b4: "pV" +0x33b5: "nV" +0x33b6: "microvolt" +0x33b7: "mV" +0x33b8: "kV" +0x33b9: "MV" +0x33ba: "pW" +0x33bb: "nW" +0x33bc: "microwatt" +0x33bd: "mW" +0x33be: "kW" +0x33bf: "MW" +0x33c0: "kOhm" +0x33c1: "MOhm" +0x33c2: "a.m." +0x33c3: "Bq" +0x33c4: "cc" +0x33c5: "cd" +0x33c6: "C/kg" +0x33c7: "Co." +0x33c8: "dB" +0x33c9: "Gy" +0x33ca: "ha" +0x33cb: "HP" +0x33cc: "in" +0x33cd: "K.K." +0x33ce: "KM" +0x33cf: "kt" +0x33d0: "lm" +0x33d1: "ln" +0x33d2: "log" +0x33d3: "lx" +0x33d4: "mb" +0x33d5: "mil" +0x33d6: "mol" +0x33d7: "pH" +0x33d8: "p.m." +0x33d9: "PPM" +0x33da: "PR" +0x33db: "sr" +0x33dc: "Sv" +0x33dd: "Wb" +0x33de: "[?]" +0x33df: "[?]" +0x33e0: "1d" +0x33e1: "2d" +0x33e2: "3d" +0x33e3: "4d" +0x33e4: "5d" +0x33e5: "6d" +0x33e6: "7d" +0x33e7: "8d" +0x33e8: "9d" +0x33e9: "10d" +0x33ea: "11d" +0x33eb: "12d" +0x33ec: "13d" +0x33ed: "14d" +0x33ee: "15d" +0x33ef: "16d" +0x33f0: "17d" +0x33f1: "18d" +0x33f2: "19d" +0x33f3: "20d" +0x33f4: "21d" +0x33f5: "22d" +0x33f6: "23d" +0x33f7: "24d" +0x33f8: "25d" +0x33f9: "26d" +0x33fa: "27d" +0x33fb: "28d" +0x33fc: "29d" +0x33fd: "30d" +0x33fe: "31d" +/* x04d */ +0x4d00: "[?] " +0x4d01: "[?] " +0x4d02: "[?] " +0x4d03: "[?] " +0x4d04: "[?] " +0x4d05: "[?] " +0x4d06: "[?] " +0x4d07: "[?] " +0x4d08: "[?] " +0x4d09: "[?] " +0x4d0a: "[?] " +0x4d0b: "[?] " +0x4d0c: "[?] " +0x4d0d: "[?] " +0x4d0e: "[?] " +0x4d0f: "[?] " +0x4d10: "[?] " +0x4d11: "[?] " +0x4d12: "[?] " +0x4d13: "[?] " +0x4d14: "[?] " +0x4d15: "[?] " +0x4d16: "[?] " +0x4d17: "[?] " +0x4d18: "[?] " +0x4d19: "[?] " +0x4d1a: "[?] " +0x4d1b: "[?] " +0x4d1c: "[?] " +0x4d1d: "[?] " +0x4d1e: "[?] " +0x4d1f: "[?] " +0x4d20: "[?] " +0x4d21: "[?] " +0x4d22: "[?] " +0x4d23: "[?] " +0x4d24: "[?] " +0x4d25: "[?] " +0x4d26: "[?] " +0x4d27: "[?] " +0x4d28: "[?] " +0x4d29: "[?] " +0x4d2a: "[?] " +0x4d2b: "[?] " +0x4d2c: "[?] " +0x4d2d: "[?] " +0x4d2e: "[?] " +0x4d2f: "[?] " +0x4d30: "[?] " +0x4d31: "[?] " +0x4d32: "[?] " +0x4d33: "[?] " +0x4d34: "[?] " +0x4d35: "[?] " +0x4d36: "[?] " +0x4d37: "[?] " +0x4d38: "[?] " +0x4d39: "[?] " +0x4d3a: "[?] " +0x4d3b: "[?] " +0x4d3c: "[?] " +0x4d3d: "[?] " +0x4d3e: "[?] " +0x4d3f: "[?] " +0x4d40: "[?] " +0x4d41: "[?] " +0x4d42: "[?] " +0x4d43: "[?] " +0x4d44: "[?] " +0x4d45: "[?] " +0x4d46: "[?] " +0x4d47: "[?] " +0x4d48: "[?] " +0x4d49: "[?] " +0x4d4a: "[?] " +0x4d4b: "[?] " +0x4d4c: "[?] " +0x4d4d: "[?] " +0x4d4e: "[?] " +0x4d4f: "[?] " +0x4d50: "[?] " +0x4d51: "[?] " +0x4d52: "[?] " +0x4d53: "[?] " +0x4d54: "[?] " +0x4d55: "[?] " +0x4d56: "[?] " +0x4d57: "[?] " +0x4d58: "[?] " +0x4d59: "[?] " +0x4d5a: "[?] " +0x4d5b: "[?] " +0x4d5c: "[?] " +0x4d5d: "[?] " +0x4d5e: "[?] " +0x4d5f: "[?] " +0x4d60: "[?] " +0x4d61: "[?] " +0x4d62: "[?] " +0x4d63: "[?] " +0x4d64: "[?] " +0x4d65: "[?] " +0x4d66: "[?] " +0x4d67: "[?] " +0x4d68: "[?] " +0x4d69: "[?] " +0x4d6a: "[?] " +0x4d6b: "[?] " +0x4d6c: "[?] " +0x4d6d: "[?] " +0x4d6e: "[?] " +0x4d6f: "[?] " +0x4d70: "[?] " +0x4d71: "[?] " +0x4d72: "[?] " +0x4d73: "[?] " +0x4d74: "[?] " +0x4d75: "[?] " +0x4d76: "[?] " +0x4d77: "[?] " +0x4d78: "[?] " +0x4d79: "[?] " +0x4d7a: "[?] " +0x4d7b: "[?] " +0x4d7c: "[?] " +0x4d7d: "[?] " +0x4d7e: "[?] " +0x4d7f: "[?] " +0x4d80: "[?] " +0x4d81: "[?] " +0x4d82: "[?] " +0x4d83: "[?] " +0x4d84: "[?] " +0x4d85: "[?] " +0x4d86: "[?] " +0x4d87: "[?] " +0x4d88: "[?] " +0x4d89: "[?] " +0x4d8a: "[?] " +0x4d8b: "[?] " +0x4d8c: "[?] " +0x4d8d: "[?] " +0x4d8e: "[?] " +0x4d8f: "[?] " +0x4d90: "[?] " +0x4d91: "[?] " +0x4d92: "[?] " +0x4d93: "[?] " +0x4d94: "[?] " +0x4d95: "[?] " +0x4d96: "[?] " +0x4d97: "[?] " +0x4d98: "[?] " +0x4d99: "[?] " +0x4d9a: "[?] " +0x4d9b: "[?] " +0x4d9c: "[?] " +0x4d9d: "[?] " +0x4d9e: "[?] " +0x4d9f: "[?] " +0x4da0: "[?] " +0x4da1: "[?] " +0x4da2: "[?] " +0x4da3: "[?] " +0x4da4: "[?] " +0x4da5: "[?] " +0x4da6: "[?] " +0x4da7: "[?] " +0x4da8: "[?] " +0x4da9: "[?] " +0x4daa: "[?] " +0x4dab: "[?] " +0x4dac: "[?] " +0x4dad: "[?] " +0x4dae: "[?] " +0x4daf: "[?] " +0x4db0: "[?] " +0x4db1: "[?] " +0x4db2: "[?] " +0x4db3: "[?] " +0x4db4: "[?] " +0x4db5: "[?] " +0x4db6: "[?]" +0x4db7: "[?]" +0x4db8: "[?]" +0x4db9: "[?]" +0x4dba: "[?]" +0x4dbb: "[?]" +0x4dbc: "[?]" +0x4dbd: "[?]" +0x4dbe: "[?]" +0x4dbf: "[?]" +0x4dc0: "[?]" +0x4dc1: "[?]" +0x4dc2: "[?]" +0x4dc3: "[?]" +0x4dc4: "[?]" +0x4dc5: "[?]" +0x4dc6: "[?]" +0x4dc7: "[?]" +0x4dc8: "[?]" +0x4dc9: "[?]" +0x4dca: "[?]" +0x4dcb: "[?]" +0x4dcc: "[?]" +0x4dcd: "[?]" +0x4dce: "[?]" +0x4dcf: "[?]" +0x4dd0: "[?]" +0x4dd1: "[?]" +0x4dd2: "[?]" +0x4dd3: "[?]" +0x4dd4: "[?]" +0x4dd5: "[?]" +0x4dd6: "[?]" +0x4dd7: "[?]" +0x4dd8: "[?]" +0x4dd9: "[?]" +0x4dda: "[?]" +0x4ddb: "[?]" +0x4ddc: "[?]" +0x4ddd: "[?]" +0x4dde: "[?]" +0x4ddf: "[?]" +0x4de0: "[?]" +0x4de1: "[?]" +0x4de2: "[?]" +0x4de3: "[?]" +0x4de4: "[?]" +0x4de5: "[?]" +0x4de6: "[?]" +0x4de7: "[?]" +0x4de8: "[?]" +0x4de9: "[?]" +0x4dea: "[?]" +0x4deb: "[?]" +0x4dec: "[?]" +0x4ded: "[?]" +0x4dee: "[?]" +0x4def: "[?]" +0x4df0: "[?]" +0x4df1: "[?]" +0x4df2: "[?]" +0x4df3: "[?]" +0x4df4: "[?]" +0x4df5: "[?]" +0x4df6: "[?]" +0x4df7: "[?]" +0x4df8: "[?]" +0x4df9: "[?]" +0x4dfa: "[?]" +0x4dfb: "[?]" +0x4dfc: "[?]" +0x4dfd: "[?]" +0x4dfe: "[?]" +/* x04e */ +0x4e00: "[?] " +0x4e01: "Ding " +0x4e02: "Kao " +0x4e03: "Qi " +0x4e04: "Shang " +0x4e05: "Xia " +0x4e06: "[?] " +0x4e07: "Mo " +0x4e08: "Zhang " +0x4e09: "San " +0x4e0a: "Shang " +0x4e0b: "Xia " +0x4e0c: "Ji " +0x4e0d: "Bu " +0x4e0e: "Yu " +0x4e0f: "Mian " +0x4e10: "Gai " +0x4e11: "Chou " +0x4e12: "Chou " +0x4e13: "Zhuan " +0x4e14: "Qie " +0x4e15: "Pi " +0x4e16: "Shi " +0x4e17: "Shi " +0x4e18: "Qiu " +0x4e19: "Bing " +0x4e1a: "Ye " +0x4e1b: "Cong " +0x4e1c: "Dong " +0x4e1d: "Si " +0x4e1e: "Cheng " +0x4e1f: "Diu " +0x4e20: "Qiu " +0x4e21: "Liang " +0x4e22: "Diu " +0x4e23: "You " +0x4e24: "Liang " +0x4e25: "Yan " +0x4e26: "Bing " +0x4e27: "Sang " +0x4e28: "Gun " +0x4e29: "Jiu " +0x4e2a: "Ge " +0x4e2b: "Ya " +0x4e2c: "Qiang " +0x4e2d: "Zhong " +0x4e2e: "Ji " +0x4e2f: "Jie " +0x4e30: "Feng " +0x4e31: "Guan " +0x4e32: "Chuan " +0x4e33: "Chan " +0x4e34: "Lin " +0x4e35: "Zhuo " +0x4e36: "Zhu " +0x4e37: "Ha " +0x4e38: "Wan " +0x4e39: "Dan " +0x4e3a: "Wei " +0x4e3b: "Zhu " +0x4e3c: "Jing " +0x4e3d: "Li " +0x4e3e: "Ju " +0x4e3f: "Pie " +0x4e40: "Fu " +0x4e41: "Yi " +0x4e42: "Yi " +0x4e43: "Nai " +0x4e44: "Shime " +0x4e45: "Jiu " +0x4e46: "Jiu " +0x4e47: "Zhe " +0x4e48: "Yao " +0x4e49: "Yi " +0x4e4a: "[?] " +0x4e4b: "Zhi " +0x4e4c: "Wu " +0x4e4d: "Zha " +0x4e4e: "Hu " +0x4e4f: "Fa " +0x4e50: "Le " +0x4e51: "Zhong " +0x4e52: "Ping " +0x4e53: "Pang " +0x4e54: "Qiao " +0x4e55: "Hu " +0x4e56: "Guai " +0x4e57: "Cheng " +0x4e58: "Cheng " +0x4e59: "Yi " +0x4e5a: "Yin " +0x4e5b: "[?] " +0x4e5c: "Mie " +0x4e5d: "Jiu " +0x4e5e: "Qi " +0x4e5f: "Ye " +0x4e60: "Xi " +0x4e61: "Xiang " +0x4e62: "Gai " +0x4e63: "Diu " +0x4e64: "Hal " +0x4e65: "[?] " +0x4e66: "Shu " +0x4e67: "Twul " +0x4e68: "Shi " +0x4e69: "Ji " +0x4e6a: "Nang " +0x4e6b: "Jia " +0x4e6c: "Kel " +0x4e6d: "Shi " +0x4e6e: "[?] " +0x4e6f: "Ol " +0x4e70: "Mai " +0x4e71: "Luan " +0x4e72: "Cal " +0x4e73: "Ru " +0x4e74: "Xue " +0x4e75: "Yan " +0x4e76: "Fu " +0x4e77: "Sha " +0x4e78: "Na " +0x4e79: "Gan " +0x4e7a: "Sol " +0x4e7b: "El " +0x4e7c: "Cwul " +0x4e7d: "[?] " +0x4e7e: "Gan " +0x4e7f: "Chi " +0x4e80: "Gui " +0x4e81: "Gan " +0x4e82: "Luan " +0x4e83: "Lin " +0x4e84: "Yi " +0x4e85: "Jue " +0x4e86: "Liao " +0x4e87: "Ma " +0x4e88: "Yu " +0x4e89: "Zheng " +0x4e8a: "Shi " +0x4e8b: "Shi " +0x4e8c: "Er " +0x4e8d: "Chu " +0x4e8e: "Yu " +0x4e8f: "Yu " +0x4e90: "Yu " +0x4e91: "Yun " +0x4e92: "Hu " +0x4e93: "Qi " +0x4e94: "Wu " +0x4e95: "Jing " +0x4e96: "Si " +0x4e97: "Sui " +0x4e98: "Gen " +0x4e99: "Gen " +0x4e9a: "Ya " +0x4e9b: "Xie " +0x4e9c: "Ya " +0x4e9d: "Qi " +0x4e9e: "Ya " +0x4e9f: "Ji " +0x4ea0: "Tou " +0x4ea1: "Wang " +0x4ea2: "Kang " +0x4ea3: "Ta " +0x4ea4: "Jiao " +0x4ea5: "Hai " +0x4ea6: "Yi " +0x4ea7: "Chan " +0x4ea8: "Heng " +0x4ea9: "Mu " +0x4eaa: "[?] " +0x4eab: "Xiang " +0x4eac: "Jing " +0x4ead: "Ting " +0x4eae: "Liang " +0x4eaf: "Xiang " +0x4eb0: "Jing " +0x4eb1: "Ye " +0x4eb2: "Qin " +0x4eb3: "Bo " +0x4eb4: "You " +0x4eb5: "Xie " +0x4eb6: "Dan " +0x4eb7: "Lian " +0x4eb8: "Duo " +0x4eb9: "Wei " +0x4eba: "Ren " +0x4ebb: "Ren " +0x4ebc: "Ji " +0x4ebd: "La " +0x4ebe: "Wang " +0x4ebf: "Yi " +0x4ec0: "Shi " +0x4ec1: "Ren " +0x4ec2: "Le " +0x4ec3: "Ding " +0x4ec4: "Ze " +0x4ec5: "Jin " +0x4ec6: "Pu " +0x4ec7: "Chou " +0x4ec8: "Ba " +0x4ec9: "Zhang " +0x4eca: "Jin " +0x4ecb: "Jie " +0x4ecc: "Bing " +0x4ecd: "Reng " +0x4ece: "Cong " +0x4ecf: "Fo " +0x4ed0: "San " +0x4ed1: "Lun " +0x4ed2: "Sya " +0x4ed3: "Cang " +0x4ed4: "Zi " +0x4ed5: "Shi " +0x4ed6: "Ta " +0x4ed7: "Zhang " +0x4ed8: "Fu " +0x4ed9: "Xian " +0x4eda: "Xian " +0x4edb: "Tuo " +0x4edc: "Hong " +0x4edd: "Tong " +0x4ede: "Ren " +0x4edf: "Qian " +0x4ee0: "Gan " +0x4ee1: "Yi " +0x4ee2: "Di " +0x4ee3: "Dai " +0x4ee4: "Ling " +0x4ee5: "Yi " +0x4ee6: "Chao " +0x4ee7: "Chang " +0x4ee8: "Sa " +0x4ee9: "[?] " +0x4eea: "Yi " +0x4eeb: "Mu " +0x4eec: "Men " +0x4eed: "Ren " +0x4eee: "Jia " +0x4eef: "Chao " +0x4ef0: "Yang " +0x4ef1: "Qian " +0x4ef2: "Zhong " +0x4ef3: "Pi " +0x4ef4: "Wan " +0x4ef5: "Wu " +0x4ef6: "Jian " +0x4ef7: "Jie " +0x4ef8: "Yao " +0x4ef9: "Feng " +0x4efa: "Cang " +0x4efb: "Ren " +0x4efc: "Wang " +0x4efd: "Fen " +0x4efe: "Di " +0x4eff: "Fang " +/* x04f */ +0x4f00: "Zhong " +0x4f01: "Qi " +0x4f02: "Pei " +0x4f03: "Yu " +0x4f04: "Diao " +0x4f05: "Dun " +0x4f06: "Wen " +0x4f07: "Yi " +0x4f08: "Xin " +0x4f09: "Kang " +0x4f0a: "Yi " +0x4f0b: "Ji " +0x4f0c: "Ai " +0x4f0d: "Wu " +0x4f0e: "Ji " +0x4f0f: "Fu " +0x4f10: "Fa " +0x4f11: "Xiu " +0x4f12: "Jin " +0x4f13: "Bei " +0x4f14: "Dan " +0x4f15: "Fu " +0x4f16: "Tang " +0x4f17: "Zhong " +0x4f18: "You " +0x4f19: "Huo " +0x4f1a: "Hui " +0x4f1b: "Yu " +0x4f1c: "Cui " +0x4f1d: "Chuan " +0x4f1e: "San " +0x4f1f: "Wei " +0x4f20: "Chuan " +0x4f21: "Che " +0x4f22: "Ya " +0x4f23: "Xian " +0x4f24: "Shang " +0x4f25: "Chang " +0x4f26: "Lun " +0x4f27: "Cang " +0x4f28: "Xun " +0x4f29: "Xin " +0x4f2a: "Wei " +0x4f2b: "Zhu " +0x4f2c: "[?] " +0x4f2d: "Xuan " +0x4f2e: "Nu " +0x4f2f: "Bo " +0x4f30: "Gu " +0x4f31: "Ni " +0x4f32: "Ni " +0x4f33: "Xie " +0x4f34: "Ban " +0x4f35: "Xu " +0x4f36: "Ling " +0x4f37: "Zhou " +0x4f38: "Shen " +0x4f39: "Qu " +0x4f3a: "Si " +0x4f3b: "Beng " +0x4f3c: "Si " +0x4f3d: "Jia " +0x4f3e: "Pi " +0x4f3f: "Yi " +0x4f40: "Si " +0x4f41: "Ai " +0x4f42: "Zheng " +0x4f43: "Dian " +0x4f44: "Han " +0x4f45: "Mai " +0x4f46: "Dan " +0x4f47: "Zhu " +0x4f48: "Bu " +0x4f49: "Qu " +0x4f4a: "Bi " +0x4f4b: "Shao " +0x4f4c: "Ci " +0x4f4d: "Wei " +0x4f4e: "Di " +0x4f4f: "Zhu " +0x4f50: "Zuo " +0x4f51: "You " +0x4f52: "Yang " +0x4f53: "Ti " +0x4f54: "Zhan " +0x4f55: "He " +0x4f56: "Bi " +0x4f57: "Tuo " +0x4f58: "She " +0x4f59: "Yu " +0x4f5a: "Yi " +0x4f5b: "Fo " +0x4f5c: "Zuo " +0x4f5d: "Kou " +0x4f5e: "Ning " +0x4f5f: "Tong " +0x4f60: "Ni " +0x4f61: "Xuan " +0x4f62: "Qu " +0x4f63: "Yong " +0x4f64: "Wa " +0x4f65: "Qian " +0x4f66: "[?] " +0x4f67: "Ka " +0x4f68: "[?] " +0x4f69: "Pei " +0x4f6a: "Huai " +0x4f6b: "He " +0x4f6c: "Lao " +0x4f6d: "Xiang " +0x4f6e: "Ge " +0x4f6f: "Yang " +0x4f70: "Bai " +0x4f71: "Fa " +0x4f72: "Ming " +0x4f73: "Jia " +0x4f74: "Er " +0x4f75: "Bing " +0x4f76: "Ji " +0x4f77: "Hen " +0x4f78: "Huo " +0x4f79: "Gui " +0x4f7a: "Quan " +0x4f7b: "Tiao " +0x4f7c: "Jiao " +0x4f7d: "Ci " +0x4f7e: "Yi " +0x4f7f: "Shi " +0x4f80: "Xing " +0x4f81: "Shen " +0x4f82: "Tuo " +0x4f83: "Kan " +0x4f84: "Zhi " +0x4f85: "Gai " +0x4f86: "Lai " +0x4f87: "Yi " +0x4f88: "Chi " +0x4f89: "Kua " +0x4f8a: "Guang " +0x4f8b: "Li " +0x4f8c: "Yin " +0x4f8d: "Shi " +0x4f8e: "Mi " +0x4f8f: "Zhu " +0x4f90: "Xu " +0x4f91: "You " +0x4f92: "An " +0x4f93: "Lu " +0x4f94: "Mou " +0x4f95: "Er " +0x4f96: "Lun " +0x4f97: "Tong " +0x4f98: "Cha " +0x4f99: "Chi " +0x4f9a: "Xun " +0x4f9b: "Gong " +0x4f9c: "Zhou " +0x4f9d: "Yi " +0x4f9e: "Ru " +0x4f9f: "Jian " +0x4fa0: "Xia " +0x4fa1: "Jia " +0x4fa2: "Zai " +0x4fa3: "Lu " +0x4fa4: "Ko " +0x4fa5: "Jiao " +0x4fa6: "Zhen " +0x4fa7: "Ce " +0x4fa8: "Qiao " +0x4fa9: "Kuai " +0x4faa: "Chai " +0x4fab: "Ning " +0x4fac: "Nong " +0x4fad: "Jin " +0x4fae: "Wu " +0x4faf: "Hou " +0x4fb0: "Jiong " +0x4fb1: "Cheng " +0x4fb2: "Zhen " +0x4fb3: "Zuo " +0x4fb4: "Chou " +0x4fb5: "Qin " +0x4fb6: "Lu " +0x4fb7: "Ju " +0x4fb8: "Shu " +0x4fb9: "Ting " +0x4fba: "Shen " +0x4fbb: "Tuo " +0x4fbc: "Bo " +0x4fbd: "Nan " +0x4fbe: "Hao " +0x4fbf: "Bian " +0x4fc0: "Tui " +0x4fc1: "Yu " +0x4fc2: "Xi " +0x4fc3: "Cu " +0x4fc4: "E " +0x4fc5: "Qiu " +0x4fc6: "Xu " +0x4fc7: "Kuang " +0x4fc8: "Ku " +0x4fc9: "Wu " +0x4fca: "Jun " +0x4fcb: "Yi " +0x4fcc: "Fu " +0x4fcd: "Lang " +0x4fce: "Zu " +0x4fcf: "Qiao " +0x4fd0: "Li " +0x4fd1: "Yong " +0x4fd2: "Hun " +0x4fd3: "Jing " +0x4fd4: "Xian " +0x4fd5: "San " +0x4fd6: "Pai " +0x4fd7: "Su " +0x4fd8: "Fu " +0x4fd9: "Xi " +0x4fda: "Li " +0x4fdb: "Fu " +0x4fdc: "Ping " +0x4fdd: "Bao " +0x4fde: "Yu " +0x4fdf: "Si " +0x4fe0: "Xia " +0x4fe1: "Xin " +0x4fe2: "Xiu " +0x4fe3: "Yu " +0x4fe4: "Ti " +0x4fe5: "Che " +0x4fe6: "Chou " +0x4fe7: "[?] " +0x4fe8: "Yan " +0x4fe9: "Lia " +0x4fea: "Li " +0x4feb: "Lai " +0x4fec: "[?] " +0x4fed: "Jian " +0x4fee: "Xiu " +0x4fef: "Fu " +0x4ff0: "He " +0x4ff1: "Ju " +0x4ff2: "Xiao " +0x4ff3: "Pai " +0x4ff4: "Jian " +0x4ff5: "Biao " +0x4ff6: "Chu " +0x4ff7: "Fei " +0x4ff8: "Feng " +0x4ff9: "Ya " +0x4ffa: "An " +0x4ffb: "Bei " +0x4ffc: "Yu " +0x4ffd: "Xin " +0x4ffe: "Bi " +0x4fff: "Jian " +/* x050 */ +0x5000: "Chang " +0x5001: "Chi " +0x5002: "Bing " +0x5003: "Zan " +0x5004: "Yao " +0x5005: "Cui " +0x5006: "Lia " +0x5007: "Wan " +0x5008: "Lai " +0x5009: "Cang " +0x500a: "Zong " +0x500b: "Ge " +0x500c: "Guan " +0x500d: "Bei " +0x500e: "Tian " +0x500f: "Shu " +0x5010: "Shu " +0x5011: "Men " +0x5012: "Dao " +0x5013: "Tan " +0x5014: "Jue " +0x5015: "Chui " +0x5016: "Xing " +0x5017: "Peng " +0x5018: "Tang " +0x5019: "Hou " +0x501a: "Yi " +0x501b: "Qi " +0x501c: "Ti " +0x501d: "Gan " +0x501e: "Jing " +0x501f: "Jie " +0x5020: "Sui " +0x5021: "Chang " +0x5022: "Jie " +0x5023: "Fang " +0x5024: "Zhi " +0x5025: "Kong " +0x5026: "Juan " +0x5027: "Zong " +0x5028: "Ju " +0x5029: "Qian " +0x502a: "Ni " +0x502b: "Lun " +0x502c: "Zhuo " +0x502d: "Wei " +0x502e: "Luo " +0x502f: "Song " +0x5030: "Leng " +0x5031: "Hun " +0x5032: "Dong " +0x5033: "Zi " +0x5034: "Ben " +0x5035: "Wu " +0x5036: "Ju " +0x5037: "Nai " +0x5038: "Cai " +0x5039: "Jian " +0x503a: "Zhai " +0x503b: "Ye " +0x503c: "Zhi " +0x503d: "Sha " +0x503e: "Qing " +0x503f: "[?] " +0x5040: "Ying " +0x5041: "Cheng " +0x5042: "Jian " +0x5043: "Yan " +0x5044: "Nuan " +0x5045: "Zhong " +0x5046: "Chun " +0x5047: "Jia " +0x5048: "Jie " +0x5049: "Wei " +0x504a: "Yu " +0x504b: "Bing " +0x504c: "Ruo " +0x504d: "Ti " +0x504e: "Wei " +0x504f: "Pian " +0x5050: "Yan " +0x5051: "Feng " +0x5052: "Tang " +0x5053: "Wo " +0x5054: "E " +0x5055: "Xie " +0x5056: "Che " +0x5057: "Sheng " +0x5058: "Kan " +0x5059: "Di " +0x505a: "Zuo " +0x505b: "Cha " +0x505c: "Ting " +0x505d: "Bei " +0x505e: "Ye " +0x505f: "Huang " +0x5060: "Yao " +0x5061: "Zhan " +0x5062: "Chou " +0x5063: "Yan " +0x5064: "You " +0x5065: "Jian " +0x5066: "Xu " +0x5067: "Zha " +0x5068: "Ci " +0x5069: "Fu " +0x506a: "Bi " +0x506b: "Zhi " +0x506c: "Zong " +0x506d: "Mian " +0x506e: "Ji " +0x506f: "Yi " +0x5070: "Xie " +0x5071: "Xun " +0x5072: "Si " +0x5073: "Duan " +0x5074: "Ce " +0x5075: "Zhen " +0x5076: "Ou " +0x5077: "Tou " +0x5078: "Tou " +0x5079: "Bei " +0x507a: "Za " +0x507b: "Lu " +0x507c: "Jie " +0x507d: "Wei " +0x507e: "Fen " +0x507f: "Chang " +0x5080: "Gui " +0x5081: "Sou " +0x5082: "Zhi " +0x5083: "Su " +0x5084: "Xia " +0x5085: "Fu " +0x5086: "Yuan " +0x5087: "Rong " +0x5088: "Li " +0x5089: "Ru " +0x508a: "Yun " +0x508b: "Gou " +0x508c: "Ma " +0x508d: "Bang " +0x508e: "Dian " +0x508f: "Tang " +0x5090: "Hao " +0x5091: "Jie " +0x5092: "Xi " +0x5093: "Shan " +0x5094: "Qian " +0x5095: "Jue " +0x5096: "Cang " +0x5097: "Chu " +0x5098: "San " +0x5099: "Bei " +0x509a: "Xiao " +0x509b: "Yong " +0x509c: "Yao " +0x509d: "Tan " +0x509e: "Suo " +0x509f: "Yang " +0x50a0: "Fa " +0x50a1: "Bing " +0x50a2: "Jia " +0x50a3: "Dai " +0x50a4: "Zai " +0x50a5: "Tang " +0x50a6: "[?] " +0x50a7: "Bin " +0x50a8: "Chu " +0x50a9: "Nuo " +0x50aa: "Can " +0x50ab: "Lei " +0x50ac: "Cui " +0x50ad: "Yong " +0x50ae: "Zao " +0x50af: "Zong " +0x50b0: "Peng " +0x50b1: "Song " +0x50b2: "Ao " +0x50b3: "Chuan " +0x50b4: "Yu " +0x50b5: "Zhai " +0x50b6: "Cou " +0x50b7: "Shang " +0x50b8: "Qiang " +0x50b9: "Jing " +0x50ba: "Chi " +0x50bb: "Sha " +0x50bc: "Han " +0x50bd: "Zhang " +0x50be: "Qing " +0x50bf: "Yan " +0x50c0: "Di " +0x50c1: "Xi " +0x50c2: "Lu " +0x50c3: "Bei " +0x50c4: "Piao " +0x50c5: "Jin " +0x50c6: "Lian " +0x50c7: "Lu " +0x50c8: "Man " +0x50c9: "Qian " +0x50ca: "Xian " +0x50cb: "Tan " +0x50cc: "Ying " +0x50cd: "Dong " +0x50ce: "Zhuan " +0x50cf: "Xiang " +0x50d0: "Shan " +0x50d1: "Qiao " +0x50d2: "Jiong " +0x50d3: "Tui " +0x50d4: "Zun " +0x50d5: "Pu " +0x50d6: "Xi " +0x50d7: "Lao " +0x50d8: "Chang " +0x50d9: "Guang " +0x50da: "Liao " +0x50db: "Qi " +0x50dc: "Deng " +0x50dd: "Chan " +0x50de: "Wei " +0x50df: "Ji " +0x50e0: "Fan " +0x50e1: "Hui " +0x50e2: "Chuan " +0x50e3: "Jian " +0x50e4: "Dan " +0x50e5: "Jiao " +0x50e6: "Jiu " +0x50e7: "Seng " +0x50e8: "Fen " +0x50e9: "Xian " +0x50ea: "Jue " +0x50eb: "E " +0x50ec: "Jiao " +0x50ed: "Jian " +0x50ee: "Tong " +0x50ef: "Lin " +0x50f0: "Bo " +0x50f1: "Gu " +0x50f2: "[?] " +0x50f3: "Su " +0x50f4: "Xian " +0x50f5: "Jiang " +0x50f6: "Min " +0x50f7: "Ye " +0x50f8: "Jin " +0x50f9: "Jia " +0x50fa: "Qiao " +0x50fb: "Pi " +0x50fc: "Feng " +0x50fd: "Zhou " +0x50fe: "Ai " +0x50ff: "Sai " +/* x051 */ +0x5100: "Yi " +0x5101: "Jun " +0x5102: "Nong " +0x5103: "Chan " +0x5104: "Yi " +0x5105: "Dang " +0x5106: "Jing " +0x5107: "Xuan " +0x5108: "Kuai " +0x5109: "Jian " +0x510a: "Chu " +0x510b: "Dan " +0x510c: "Jiao " +0x510d: "Sha " +0x510e: "Zai " +0x510f: "[?] " +0x5110: "Bin " +0x5111: "An " +0x5112: "Ru " +0x5113: "Tai " +0x5114: "Chou " +0x5115: "Chai " +0x5116: "Lan " +0x5117: "Ni " +0x5118: "Jin " +0x5119: "Qian " +0x511a: "Meng " +0x511b: "Wu " +0x511c: "Ning " +0x511d: "Qiong " +0x511e: "Ni " +0x511f: "Chang " +0x5120: "Lie " +0x5121: "Lei " +0x5122: "Lu " +0x5123: "Kuang " +0x5124: "Bao " +0x5125: "Du " +0x5126: "Biao " +0x5127: "Zan " +0x5128: "Zhi " +0x5129: "Si " +0x512a: "You " +0x512b: "Hao " +0x512c: "Chen " +0x512d: "Chen " +0x512e: "Li " +0x512f: "Teng " +0x5130: "Wei " +0x5131: "Long " +0x5132: "Chu " +0x5133: "Chan " +0x5134: "Rang " +0x5135: "Shu " +0x5136: "Hui " +0x5137: "Li " +0x5138: "Luo " +0x5139: "Zan " +0x513a: "Nuo " +0x513b: "Tang " +0x513c: "Yan " +0x513d: "Lei " +0x513e: "Nang " +0x513f: "Er " +0x5140: "Wu " +0x5141: "Yun " +0x5142: "Zan " +0x5143: "Yuan " +0x5144: "Xiong " +0x5145: "Chong " +0x5146: "Zhao " +0x5147: "Xiong " +0x5148: "Xian " +0x5149: "Guang " +0x514a: "Dui " +0x514b: "Ke " +0x514c: "Dui " +0x514d: "Mian " +0x514e: "Tu " +0x514f: "Chang " +0x5150: "Er " +0x5151: "Dui " +0x5152: "Er " +0x5153: "Xin " +0x5154: "Tu " +0x5155: "Si " +0x5156: "Yan " +0x5157: "Yan " +0x5158: "Shi " +0x5159: "Shi " +0x515a: "Dang " +0x515b: "Qian " +0x515c: "Dou " +0x515d: "Fen " +0x515e: "Mao " +0x515f: "Shen " +0x5160: "Dou " +0x5161: "Bai " +0x5162: "Jing " +0x5163: "Li " +0x5164: "Huang " +0x5165: "Ru " +0x5166: "Wang " +0x5167: "Nei " +0x5168: "Quan " +0x5169: "Liang " +0x516a: "Yu " +0x516b: "Ba " +0x516c: "Gong " +0x516d: "Liu " +0x516e: "Xi " +0x516f: "[?] " +0x5170: "Lan " +0x5171: "Gong " +0x5172: "Tian " +0x5173: "Guan " +0x5174: "Xing " +0x5175: "Bing " +0x5176: "Qi " +0x5177: "Ju " +0x5178: "Dian " +0x5179: "Zi " +0x517a: "Ppwun " +0x517b: "Yang " +0x517c: "Jian " +0x517d: "Shou " +0x517e: "Ji " +0x517f: "Yi " +0x5180: "Ji " +0x5181: "Chan " +0x5182: "Jiong " +0x5183: "Mao " +0x5184: "Ran " +0x5185: "Nei " +0x5186: "Yuan " +0x5187: "Mao " +0x5188: "Gang " +0x5189: "Ran " +0x518a: "Ce " +0x518b: "Jiong " +0x518c: "Ce " +0x518d: "Zai " +0x518e: "Gua " +0x518f: "Jiong " +0x5190: "Mao " +0x5191: "Zhou " +0x5192: "Mou " +0x5193: "Gou " +0x5194: "Xu " +0x5195: "Mian " +0x5196: "Mi " +0x5197: "Rong " +0x5198: "Yin " +0x5199: "Xie " +0x519a: "Kan " +0x519b: "Jun " +0x519c: "Nong " +0x519d: "Yi " +0x519e: "Mi " +0x519f: "Shi " +0x51a0: "Guan " +0x51a1: "Meng " +0x51a2: "Zhong " +0x51a3: "Ju " +0x51a4: "Yuan " +0x51a5: "Ming " +0x51a6: "Kou " +0x51a7: "Lam " +0x51a8: "Fu " +0x51a9: "Xie " +0x51aa: "Mi " +0x51ab: "Bing " +0x51ac: "Dong " +0x51ad: "Tai " +0x51ae: "Gang " +0x51af: "Feng " +0x51b0: "Bing " +0x51b1: "Hu " +0x51b2: "Chong " +0x51b3: "Jue " +0x51b4: "Hu " +0x51b5: "Kuang " +0x51b6: "Ye " +0x51b7: "Leng " +0x51b8: "Pan " +0x51b9: "Fu " +0x51ba: "Min " +0x51bb: "Dong " +0x51bc: "Xian " +0x51bd: "Lie " +0x51be: "Xia " +0x51bf: "Jian " +0x51c0: "Jing " +0x51c1: "Shu " +0x51c2: "Mei " +0x51c3: "Tu " +0x51c4: "Qi " +0x51c5: "Gu " +0x51c6: "Zhun " +0x51c7: "Song " +0x51c8: "Jing " +0x51c9: "Liang " +0x51ca: "Qing " +0x51cb: "Diao " +0x51cc: "Ling " +0x51cd: "Dong " +0x51ce: "Gan " +0x51cf: "Jian " +0x51d0: "Yin " +0x51d1: "Cou " +0x51d2: "Yi " +0x51d3: "Li " +0x51d4: "Cang " +0x51d5: "Ming " +0x51d6: "Zhuen " +0x51d7: "Cui " +0x51d8: "Si " +0x51d9: "Duo " +0x51da: "Jin " +0x51db: "Lin " +0x51dc: "Lin " +0x51dd: "Ning " +0x51de: "Xi " +0x51df: "Du " +0x51e0: "Ji " +0x51e1: "Fan " +0x51e2: "Fan " +0x51e3: "Fan " +0x51e4: "Feng " +0x51e5: "Ju " +0x51e6: "Chu " +0x51e7: "Tako " +0x51e8: "Feng " +0x51e9: "Mok " +0x51ea: "Ci " +0x51eb: "Fu " +0x51ec: "Feng " +0x51ed: "Ping " +0x51ee: "Feng " +0x51ef: "Kai " +0x51f0: "Huang " +0x51f1: "Kai " +0x51f2: "Gan " +0x51f3: "Deng " +0x51f4: "Ping " +0x51f5: "Qu " +0x51f6: "Xiong " +0x51f7: "Kuai " +0x51f8: "Tu " +0x51f9: "Ao " +0x51fa: "Chu " +0x51fb: "Ji " +0x51fc: "Dang " +0x51fd: "Han " +0x51fe: "Han " +0x51ff: "Zao " +/* x052 */ +0x5200: "Dao " +0x5201: "Diao " +0x5202: "Dao " +0x5203: "Ren " +0x5204: "Ren " +0x5205: "Chuang " +0x5206: "Fen " +0x5207: "Qie " +0x5208: "Yi " +0x5209: "Ji " +0x520a: "Kan " +0x520b: "Qian " +0x520c: "Cun " +0x520d: "Chu " +0x520e: "Wen " +0x520f: "Ji " +0x5210: "Dan " +0x5211: "Xing " +0x5212: "Hua " +0x5213: "Wan " +0x5214: "Jue " +0x5215: "Li " +0x5216: "Yue " +0x5217: "Lie " +0x5218: "Liu " +0x5219: "Ze " +0x521a: "Gang " +0x521b: "Chuang " +0x521c: "Fu " +0x521d: "Chu " +0x521e: "Qu " +0x521f: "Ju " +0x5220: "Shan " +0x5221: "Min " +0x5222: "Ling " +0x5223: "Zhong " +0x5224: "Pan " +0x5225: "Bie " +0x5226: "Jie " +0x5227: "Jie " +0x5228: "Bao " +0x5229: "Li " +0x522a: "Shan " +0x522b: "Bie " +0x522c: "Chan " +0x522d: "Jing " +0x522e: "Gua " +0x522f: "Gen " +0x5230: "Dao " +0x5231: "Chuang " +0x5232: "Kui " +0x5233: "Ku " +0x5234: "Duo " +0x5235: "Er " +0x5236: "Zhi " +0x5237: "Shua " +0x5238: "Quan " +0x5239: "Cha " +0x523a: "Ci " +0x523b: "Ke " +0x523c: "Jie " +0x523d: "Gui " +0x523e: "Ci " +0x523f: "Gui " +0x5240: "Kai " +0x5241: "Duo " +0x5242: "Ji " +0x5243: "Ti " +0x5244: "Jing " +0x5245: "Lou " +0x5246: "Gen " +0x5247: "Ze " +0x5248: "Yuan " +0x5249: "Cuo " +0x524a: "Xue " +0x524b: "Ke " +0x524c: "La " +0x524d: "Qian " +0x524e: "Cha " +0x524f: "Chuang " +0x5250: "Gua " +0x5251: "Jian " +0x5252: "Cuo " +0x5253: "Li " +0x5254: "Ti " +0x5255: "Fei " +0x5256: "Pou " +0x5257: "Chan " +0x5258: "Qi " +0x5259: "Chuang " +0x525a: "Zi " +0x525b: "Gang " +0x525c: "Wan " +0x525d: "Bo " +0x525e: "Ji " +0x525f: "Duo " +0x5260: "Qing " +0x5261: "Yan " +0x5262: "Zhuo " +0x5263: "Jian " +0x5264: "Ji " +0x5265: "Bo " +0x5266: "Yan " +0x5267: "Ju " +0x5268: "Huo " +0x5269: "Sheng " +0x526a: "Jian " +0x526b: "Duo " +0x526c: "Duan " +0x526d: "Wu " +0x526e: "Gua " +0x526f: "Fu " +0x5270: "Sheng " +0x5271: "Jian " +0x5272: "Ge " +0x5273: "Zha " +0x5274: "Kai " +0x5275: "Chuang " +0x5276: "Juan " +0x5277: "Chan " +0x5278: "Tuan " +0x5279: "Lu " +0x527a: "Li " +0x527b: "Fou " +0x527c: "Shan " +0x527d: "Piao " +0x527e: "Kou " +0x527f: "Jiao " +0x5280: "Gua " +0x5281: "Qiao " +0x5282: "Jue " +0x5283: "Hua " +0x5284: "Zha " +0x5285: "Zhuo " +0x5286: "Lian " +0x5287: "Ju " +0x5288: "Pi " +0x5289: "Liu " +0x528a: "Gui " +0x528b: "Jiao " +0x528c: "Gui " +0x528d: "Jian " +0x528e: "Jian " +0x528f: "Tang " +0x5290: "Huo " +0x5291: "Ji " +0x5292: "Jian " +0x5293: "Yi " +0x5294: "Jian " +0x5295: "Zhi " +0x5296: "Chan " +0x5297: "Cuan " +0x5298: "Mo " +0x5299: "Li " +0x529a: "Zhu " +0x529b: "Li " +0x529c: "Ya " +0x529d: "Quan " +0x529e: "Ban " +0x529f: "Gong " +0x52a0: "Jia " +0x52a1: "Wu " +0x52a2: "Mai " +0x52a3: "Lie " +0x52a4: "Jin " +0x52a5: "Keng " +0x52a6: "Xie " +0x52a7: "Zhi " +0x52a8: "Dong " +0x52a9: "Zhu " +0x52aa: "Nu " +0x52ab: "Jie " +0x52ac: "Qu " +0x52ad: "Shao " +0x52ae: "Yi " +0x52af: "Zhu " +0x52b0: "Miao " +0x52b1: "Li " +0x52b2: "Jing " +0x52b3: "Lao " +0x52b4: "Lao " +0x52b5: "Juan " +0x52b6: "Kou " +0x52b7: "Yang " +0x52b8: "Wa " +0x52b9: "Xiao " +0x52ba: "Mou " +0x52bb: "Kuang " +0x52bc: "Jie " +0x52bd: "Lie " +0x52be: "He " +0x52bf: "Shi " +0x52c0: "Ke " +0x52c1: "Jing " +0x52c2: "Hao " +0x52c3: "Bo " +0x52c4: "Min " +0x52c5: "Chi " +0x52c6: "Lang " +0x52c7: "Yong " +0x52c8: "Yong " +0x52c9: "Mian " +0x52ca: "Ke " +0x52cb: "Xun " +0x52cc: "Juan " +0x52cd: "Qing " +0x52ce: "Lu " +0x52cf: "Pou " +0x52d0: "Meng " +0x52d1: "Lai " +0x52d2: "Le " +0x52d3: "Kai " +0x52d4: "Mian " +0x52d5: "Dong " +0x52d6: "Xu " +0x52d7: "Xu " +0x52d8: "Kan " +0x52d9: "Wu " +0x52da: "Yi " +0x52db: "Xun " +0x52dc: "Weng " +0x52dd: "Sheng " +0x52de: "Lao " +0x52df: "Mu " +0x52e0: "Lu " +0x52e1: "Piao " +0x52e2: "Shi " +0x52e3: "Ji " +0x52e4: "Qin " +0x52e5: "Qiang " +0x52e6: "Jiao " +0x52e7: "Quan " +0x52e8: "Yang " +0x52e9: "Yi " +0x52ea: "Jue " +0x52eb: "Fan " +0x52ec: "Juan " +0x52ed: "Tong " +0x52ee: "Ju " +0x52ef: "Dan " +0x52f0: "Xie " +0x52f1: "Mai " +0x52f2: "Xun " +0x52f3: "Xun " +0x52f4: "Lu " +0x52f5: "Li " +0x52f6: "Che " +0x52f7: "Rang " +0x52f8: "Quan " +0x52f9: "Bao " +0x52fa: "Shao " +0x52fb: "Yun " +0x52fc: "Jiu " +0x52fd: "Bao " +0x52fe: "Gou " +0x52ff: "Wu " +/* x053 */ +0x5300: "Yun " +0x5301: "Mwun " +0x5302: "Nay " +0x5303: "Gai " +0x5304: "Gai " +0x5305: "Bao " +0x5306: "Cong " +0x5307: "[?] " +0x5308: "Xiong " +0x5309: "Peng " +0x530a: "Ju " +0x530b: "Tao " +0x530c: "Ge " +0x530d: "Pu " +0x530e: "An " +0x530f: "Pao " +0x5310: "Fu " +0x5311: "Gong " +0x5312: "Da " +0x5313: "Jiu " +0x5314: "Qiong " +0x5315: "Bi " +0x5316: "Hua " +0x5317: "Bei " +0x5318: "Nao " +0x5319: "Chi " +0x531a: "Fang " +0x531b: "Jiu " +0x531c: "Yi " +0x531d: "Za " +0x531e: "Jiang " +0x531f: "Kang " +0x5320: "Jiang " +0x5321: "Kuang " +0x5322: "Hu " +0x5323: "Xia " +0x5324: "Qu " +0x5325: "Bian " +0x5326: "Gui " +0x5327: "Qie " +0x5328: "Zang " +0x5329: "Kuang " +0x532a: "Fei " +0x532b: "Hu " +0x532c: "Tou " +0x532d: "Gui " +0x532e: "Gui " +0x532f: "Hui " +0x5330: "Dan " +0x5331: "Gui " +0x5332: "Lian " +0x5333: "Lian " +0x5334: "Suan " +0x5335: "Du " +0x5336: "Jiu " +0x5337: "Qu " +0x5338: "Xi " +0x5339: "Pi " +0x533a: "Qu " +0x533b: "Yi " +0x533c: "Qia " +0x533d: "Yan " +0x533e: "Bian " +0x533f: "Ni " +0x5340: "Qu " +0x5341: "Shi " +0x5342: "Xin " +0x5343: "Qian " +0x5344: "Nian " +0x5345: "Sa " +0x5346: "Zu " +0x5347: "Sheng " +0x5348: "Wu " +0x5349: "Hui " +0x534a: "Ban " +0x534b: "Shi " +0x534c: "Xi " +0x534d: "Wan " +0x534e: "Hua " +0x534f: "Xie " +0x5350: "Wan " +0x5351: "Bei " +0x5352: "Zu " +0x5353: "Zhuo " +0x5354: "Xie " +0x5355: "Dan " +0x5356: "Mai " +0x5357: "Nan " +0x5358: "Dan " +0x5359: "Ji " +0x535a: "Bo " +0x535b: "Shuai " +0x535c: "Bu " +0x535d: "Kuang " +0x535e: "Bian " +0x535f: "Bu " +0x5360: "Zhan " +0x5361: "Qia " +0x5362: "Lu " +0x5363: "You " +0x5364: "Lu " +0x5365: "Xi " +0x5366: "Gua " +0x5367: "Wo " +0x5368: "Xie " +0x5369: "Jie " +0x536a: "Jie " +0x536b: "Wei " +0x536c: "Ang " +0x536d: "Qiong " +0x536e: "Zhi " +0x536f: "Mao " +0x5370: "Yin " +0x5371: "Wei " +0x5372: "Shao " +0x5373: "Ji " +0x5374: "Que " +0x5375: "Luan " +0x5376: "Shi " +0x5377: "Juan " +0x5378: "Xie " +0x5379: "Xu " +0x537a: "Jin " +0x537b: "Que " +0x537c: "Wu " +0x537d: "Ji " +0x537e: "E " +0x537f: "Qing " +0x5380: "Xi " +0x5381: "[?] " +0x5382: "Han " +0x5383: "Zhan " +0x5384: "E " +0x5385: "Ting " +0x5386: "Li " +0x5387: "Zhe " +0x5388: "Han " +0x5389: "Li " +0x538a: "Ya " +0x538b: "Ya " +0x538c: "Yan " +0x538d: "She " +0x538e: "Zhi " +0x538f: "Zha " +0x5390: "Pang " +0x5391: "[?] " +0x5392: "He " +0x5393: "Ya " +0x5394: "Zhi " +0x5395: "Ce " +0x5396: "Pang " +0x5397: "Ti " +0x5398: "Li " +0x5399: "She " +0x539a: "Hou " +0x539b: "Ting " +0x539c: "Zui " +0x539d: "Cuo " +0x539e: "Fei " +0x539f: "Yuan " +0x53a0: "Ce " +0x53a1: "Yuan " +0x53a2: "Xiang " +0x53a3: "Yan " +0x53a4: "Li " +0x53a5: "Jue " +0x53a6: "Sha " +0x53a7: "Dian " +0x53a8: "Chu " +0x53a9: "Jiu " +0x53aa: "Qin " +0x53ab: "Ao " +0x53ac: "Gui " +0x53ad: "Yan " +0x53ae: "Si " +0x53af: "Li " +0x53b0: "Chang " +0x53b1: "Lan " +0x53b2: "Li " +0x53b3: "Yan " +0x53b4: "Yan " +0x53b5: "Yuan " +0x53b6: "Si " +0x53b7: "Gong " +0x53b8: "Lin " +0x53b9: "Qiu " +0x53ba: "Qu " +0x53bb: "Qu " +0x53bc: "Uk " +0x53bd: "Lei " +0x53be: "Du " +0x53bf: "Xian " +0x53c0: "Zhuan " +0x53c1: "San " +0x53c2: "Can " +0x53c3: "Can " +0x53c4: "Can " +0x53c5: "Can " +0x53c6: "Ai " +0x53c7: "Dai " +0x53c8: "You " +0x53c9: "Cha " +0x53ca: "Ji " +0x53cb: "You " +0x53cc: "Shuang " +0x53cd: "Fan " +0x53ce: "Shou " +0x53cf: "Guai " +0x53d0: "Ba " +0x53d1: "Fa " +0x53d2: "Ruo " +0x53d3: "Shi " +0x53d4: "Shu " +0x53d5: "Zhuo " +0x53d6: "Qu " +0x53d7: "Shou " +0x53d8: "Bian " +0x53d9: "Xu " +0x53da: "Jia " +0x53db: "Pan " +0x53dc: "Sou " +0x53dd: "Gao " +0x53de: "Wei " +0x53df: "Sou " +0x53e0: "Die " +0x53e1: "Rui " +0x53e2: "Cong " +0x53e3: "Kou " +0x53e4: "Gu " +0x53e5: "Ju " +0x53e6: "Ling " +0x53e7: "Gua " +0x53e8: "Tao " +0x53e9: "Kou " +0x53ea: "Zhi " +0x53eb: "Jiao " +0x53ec: "Zhao " +0x53ed: "Ba " +0x53ee: "Ding " +0x53ef: "Ke " +0x53f0: "Tai " +0x53f1: "Chi " +0x53f2: "Shi " +0x53f3: "You " +0x53f4: "Qiu " +0x53f5: "Po " +0x53f6: "Xie " +0x53f7: "Hao " +0x53f8: "Si " +0x53f9: "Tan " +0x53fa: "Chi " +0x53fb: "Le " +0x53fc: "Diao " +0x53fd: "Ji " +0x53fe: "[?] " +0x53ff: "Hong " +/* x054 */ +0x5400: "Mie " +0x5401: "Xu " +0x5402: "Mang " +0x5403: "Chi " +0x5404: "Ge " +0x5405: "Xuan " +0x5406: "Yao " +0x5407: "Zi " +0x5408: "He " +0x5409: "Ji " +0x540a: "Diao " +0x540b: "Cun " +0x540c: "Tong " +0x540d: "Ming " +0x540e: "Hou " +0x540f: "Li " +0x5410: "Tu " +0x5411: "Xiang " +0x5412: "Zha " +0x5413: "Xia " +0x5414: "Ye " +0x5415: "Lu " +0x5416: "A " +0x5417: "Ma " +0x5418: "Ou " +0x5419: "Xue " +0x541a: "Yi " +0x541b: "Jun " +0x541c: "Chou " +0x541d: "Lin " +0x541e: "Tun " +0x541f: "Yin " +0x5420: "Fei " +0x5421: "Bi " +0x5422: "Qin " +0x5423: "Qin " +0x5424: "Jie " +0x5425: "Bu " +0x5426: "Fou " +0x5427: "Ba " +0x5428: "Dun " +0x5429: "Fen " +0x542a: "E " +0x542b: "Han " +0x542c: "Ting " +0x542d: "Hang " +0x542e: "Shun " +0x542f: "Qi " +0x5430: "Hong " +0x5431: "Zhi " +0x5432: "Shen " +0x5433: "Wu " +0x5434: "Wu " +0x5435: "Chao " +0x5436: "Ne " +0x5437: "Xue " +0x5438: "Xi " +0x5439: "Chui " +0x543a: "Dou " +0x543b: "Wen " +0x543c: "Hou " +0x543d: "Ou " +0x543e: "Wu " +0x543f: "Gao " +0x5440: "Ya " +0x5441: "Jun " +0x5442: "Lu " +0x5443: "E " +0x5444: "Ge " +0x5445: "Mei " +0x5446: "Ai " +0x5447: "Qi " +0x5448: "Cheng " +0x5449: "Wu " +0x544a: "Gao " +0x544b: "Fu " +0x544c: "Jiao " +0x544d: "Hong " +0x544e: "Chi " +0x544f: "Sheng " +0x5450: "Ne " +0x5451: "Tun " +0x5452: "Fu " +0x5453: "Yi " +0x5454: "Dai " +0x5455: "Ou " +0x5456: "Li " +0x5457: "Bai " +0x5458: "Yuan " +0x5459: "Kuai " +0x545a: "[?] " +0x545b: "Qiang " +0x545c: "Wu " +0x545d: "E " +0x545e: "Shi " +0x545f: "Quan " +0x5460: "Pen " +0x5461: "Wen " +0x5462: "Ni " +0x5463: "M " +0x5464: "Ling " +0x5465: "Ran " +0x5466: "You " +0x5467: "Di " +0x5468: "Zhou " +0x5469: "Shi " +0x546a: "Zhou " +0x546b: "Tie " +0x546c: "Xi " +0x546d: "Yi " +0x546e: "Qi " +0x546f: "Ping " +0x5470: "Zi " +0x5471: "Gu " +0x5472: "Zi " +0x5473: "Wei " +0x5474: "Xu " +0x5475: "He " +0x5476: "Nao " +0x5477: "Xia " +0x5478: "Pei " +0x5479: "Yi " +0x547a: "Xiao " +0x547b: "Shen " +0x547c: "Hu " +0x547d: "Ming " +0x547e: "Da " +0x547f: "Qu " +0x5480: "Ju " +0x5481: "Gem " +0x5482: "Za " +0x5483: "Tuo " +0x5484: "Duo " +0x5485: "Pou " +0x5486: "Pao " +0x5487: "Bi " +0x5488: "Fu " +0x5489: "Yang " +0x548a: "He " +0x548b: "Zha " +0x548c: "He " +0x548d: "Hai " +0x548e: "Jiu " +0x548f: "Yong " +0x5490: "Fu " +0x5491: "Que " +0x5492: "Zhou " +0x5493: "Wa " +0x5494: "Ka " +0x5495: "Gu " +0x5496: "Ka " +0x5497: "Zuo " +0x5498: "Bu " +0x5499: "Long " +0x549a: "Dong " +0x549b: "Ning " +0x549c: "Tha " +0x549d: "Si " +0x549e: "Xian " +0x549f: "Huo " +0x54a0: "Qi " +0x54a1: "Er " +0x54a2: "E " +0x54a3: "Guang " +0x54a4: "Zha " +0x54a5: "Xi " +0x54a6: "Yi " +0x54a7: "Lie " +0x54a8: "Zi " +0x54a9: "Mie " +0x54aa: "Mi " +0x54ab: "Zhi " +0x54ac: "Yao " +0x54ad: "Ji " +0x54ae: "Zhou " +0x54af: "Ge " +0x54b0: "Shuai " +0x54b1: "Zan " +0x54b2: "Xiao " +0x54b3: "Ke " +0x54b4: "Hui " +0x54b5: "Kua " +0x54b6: "Huai " +0x54b7: "Tao " +0x54b8: "Xian " +0x54b9: "E " +0x54ba: "Xuan " +0x54bb: "Xiu " +0x54bc: "Wai " +0x54bd: "Yan " +0x54be: "Lao " +0x54bf: "Yi " +0x54c0: "Ai " +0x54c1: "Pin " +0x54c2: "Shen " +0x54c3: "Tong " +0x54c4: "Hong " +0x54c5: "Xiong " +0x54c6: "Chi " +0x54c7: "Wa " +0x54c8: "Ha " +0x54c9: "Zai " +0x54ca: "Yu " +0x54cb: "Di " +0x54cc: "Pai " +0x54cd: "Xiang " +0x54ce: "Ai " +0x54cf: "Hen " +0x54d0: "Kuang " +0x54d1: "Ya " +0x54d2: "Da " +0x54d3: "Xiao " +0x54d4: "Bi " +0x54d5: "Yue " +0x54d6: "[?] " +0x54d7: "Hua " +0x54d8: "Sasou " +0x54d9: "Kuai " +0x54da: "Duo " +0x54db: "[?] " +0x54dc: "Ji " +0x54dd: "Nong " +0x54de: "Mou " +0x54df: "Yo " +0x54e0: "Hao " +0x54e1: "Yuan " +0x54e2: "Long " +0x54e3: "Pou " +0x54e4: "Mang " +0x54e5: "Ge " +0x54e6: "E " +0x54e7: "Chi " +0x54e8: "Shao " +0x54e9: "Li " +0x54ea: "Na " +0x54eb: "Zu " +0x54ec: "He " +0x54ed: "Ku " +0x54ee: "Xiao " +0x54ef: "Xian " +0x54f0: "Lao " +0x54f1: "Bo " +0x54f2: "Zhe " +0x54f3: "Zha " +0x54f4: "Liang " +0x54f5: "Ba " +0x54f6: "Mie " +0x54f7: "Le " +0x54f8: "Sui " +0x54f9: "Fou " +0x54fa: "Bu " +0x54fb: "Han " +0x54fc: "Heng " +0x54fd: "Geng " +0x54fe: "Shuo " +0x54ff: "Ge " +/* x055 */ +0x5500: "You " +0x5501: "Yan " +0x5502: "Gu " +0x5503: "Gu " +0x5504: "Bai " +0x5505: "Han " +0x5506: "Suo " +0x5507: "Chun " +0x5508: "Yi " +0x5509: "Ai " +0x550a: "Jia " +0x550b: "Tu " +0x550c: "Xian " +0x550d: "Huan " +0x550e: "Li " +0x550f: "Xi " +0x5510: "Tang " +0x5511: "Zuo " +0x5512: "Qiu " +0x5513: "Che " +0x5514: "Wu " +0x5515: "Zao " +0x5516: "Ya " +0x5517: "Dou " +0x5518: "Qi " +0x5519: "Di " +0x551a: "Qin " +0x551b: "Ma " +0x551c: "Mal " +0x551d: "Hong " +0x551e: "Dou " +0x551f: "Kes " +0x5520: "Lao " +0x5521: "Liang " +0x5522: "Suo " +0x5523: "Zao " +0x5524: "Huan " +0x5525: "Lang " +0x5526: "Sha " +0x5527: "Ji " +0x5528: "Zuo " +0x5529: "Wo " +0x552a: "Feng " +0x552b: "Yin " +0x552c: "Hu " +0x552d: "Qi " +0x552e: "Shou " +0x552f: "Wei " +0x5530: "Shua " +0x5531: "Chang " +0x5532: "Er " +0x5533: "Li " +0x5534: "Qiang " +0x5535: "An " +0x5536: "Jie " +0x5537: "Yo " +0x5538: "Nian " +0x5539: "Yu " +0x553a: "Tian " +0x553b: "Lai " +0x553c: "Sha " +0x553d: "Xi " +0x553e: "Tuo " +0x553f: "Hu " +0x5540: "Ai " +0x5541: "Zhou " +0x5542: "Nou " +0x5543: "Ken " +0x5544: "Zhuo " +0x5545: "Zhuo " +0x5546: "Shang " +0x5547: "Di " +0x5548: "Heng " +0x5549: "Lan " +0x554a: "A " +0x554b: "Xiao " +0x554c: "Xiang " +0x554d: "Tun " +0x554e: "Wu " +0x554f: "Wen " +0x5550: "Cui " +0x5551: "Sha " +0x5552: "Hu " +0x5553: "Qi " +0x5554: "Qi " +0x5555: "Tao " +0x5556: "Dan " +0x5557: "Dan " +0x5558: "Ye " +0x5559: "Zi " +0x555a: "Bi " +0x555b: "Cui " +0x555c: "Chuo " +0x555d: "He " +0x555e: "Ya " +0x555f: "Qi " +0x5560: "Zhe " +0x5561: "Pei " +0x5562: "Liang " +0x5563: "Xian " +0x5564: "Pi " +0x5565: "Sha " +0x5566: "La " +0x5567: "Ze " +0x5568: "Qing " +0x5569: "Gua " +0x556a: "Pa " +0x556b: "Zhe " +0x556c: "Se " +0x556d: "Zhuan " +0x556e: "Nie " +0x556f: "Guo " +0x5570: "Luo " +0x5571: "Yan " +0x5572: "Di " +0x5573: "Quan " +0x5574: "Tan " +0x5575: "Bo " +0x5576: "Ding " +0x5577: "Lang " +0x5578: "Xiao " +0x5579: "[?] " +0x557a: "Tang " +0x557b: "Chi " +0x557c: "Ti " +0x557d: "An " +0x557e: "Jiu " +0x557f: "Dan " +0x5580: "Ke " +0x5581: "Yong " +0x5582: "Wei " +0x5583: "Nan " +0x5584: "Shan " +0x5585: "Yu " +0x5586: "Zhe " +0x5587: "La " +0x5588: "Jie " +0x5589: "Hou " +0x558a: "Han " +0x558b: "Die " +0x558c: "Zhou " +0x558d: "Chai " +0x558e: "Wai " +0x558f: "Re " +0x5590: "Yu " +0x5591: "Yin " +0x5592: "Zan " +0x5593: "Yao " +0x5594: "Wo " +0x5595: "Mian " +0x5596: "Hu " +0x5597: "Yun " +0x5598: "Chuan " +0x5599: "Hui " +0x559a: "Huan " +0x559b: "Huan " +0x559c: "Xi " +0x559d: "He " +0x559e: "Ji " +0x559f: "Kui " +0x55a0: "Zhong " +0x55a1: "Wei " +0x55a2: "Sha " +0x55a3: "Xu " +0x55a4: "Huang " +0x55a5: "Du " +0x55a6: "Nie " +0x55a7: "Xuan " +0x55a8: "Liang " +0x55a9: "Yu " +0x55aa: "Sang " +0x55ab: "Chi " +0x55ac: "Qiao " +0x55ad: "Yan " +0x55ae: "Dan " +0x55af: "Pen " +0x55b0: "Can " +0x55b1: "Li " +0x55b2: "Yo " +0x55b3: "Zha " +0x55b4: "Wei " +0x55b5: "Miao " +0x55b6: "Ying " +0x55b7: "Pen " +0x55b8: "Phos " +0x55b9: "Kui " +0x55ba: "Xi " +0x55bb: "Yu " +0x55bc: "Jie " +0x55bd: "Lou " +0x55be: "Ku " +0x55bf: "Sao " +0x55c0: "Huo " +0x55c1: "Ti " +0x55c2: "Yao " +0x55c3: "He " +0x55c4: "A " +0x55c5: "Xiu " +0x55c6: "Qiang " +0x55c7: "Se " +0x55c8: "Yong " +0x55c9: "Su " +0x55ca: "Hong " +0x55cb: "Xie " +0x55cc: "Yi " +0x55cd: "Suo " +0x55ce: "Ma " +0x55cf: "Cha " +0x55d0: "Hai " +0x55d1: "Ke " +0x55d2: "Ta " +0x55d3: "Sang " +0x55d4: "Tian " +0x55d5: "Ru " +0x55d6: "Sou " +0x55d7: "Wa " +0x55d8: "Ji " +0x55d9: "Pang " +0x55da: "Wu " +0x55db: "Xian " +0x55dc: "Shi " +0x55dd: "Ge " +0x55de: "Zi " +0x55df: "Jie " +0x55e0: "Luo " +0x55e1: "Weng " +0x55e2: "Wa " +0x55e3: "Si " +0x55e4: "Chi " +0x55e5: "Hao " +0x55e6: "Suo " +0x55e7: "Jia " +0x55e8: "Hai " +0x55e9: "Suo " +0x55ea: "Qin " +0x55eb: "Nie " +0x55ec: "He " +0x55ed: "Cis " +0x55ee: "Sai " +0x55ef: "Ng " +0x55f0: "Ge " +0x55f1: "Na " +0x55f2: "Dia " +0x55f3: "Ai " +0x55f4: "[?] " +0x55f5: "Tong " +0x55f6: "Bi " +0x55f7: "Ao " +0x55f8: "Ao " +0x55f9: "Lian " +0x55fa: "Cui " +0x55fb: "Zhe " +0x55fc: "Mo " +0x55fd: "Sou " +0x55fe: "Sou " +0x55ff: "Tan " +/* x056 */ +0x5600: "Di " +0x5601: "Qi " +0x5602: "Jiao " +0x5603: "Chong " +0x5604: "Jiao " +0x5605: "Kai " +0x5606: "Tan " +0x5607: "San " +0x5608: "Cao " +0x5609: "Jia " +0x560a: "Ai " +0x560b: "Xiao " +0x560c: "Piao " +0x560d: "Lou " +0x560e: "Ga " +0x560f: "Gu " +0x5610: "Xiao " +0x5611: "Hu " +0x5612: "Hui " +0x5613: "Guo " +0x5614: "Ou " +0x5615: "Xian " +0x5616: "Ze " +0x5617: "Chang " +0x5618: "Xu " +0x5619: "Po " +0x561a: "De " +0x561b: "Ma " +0x561c: "Ma " +0x561d: "Hu " +0x561e: "Lei " +0x561f: "Du " +0x5620: "Ga " +0x5621: "Tang " +0x5622: "Ye " +0x5623: "Beng " +0x5624: "Ying " +0x5625: "Saai " +0x5626: "Jiao " +0x5627: "Mi " +0x5628: "Xiao " +0x5629: "Hua " +0x562a: "Mai " +0x562b: "Ran " +0x562c: "Zuo " +0x562d: "Peng " +0x562e: "Lao " +0x562f: "Xiao " +0x5630: "Ji " +0x5631: "Zhu " +0x5632: "Chao " +0x5633: "Kui " +0x5634: "Zui " +0x5635: "Xiao " +0x5636: "Si " +0x5637: "Hao " +0x5638: "Fu " +0x5639: "Liao " +0x563a: "Qiao " +0x563b: "Xi " +0x563c: "Xiu " +0x563d: "Tan " +0x563e: "Tan " +0x563f: "Mo " +0x5640: "Xun " +0x5641: "E " +0x5642: "Zun " +0x5643: "Fan " +0x5644: "Chi " +0x5645: "Hui " +0x5646: "Zan " +0x5647: "Chuang " +0x5648: "Cu " +0x5649: "Dan " +0x564a: "Yu " +0x564b: "Tun " +0x564c: "Cheng " +0x564d: "Jiao " +0x564e: "Ye " +0x564f: "Xi " +0x5650: "Qi " +0x5651: "Hao " +0x5652: "Lian " +0x5653: "Xu " +0x5654: "Deng " +0x5655: "Hui " +0x5656: "Yin " +0x5657: "Pu " +0x5658: "Jue " +0x5659: "Qin " +0x565a: "Xun " +0x565b: "Nie " +0x565c: "Lu " +0x565d: "Si " +0x565e: "Yan " +0x565f: "Ying " +0x5660: "Da " +0x5661: "Dan " +0x5662: "Yu " +0x5663: "Zhou " +0x5664: "Jin " +0x5665: "Nong " +0x5666: "Yue " +0x5667: "Hui " +0x5668: "Qi " +0x5669: "E " +0x566a: "Zao " +0x566b: "Yi " +0x566c: "Shi " +0x566d: "Jiao " +0x566e: "Yuan " +0x566f: "Ai " +0x5670: "Yong " +0x5671: "Jue " +0x5672: "Kuai " +0x5673: "Yu " +0x5674: "Pen " +0x5675: "Dao " +0x5676: "Ge " +0x5677: "Xin " +0x5678: "Dun " +0x5679: "Dang " +0x567a: "Sin " +0x567b: "Sai " +0x567c: "Pi " +0x567d: "Pi " +0x567e: "Yin " +0x567f: "Zui " +0x5680: "Ning " +0x5681: "Di " +0x5682: "Lan " +0x5683: "Ta " +0x5684: "Huo " +0x5685: "Ru " +0x5686: "Hao " +0x5687: "Xia " +0x5688: "Ya " +0x5689: "Duo " +0x568a: "Xi " +0x568b: "Chou " +0x568c: "Ji " +0x568d: "Jin " +0x568e: "Hao " +0x568f: "Ti " +0x5690: "Chang " +0x5691: "[?] " +0x5692: "[?] " +0x5693: "Ca " +0x5694: "Ti " +0x5695: "Lu " +0x5696: "Hui " +0x5697: "Bo " +0x5698: "You " +0x5699: "Nie " +0x569a: "Yin " +0x569b: "Hu " +0x569c: "Mo " +0x569d: "Huang " +0x569e: "Zhe " +0x569f: "Li " +0x56a0: "Liu " +0x56a1: "Haai " +0x56a2: "Nang " +0x56a3: "Xiao " +0x56a4: "Mo " +0x56a5: "Yan " +0x56a6: "Li " +0x56a7: "Lu " +0x56a8: "Long " +0x56a9: "Fu " +0x56aa: "Dan " +0x56ab: "Chen " +0x56ac: "Pin " +0x56ad: "Pi " +0x56ae: "Xiang " +0x56af: "Huo " +0x56b0: "Mo " +0x56b1: "Xi " +0x56b2: "Duo " +0x56b3: "Ku " +0x56b4: "Yan " +0x56b5: "Chan " +0x56b6: "Ying " +0x56b7: "Rang " +0x56b8: "Dian " +0x56b9: "La " +0x56ba: "Ta " +0x56bb: "Xiao " +0x56bc: "Jiao " +0x56bd: "Chuo " +0x56be: "Huan " +0x56bf: "Huo " +0x56c0: "Zhuan " +0x56c1: "Nie " +0x56c2: "Xiao " +0x56c3: "Ca " +0x56c4: "Li " +0x56c5: "Chan " +0x56c6: "Chai " +0x56c7: "Li " +0x56c8: "Yi " +0x56c9: "Luo " +0x56ca: "Nang " +0x56cb: "Zan " +0x56cc: "Su " +0x56cd: "Xi " +0x56ce: "So " +0x56cf: "Jian " +0x56d0: "Za " +0x56d1: "Zhu " +0x56d2: "Lan " +0x56d3: "Nie " +0x56d4: "Nang " +0x56d5: "[?] " +0x56d6: "[?] " +0x56d7: "Wei " +0x56d8: "Hui " +0x56d9: "Yin " +0x56da: "Qiu " +0x56db: "Si " +0x56dc: "Nin " +0x56dd: "Jian " +0x56de: "Hui " +0x56df: "Xin " +0x56e0: "Yin " +0x56e1: "Nan " +0x56e2: "Tuan " +0x56e3: "Tuan " +0x56e4: "Dun " +0x56e5: "Kang " +0x56e6: "Yuan " +0x56e7: "Jiong " +0x56e8: "Pian " +0x56e9: "Yun " +0x56ea: "Cong " +0x56eb: "Hu " +0x56ec: "Hui " +0x56ed: "Yuan " +0x56ee: "You " +0x56ef: "Guo " +0x56f0: "Kun " +0x56f1: "Cong " +0x56f2: "Wei " +0x56f3: "Tu " +0x56f4: "Wei " +0x56f5: "Lun " +0x56f6: "Guo " +0x56f7: "Qun " +0x56f8: "Ri " +0x56f9: "Ling " +0x56fa: "Gu " +0x56fb: "Guo " +0x56fc: "Tai " +0x56fd: "Guo " +0x56fe: "Tu " +0x56ff: "You " +/* x057 */ +0x5700: "Guo " +0x5701: "Yin " +0x5702: "Hun " +0x5703: "Pu " +0x5704: "Yu " +0x5705: "Han " +0x5706: "Yuan " +0x5707: "Lun " +0x5708: "Quan " +0x5709: "Yu " +0x570a: "Qing " +0x570b: "Guo " +0x570c: "Chuan " +0x570d: "Wei " +0x570e: "Yuan " +0x570f: "Quan " +0x5710: "Ku " +0x5711: "Fu " +0x5712: "Yuan " +0x5713: "Yuan " +0x5714: "E " +0x5715: "Tu " +0x5716: "Tu " +0x5717: "Tu " +0x5718: "Tuan " +0x5719: "Lue " +0x571a: "Hui " +0x571b: "Yi " +0x571c: "Yuan " +0x571d: "Luan " +0x571e: "Luan " +0x571f: "Tu " +0x5720: "Ya " +0x5721: "Tu " +0x5722: "Ting " +0x5723: "Sheng " +0x5724: "Pu " +0x5725: "Lu " +0x5726: "Iri " +0x5727: "Ya " +0x5728: "Zai " +0x5729: "Wei " +0x572a: "Ge " +0x572b: "Yu " +0x572c: "Wu " +0x572d: "Gui " +0x572e: "Pi " +0x572f: "Yi " +0x5730: "Di " +0x5731: "Qian " +0x5732: "Qian " +0x5733: "Zhen " +0x5734: "Zhuo " +0x5735: "Dang " +0x5736: "Qia " +0x5737: "Akutsu " +0x5738: "Yama " +0x5739: "Kuang " +0x573a: "Chang " +0x573b: "Qi " +0x573c: "Nie " +0x573d: "Mo " +0x573e: "Ji " +0x573f: "Jia " +0x5740: "Zhi " +0x5741: "Zhi " +0x5742: "Ban " +0x5743: "Xun " +0x5744: "Tou " +0x5745: "Qin " +0x5746: "Fen " +0x5747: "Jun " +0x5748: "Keng " +0x5749: "Tun " +0x574a: "Fang " +0x574b: "Fen " +0x574c: "Ben " +0x574d: "Tan " +0x574e: "Kan " +0x574f: "Pi " +0x5750: "Zuo " +0x5751: "Keng " +0x5752: "Bi " +0x5753: "Xing " +0x5754: "Di " +0x5755: "Jing " +0x5756: "Ji " +0x5757: "Kuai " +0x5758: "Di " +0x5759: "Jing " +0x575a: "Jian " +0x575b: "Tan " +0x575c: "Li " +0x575d: "Ba " +0x575e: "Wu " +0x575f: "Fen " +0x5760: "Zhui " +0x5761: "Po " +0x5762: "Pan " +0x5763: "Tang " +0x5764: "Kun " +0x5765: "Qu " +0x5766: "Tan " +0x5767: "Zhi " +0x5768: "Tuo " +0x5769: "Gan " +0x576a: "Ping " +0x576b: "Dian " +0x576c: "Gua " +0x576d: "Ni " +0x576e: "Tai " +0x576f: "Pi " +0x5770: "Jiong " +0x5771: "Yang " +0x5772: "Fo " +0x5773: "Ao " +0x5774: "Liu " +0x5775: "Qiu " +0x5776: "Mu " +0x5777: "Ke " +0x5778: "Gou " +0x5779: "Xue " +0x577a: "Ba " +0x577b: "Chi " +0x577c: "Che " +0x577d: "Ling " +0x577e: "Zhu " +0x577f: "Fu " +0x5780: "Hu " +0x5781: "Zhi " +0x5782: "Chui " +0x5783: "La " +0x5784: "Long " +0x5785: "Long " +0x5786: "Lu " +0x5787: "Ao " +0x5788: "Tay " +0x5789: "Pao " +0x578a: "[?] " +0x578b: "Xing " +0x578c: "Dong " +0x578d: "Ji " +0x578e: "Ke " +0x578f: "Lu " +0x5790: "Ci " +0x5791: "Chi " +0x5792: "Lei " +0x5793: "Gai " +0x5794: "Yin " +0x5795: "Hou " +0x5796: "Dui " +0x5797: "Zhao " +0x5798: "Fu " +0x5799: "Guang " +0x579a: "Yao " +0x579b: "Duo " +0x579c: "Duo " +0x579d: "Gui " +0x579e: "Cha " +0x579f: "Yang " +0x57a0: "Yin " +0x57a1: "Fa " +0x57a2: "Gou " +0x57a3: "Yuan " +0x57a4: "Die " +0x57a5: "Xie " +0x57a6: "Ken " +0x57a7: "Jiong " +0x57a8: "Shou " +0x57a9: "E " +0x57aa: "Ha " +0x57ab: "Dian " +0x57ac: "Hong " +0x57ad: "Wu " +0x57ae: "Kua " +0x57af: "[?] " +0x57b0: "Tao " +0x57b1: "Dang " +0x57b2: "Kai " +0x57b3: "Gake " +0x57b4: "Nao " +0x57b5: "An " +0x57b6: "Xing " +0x57b7: "Xian " +0x57b8: "Huan " +0x57b9: "Bang " +0x57ba: "Pei " +0x57bb: "Ba " +0x57bc: "Yi " +0x57bd: "Yin " +0x57be: "Han " +0x57bf: "Xu " +0x57c0: "Chui " +0x57c1: "Cen " +0x57c2: "Geng " +0x57c3: "Ai " +0x57c4: "Peng " +0x57c5: "Fang " +0x57c6: "Que " +0x57c7: "Yong " +0x57c8: "Xun " +0x57c9: "Jia " +0x57ca: "Di " +0x57cb: "Mai " +0x57cc: "Lang " +0x57cd: "Xuan " +0x57ce: "Cheng " +0x57cf: "Yan " +0x57d0: "Jin " +0x57d1: "Zhe " +0x57d2: "Lei " +0x57d3: "Lie " +0x57d4: "Bu " +0x57d5: "Cheng " +0x57d6: "Gomi " +0x57d7: "Bu " +0x57d8: "Shi " +0x57d9: "Xun " +0x57da: "Guo " +0x57db: "Jiong " +0x57dc: "Ye " +0x57dd: "Nian " +0x57de: "Di " +0x57df: "Yu " +0x57e0: "Bu " +0x57e1: "Ya " +0x57e2: "Juan " +0x57e3: "Sui " +0x57e4: "Pi " +0x57e5: "Cheng " +0x57e6: "Wan " +0x57e7: "Ju " +0x57e8: "Lun " +0x57e9: "Zheng " +0x57ea: "Kong " +0x57eb: "Chong " +0x57ec: "Dong " +0x57ed: "Dai " +0x57ee: "Tan " +0x57ef: "An " +0x57f0: "Cai " +0x57f1: "Shu " +0x57f2: "Beng " +0x57f3: "Kan " +0x57f4: "Zhi " +0x57f5: "Duo " +0x57f6: "Yi " +0x57f7: "Zhi " +0x57f8: "Yi " +0x57f9: "Pei " +0x57fa: "Ji " +0x57fb: "Zhun " +0x57fc: "Qi " +0x57fd: "Sao " +0x57fe: "Ju " +0x57ff: "Ni " +/* x058 */ +0x5800: "Ku " +0x5801: "Ke " +0x5802: "Tang " +0x5803: "Kun " +0x5804: "Ni " +0x5805: "Jian " +0x5806: "Dui " +0x5807: "Jin " +0x5808: "Gang " +0x5809: "Yu " +0x580a: "E " +0x580b: "Peng " +0x580c: "Gu " +0x580d: "Tu " +0x580e: "Leng " +0x580f: "[?] " +0x5810: "Ya " +0x5811: "Qian " +0x5812: "[?] " +0x5813: "An " +0x5814: "[?] " +0x5815: "Duo " +0x5816: "Nao " +0x5817: "Tu " +0x5818: "Cheng " +0x5819: "Yin " +0x581a: "Hun " +0x581b: "Bi " +0x581c: "Lian " +0x581d: "Guo " +0x581e: "Die " +0x581f: "Zhuan " +0x5820: "Hou " +0x5821: "Bao " +0x5822: "Bao " +0x5823: "Yu " +0x5824: "Di " +0x5825: "Mao " +0x5826: "Jie " +0x5827: "Ruan " +0x5828: "E " +0x5829: "Geng " +0x582a: "Kan " +0x582b: "Zong " +0x582c: "Yu " +0x582d: "Huang " +0x582e: "E " +0x582f: "Yao " +0x5830: "Yan " +0x5831: "Bao " +0x5832: "Ji " +0x5833: "Mei " +0x5834: "Chang " +0x5835: "Du " +0x5836: "Tuo " +0x5837: "Yin " +0x5838: "Feng " +0x5839: "Zhong " +0x583a: "Jie " +0x583b: "Zhen " +0x583c: "Feng " +0x583d: "Gang " +0x583e: "Chuan " +0x583f: "Jian " +0x5840: "Pyeng " +0x5841: "Toride " +0x5842: "Xiang " +0x5843: "Huang " +0x5844: "Leng " +0x5845: "Duan " +0x5846: "[?] " +0x5847: "Xuan " +0x5848: "Ji " +0x5849: "Ji " +0x584a: "Kuai " +0x584b: "Ying " +0x584c: "Ta " +0x584d: "Cheng " +0x584e: "Yong " +0x584f: "Kai " +0x5850: "Su " +0x5851: "Su " +0x5852: "Shi " +0x5853: "Mi " +0x5854: "Ta " +0x5855: "Weng " +0x5856: "Cheng " +0x5857: "Tu " +0x5858: "Tang " +0x5859: "Que " +0x585a: "Zhong " +0x585b: "Li " +0x585c: "Peng " +0x585d: "Bang " +0x585e: "Sai " +0x585f: "Zang " +0x5860: "Dui " +0x5861: "Tian " +0x5862: "Wu " +0x5863: "Cheng " +0x5864: "Xun " +0x5865: "Ge " +0x5866: "Zhen " +0x5867: "Ai " +0x5868: "Gong " +0x5869: "Yan " +0x586a: "Kan " +0x586b: "Tian " +0x586c: "Yuan " +0x586d: "Wen " +0x586e: "Xie " +0x586f: "Liu " +0x5870: "Ama " +0x5871: "Lang " +0x5872: "Chang " +0x5873: "Peng " +0x5874: "Beng " +0x5875: "Chen " +0x5876: "Cu " +0x5877: "Lu " +0x5878: "Ou " +0x5879: "Qian " +0x587a: "Mei " +0x587b: "Mo " +0x587c: "Zhuan " +0x587d: "Shuang " +0x587e: "Shu " +0x587f: "Lou " +0x5880: "Chi " +0x5881: "Man " +0x5882: "Biao " +0x5883: "Jing " +0x5884: "Qi " +0x5885: "Shu " +0x5886: "Di " +0x5887: "Zhang " +0x5888: "Kan " +0x5889: "Yong " +0x588a: "Dian " +0x588b: "Chen " +0x588c: "Zhi " +0x588d: "Xi " +0x588e: "Guo " +0x588f: "Qiang " +0x5890: "Jin " +0x5891: "Di " +0x5892: "Shang " +0x5893: "Mu " +0x5894: "Cui " +0x5895: "Yan " +0x5896: "Ta " +0x5897: "Zeng " +0x5898: "Qi " +0x5899: "Qiang " +0x589a: "Liang " +0x589b: "[?] " +0x589c: "Zhui " +0x589d: "Qiao " +0x589e: "Zeng " +0x589f: "Xu " +0x58a0: "Shan " +0x58a1: "Shan " +0x58a2: "Ba " +0x58a3: "Pu " +0x58a4: "Kuai " +0x58a5: "Dong " +0x58a6: "Fan " +0x58a7: "Que " +0x58a8: "Mo " +0x58a9: "Dun " +0x58aa: "Dun " +0x58ab: "Dun " +0x58ac: "Di " +0x58ad: "Sheng " +0x58ae: "Duo " +0x58af: "Duo " +0x58b0: "Tan " +0x58b1: "Deng " +0x58b2: "Wu " +0x58b3: "Fen " +0x58b4: "Huang " +0x58b5: "Tan " +0x58b6: "Da " +0x58b7: "Ye " +0x58b8: "Sho " +0x58b9: "Mama " +0x58ba: "Yu " +0x58bb: "Qiang " +0x58bc: "Ji " +0x58bd: "Qiao " +0x58be: "Ken " +0x58bf: "Yi " +0x58c0: "Pi " +0x58c1: "Bi " +0x58c2: "Dian " +0x58c3: "Jiang " +0x58c4: "Ye " +0x58c5: "Yong " +0x58c6: "Bo " +0x58c7: "Tan " +0x58c8: "Lan " +0x58c9: "Ju " +0x58ca: "Huai " +0x58cb: "Dang " +0x58cc: "Rang " +0x58cd: "Qian " +0x58ce: "Xun " +0x58cf: "Lan " +0x58d0: "Xi " +0x58d1: "He " +0x58d2: "Ai " +0x58d3: "Ya " +0x58d4: "Dao " +0x58d5: "Hao " +0x58d6: "Ruan " +0x58d7: "Mama " +0x58d8: "Lei " +0x58d9: "Kuang " +0x58da: "Lu " +0x58db: "Yan " +0x58dc: "Tan " +0x58dd: "Wei " +0x58de: "Huai " +0x58df: "Long " +0x58e0: "Long " +0x58e1: "Rui " +0x58e2: "Li " +0x58e3: "Lin " +0x58e4: "Rang " +0x58e5: "Ten " +0x58e6: "Xun " +0x58e7: "Yan " +0x58e8: "Lei " +0x58e9: "Ba " +0x58ea: "[?] " +0x58eb: "Shi " +0x58ec: "Ren " +0x58ed: "[?] " +0x58ee: "Zhuang " +0x58ef: "Zhuang " +0x58f0: "Sheng " +0x58f1: "Yi " +0x58f2: "Mai " +0x58f3: "Ke " +0x58f4: "Zhu " +0x58f5: "Zhuang " +0x58f6: "Hu " +0x58f7: "Hu " +0x58f8: "Kun " +0x58f9: "Yi " +0x58fa: "Hu " +0x58fb: "Xu " +0x58fc: "Kun " +0x58fd: "Shou " +0x58fe: "Mang " +0x58ff: "Zun " +/* x059 */ +0x5900: "Shou " +0x5901: "Yi " +0x5902: "Zhi " +0x5903: "Gu " +0x5904: "Chu " +0x5905: "Jiang " +0x5906: "Feng " +0x5907: "Bei " +0x5908: "Cay " +0x5909: "Bian " +0x590a: "Sui " +0x590b: "Qun " +0x590c: "Ling " +0x590d: "Fu " +0x590e: "Zuo " +0x590f: "Xia " +0x5910: "Xiong " +0x5911: "[?] " +0x5912: "Nao " +0x5913: "Xia " +0x5914: "Kui " +0x5915: "Xi " +0x5916: "Wai " +0x5917: "Yuan " +0x5918: "Mao " +0x5919: "Su " +0x591a: "Duo " +0x591b: "Duo " +0x591c: "Ye " +0x591d: "Qing " +0x591e: "Uys " +0x591f: "Gou " +0x5920: "Gou " +0x5921: "Qi " +0x5922: "Meng " +0x5923: "Meng " +0x5924: "Yin " +0x5925: "Huo " +0x5926: "Chen " +0x5927: "Da " +0x5928: "Ze " +0x5929: "Tian " +0x592a: "Tai " +0x592b: "Fu " +0x592c: "Guai " +0x592d: "Yao " +0x592e: "Yang " +0x592f: "Hang " +0x5930: "Gao " +0x5931: "Shi " +0x5932: "Ben " +0x5933: "Tai " +0x5934: "Tou " +0x5935: "Yan " +0x5936: "Bi " +0x5937: "Yi " +0x5938: "Kua " +0x5939: "Jia " +0x593a: "Duo " +0x593b: "Kwu " +0x593c: "Kuang " +0x593d: "Yun " +0x593e: "Jia " +0x593f: "Pa " +0x5940: "En " +0x5941: "Lian " +0x5942: "Huan " +0x5943: "Di " +0x5944: "Yan " +0x5945: "Pao " +0x5946: "Quan " +0x5947: "Qi " +0x5948: "Nai " +0x5949: "Feng " +0x594a: "Xie " +0x594b: "Fen " +0x594c: "Dian " +0x594d: "[?] " +0x594e: "Kui " +0x594f: "Zou " +0x5950: "Huan " +0x5951: "Qi " +0x5952: "Kai " +0x5953: "Zha " +0x5954: "Ben " +0x5955: "Yi " +0x5956: "Jiang " +0x5957: "Tao " +0x5958: "Zang " +0x5959: "Ben " +0x595a: "Xi " +0x595b: "Xiang " +0x595c: "Fei " +0x595d: "Diao " +0x595e: "Xun " +0x595f: "Keng " +0x5960: "Dian " +0x5961: "Ao " +0x5962: "She " +0x5963: "Weng " +0x5964: "Pan " +0x5965: "Ao " +0x5966: "Wu " +0x5967: "Ao " +0x5968: "Jiang " +0x5969: "Lian " +0x596a: "Duo " +0x596b: "Yun " +0x596c: "Jiang " +0x596d: "Shi " +0x596e: "Fen " +0x596f: "Huo " +0x5970: "Bi " +0x5971: "Lian " +0x5972: "Duo " +0x5973: "Nu " +0x5974: "Nu " +0x5975: "Ding " +0x5976: "Nai " +0x5977: "Qian " +0x5978: "Jian " +0x5979: "Ta " +0x597a: "Jiu " +0x597b: "Nan " +0x597c: "Cha " +0x597d: "Hao " +0x597e: "Xian " +0x597f: "Fan " +0x5980: "Ji " +0x5981: "Shuo " +0x5982: "Ru " +0x5983: "Fei " +0x5984: "Wang " +0x5985: "Hong " +0x5986: "Zhuang " +0x5987: "Fu " +0x5988: "Ma " +0x5989: "Dan " +0x598a: "Ren " +0x598b: "Fu " +0x598c: "Jing " +0x598d: "Yan " +0x598e: "Xie " +0x598f: "Wen " +0x5990: "Zhong " +0x5991: "Pa " +0x5992: "Du " +0x5993: "Ji " +0x5994: "Keng " +0x5995: "Zhong " +0x5996: "Yao " +0x5997: "Jin " +0x5998: "Yun " +0x5999: "Miao " +0x599a: "Pei " +0x599b: "Shi " +0x599c: "Yue " +0x599d: "Zhuang " +0x599e: "Niu " +0x599f: "Yan " +0x59a0: "Na " +0x59a1: "Xin " +0x59a2: "Fen " +0x59a3: "Bi " +0x59a4: "Yu " +0x59a5: "Tuo " +0x59a6: "Feng " +0x59a7: "Yuan " +0x59a8: "Fang " +0x59a9: "Wu " +0x59aa: "Yu " +0x59ab: "Gui " +0x59ac: "Du " +0x59ad: "Ba " +0x59ae: "Ni " +0x59af: "Zhou " +0x59b0: "Zhuo " +0x59b1: "Zhao " +0x59b2: "Da " +0x59b3: "Nai " +0x59b4: "Yuan " +0x59b5: "Tou " +0x59b6: "Xuan " +0x59b7: "Zhi " +0x59b8: "E " +0x59b9: "Mei " +0x59ba: "Mo " +0x59bb: "Qi " +0x59bc: "Bi " +0x59bd: "Shen " +0x59be: "Qie " +0x59bf: "E " +0x59c0: "He " +0x59c1: "Xu " +0x59c2: "Fa " +0x59c3: "Zheng " +0x59c4: "Min " +0x59c5: "Ban " +0x59c6: "Mu " +0x59c7: "Fu " +0x59c8: "Ling " +0x59c9: "Zi " +0x59ca: "Zi " +0x59cb: "Shi " +0x59cc: "Ran " +0x59cd: "Shan " +0x59ce: "Yang " +0x59cf: "Man " +0x59d0: "Jie " +0x59d1: "Gu " +0x59d2: "Si " +0x59d3: "Xing " +0x59d4: "Wei " +0x59d5: "Zi " +0x59d6: "Ju " +0x59d7: "Shan " +0x59d8: "Pin " +0x59d9: "Ren " +0x59da: "Yao " +0x59db: "Tong " +0x59dc: "Jiang " +0x59dd: "Shu " +0x59de: "Ji " +0x59df: "Gai " +0x59e0: "Shang " +0x59e1: "Kuo " +0x59e2: "Juan " +0x59e3: "Jiao " +0x59e4: "Gou " +0x59e5: "Mu " +0x59e6: "Jian " +0x59e7: "Jian " +0x59e8: "Yi " +0x59e9: "Nian " +0x59ea: "Zhi " +0x59eb: "Ji " +0x59ec: "Ji " +0x59ed: "Xian " +0x59ee: "Heng " +0x59ef: "Guang " +0x59f0: "Jun " +0x59f1: "Kua " +0x59f2: "Yan " +0x59f3: "Ming " +0x59f4: "Lie " +0x59f5: "Pei " +0x59f6: "Yan " +0x59f7: "You " +0x59f8: "Yan " +0x59f9: "Cha " +0x59fa: "Shen " +0x59fb: "Yin " +0x59fc: "Chi " +0x59fd: "Gui " +0x59fe: "Quan " +0x59ff: "Zi " +/* x05a */ +0x5a00: "Song " +0x5a01: "Wei " +0x5a02: "Hong " +0x5a03: "Wa " +0x5a04: "Lou " +0x5a05: "Ya " +0x5a06: "Rao " +0x5a07: "Jiao " +0x5a08: "Luan " +0x5a09: "Ping " +0x5a0a: "Xian " +0x5a0b: "Shao " +0x5a0c: "Li " +0x5a0d: "Cheng " +0x5a0e: "Xiao " +0x5a0f: "Mang " +0x5a10: "Fu " +0x5a11: "Suo " +0x5a12: "Wu " +0x5a13: "Wei " +0x5a14: "Ke " +0x5a15: "Lai " +0x5a16: "Chuo " +0x5a17: "Ding " +0x5a18: "Niang " +0x5a19: "Xing " +0x5a1a: "Nan " +0x5a1b: "Yu " +0x5a1c: "Nuo " +0x5a1d: "Pei " +0x5a1e: "Nei " +0x5a1f: "Juan " +0x5a20: "Shen " +0x5a21: "Zhi " +0x5a22: "Han " +0x5a23: "Di " +0x5a24: "Zhuang " +0x5a25: "E " +0x5a26: "Pin " +0x5a27: "Tui " +0x5a28: "Han " +0x5a29: "Mian " +0x5a2a: "Wu " +0x5a2b: "Yan " +0x5a2c: "Wu " +0x5a2d: "Xi " +0x5a2e: "Yan " +0x5a2f: "Yu " +0x5a30: "Si " +0x5a31: "Yu " +0x5a32: "Wa " +0x5a33: "[?] " +0x5a34: "Xian " +0x5a35: "Ju " +0x5a36: "Qu " +0x5a37: "Shui " +0x5a38: "Qi " +0x5a39: "Xian " +0x5a3a: "Zhui " +0x5a3b: "Dong " +0x5a3c: "Chang " +0x5a3d: "Lu " +0x5a3e: "Ai " +0x5a3f: "E " +0x5a40: "E " +0x5a41: "Lou " +0x5a42: "Mian " +0x5a43: "Cong " +0x5a44: "Pou " +0x5a45: "Ju " +0x5a46: "Po " +0x5a47: "Cai " +0x5a48: "Ding " +0x5a49: "Wan " +0x5a4a: "Biao " +0x5a4b: "Xiao " +0x5a4c: "Shu " +0x5a4d: "Qi " +0x5a4e: "Hui " +0x5a4f: "Fu " +0x5a50: "E " +0x5a51: "Wo " +0x5a52: "Tan " +0x5a53: "Fei " +0x5a54: "Wei " +0x5a55: "Jie " +0x5a56: "Tian " +0x5a57: "Ni " +0x5a58: "Quan " +0x5a59: "Jing " +0x5a5a: "Hun " +0x5a5b: "Jing " +0x5a5c: "Qian " +0x5a5d: "Dian " +0x5a5e: "Xing " +0x5a5f: "Hu " +0x5a60: "Wa " +0x5a61: "Lai " +0x5a62: "Bi " +0x5a63: "Yin " +0x5a64: "Chou " +0x5a65: "Chuo " +0x5a66: "Fu " +0x5a67: "Jing " +0x5a68: "Lun " +0x5a69: "Yan " +0x5a6a: "Lan " +0x5a6b: "Kun " +0x5a6c: "Yin " +0x5a6d: "Ya " +0x5a6e: "Ju " +0x5a6f: "Li " +0x5a70: "Dian " +0x5a71: "Xian " +0x5a72: "Hwa " +0x5a73: "Hua " +0x5a74: "Ying " +0x5a75: "Chan " +0x5a76: "Shen " +0x5a77: "Ting " +0x5a78: "Dang " +0x5a79: "Yao " +0x5a7a: "Wu " +0x5a7b: "Nan " +0x5a7c: "Ruo " +0x5a7d: "Jia " +0x5a7e: "Tou " +0x5a7f: "Xu " +0x5a80: "Yu " +0x5a81: "Wei " +0x5a82: "Ti " +0x5a83: "Rou " +0x5a84: "Mei " +0x5a85: "Dan " +0x5a86: "Ruan " +0x5a87: "Qin " +0x5a88: "Hui " +0x5a89: "Wu " +0x5a8a: "Qian " +0x5a8b: "Chun " +0x5a8c: "Mao " +0x5a8d: "Fu " +0x5a8e: "Jie " +0x5a8f: "Duan " +0x5a90: "Xi " +0x5a91: "Zhong " +0x5a92: "Mei " +0x5a93: "Huang " +0x5a94: "Mian " +0x5a95: "An " +0x5a96: "Ying " +0x5a97: "Xuan " +0x5a98: "Jie " +0x5a99: "Wei " +0x5a9a: "Mei " +0x5a9b: "Yuan " +0x5a9c: "Zhen " +0x5a9d: "Qiu " +0x5a9e: "Ti " +0x5a9f: "Xie " +0x5aa0: "Tuo " +0x5aa1: "Lian " +0x5aa2: "Mao " +0x5aa3: "Ran " +0x5aa4: "Si " +0x5aa5: "Pian " +0x5aa6: "Wei " +0x5aa7: "Wa " +0x5aa8: "Jiu " +0x5aa9: "Hu " +0x5aaa: "Ao " +0x5aab: "[?] " +0x5aac: "Bou " +0x5aad: "Xu " +0x5aae: "Tou " +0x5aaf: "Gui " +0x5ab0: "Zou " +0x5ab1: "Yao " +0x5ab2: "Pi " +0x5ab3: "Xi " +0x5ab4: "Yuan " +0x5ab5: "Ying " +0x5ab6: "Rong " +0x5ab7: "Ru " +0x5ab8: "Chi " +0x5ab9: "Liu " +0x5aba: "Mei " +0x5abb: "Pan " +0x5abc: "Ao " +0x5abd: "Ma " +0x5abe: "Gou " +0x5abf: "Kui " +0x5ac0: "Qin " +0x5ac1: "Jia " +0x5ac2: "Sao " +0x5ac3: "Zhen " +0x5ac4: "Yuan " +0x5ac5: "Cha " +0x5ac6: "Yong " +0x5ac7: "Ming " +0x5ac8: "Ying " +0x5ac9: "Ji " +0x5aca: "Su " +0x5acb: "Niao " +0x5acc: "Xian " +0x5acd: "Tao " +0x5ace: "Pang " +0x5acf: "Lang " +0x5ad0: "Nao " +0x5ad1: "Bao " +0x5ad2: "Ai " +0x5ad3: "Pi " +0x5ad4: "Pin " +0x5ad5: "Yi " +0x5ad6: "Piao " +0x5ad7: "Yu " +0x5ad8: "Lei " +0x5ad9: "Xuan " +0x5ada: "Man " +0x5adb: "Yi " +0x5adc: "Zhang " +0x5add: "Kang " +0x5ade: "Yong " +0x5adf: "Ni " +0x5ae0: "Li " +0x5ae1: "Di " +0x5ae2: "Gui " +0x5ae3: "Yan " +0x5ae4: "Jin " +0x5ae5: "Zhuan " +0x5ae6: "Chang " +0x5ae7: "Ce " +0x5ae8: "Han " +0x5ae9: "Nen " +0x5aea: "Lao " +0x5aeb: "Mo " +0x5aec: "Zhe " +0x5aed: "Hu " +0x5aee: "Hu " +0x5aef: "Ao " +0x5af0: "Nen " +0x5af1: "Qiang " +0x5af2: "Ma " +0x5af3: "Pie " +0x5af4: "Gu " +0x5af5: "Wu " +0x5af6: "Jiao " +0x5af7: "Tuo " +0x5af8: "Zhan " +0x5af9: "Mao " +0x5afa: "Xian " +0x5afb: "Xian " +0x5afc: "Mo " +0x5afd: "Liao " +0x5afe: "Lian " +0x5aff: "Hua " +/* x05b */ +0x5b00: "Gui " +0x5b01: "Deng " +0x5b02: "Zhi " +0x5b03: "Xu " +0x5b04: "Yi " +0x5b05: "Hua " +0x5b06: "Xi " +0x5b07: "Hui " +0x5b08: "Rao " +0x5b09: "Xi " +0x5b0a: "Yan " +0x5b0b: "Chan " +0x5b0c: "Jiao " +0x5b0d: "Mei " +0x5b0e: "Fan " +0x5b0f: "Fan " +0x5b10: "Xian " +0x5b11: "Yi " +0x5b12: "Wei " +0x5b13: "Jiao " +0x5b14: "Fu " +0x5b15: "Shi " +0x5b16: "Bi " +0x5b17: "Shan " +0x5b18: "Sui " +0x5b19: "Qiang " +0x5b1a: "Lian " +0x5b1b: "Huan " +0x5b1c: "Xin " +0x5b1d: "Niao " +0x5b1e: "Dong " +0x5b1f: "Yi " +0x5b20: "Can " +0x5b21: "Ai " +0x5b22: "Niang " +0x5b23: "Neng " +0x5b24: "Ma " +0x5b25: "Tiao " +0x5b26: "Chou " +0x5b27: "Jin " +0x5b28: "Ci " +0x5b29: "Yu " +0x5b2a: "Pin " +0x5b2b: "Yong " +0x5b2c: "Xu " +0x5b2d: "Nai " +0x5b2e: "Yan " +0x5b2f: "Tai " +0x5b30: "Ying " +0x5b31: "Can " +0x5b32: "Niao " +0x5b33: "Wo " +0x5b34: "Ying " +0x5b35: "Mian " +0x5b36: "Kaka " +0x5b37: "Ma " +0x5b38: "Shen " +0x5b39: "Xing " +0x5b3a: "Ni " +0x5b3b: "Du " +0x5b3c: "Liu " +0x5b3d: "Yuan " +0x5b3e: "Lan " +0x5b3f: "Yan " +0x5b40: "Shuang " +0x5b41: "Ling " +0x5b42: "Jiao " +0x5b43: "Niang " +0x5b44: "Lan " +0x5b45: "Xian " +0x5b46: "Ying " +0x5b47: "Shuang " +0x5b48: "Shuai " +0x5b49: "Quan " +0x5b4a: "Mi " +0x5b4b: "Li " +0x5b4c: "Luan " +0x5b4d: "Yan " +0x5b4e: "Zhu " +0x5b4f: "Lan " +0x5b50: "Zi " +0x5b51: "Jie " +0x5b52: "Jue " +0x5b53: "Jue " +0x5b54: "Kong " +0x5b55: "Yun " +0x5b56: "Zi " +0x5b57: "Zi " +0x5b58: "Cun " +0x5b59: "Sun " +0x5b5a: "Fu " +0x5b5b: "Bei " +0x5b5c: "Zi " +0x5b5d: "Xiao " +0x5b5e: "Xin " +0x5b5f: "Meng " +0x5b60: "Si " +0x5b61: "Tai " +0x5b62: "Bao " +0x5b63: "Ji " +0x5b64: "Gu " +0x5b65: "Nu " +0x5b66: "Xue " +0x5b67: "[?] " +0x5b68: "Zhuan " +0x5b69: "Hai " +0x5b6a: "Luan " +0x5b6b: "Sun " +0x5b6c: "Huai " +0x5b6d: "Mie " +0x5b6e: "Cong " +0x5b6f: "Qian " +0x5b70: "Shu " +0x5b71: "Chan " +0x5b72: "Ya " +0x5b73: "Zi " +0x5b74: "Ni " +0x5b75: "Fu " +0x5b76: "Zi " +0x5b77: "Li " +0x5b78: "Xue " +0x5b79: "Bo " +0x5b7a: "Ru " +0x5b7b: "Lai " +0x5b7c: "Nie " +0x5b7d: "Nie " +0x5b7e: "Ying " +0x5b7f: "Luan " +0x5b80: "Mian " +0x5b81: "Zhu " +0x5b82: "Rong " +0x5b83: "Ta " +0x5b84: "Gui " +0x5b85: "Zhai " +0x5b86: "Qiong " +0x5b87: "Yu " +0x5b88: "Shou " +0x5b89: "An " +0x5b8a: "Tu " +0x5b8b: "Song " +0x5b8c: "Wan " +0x5b8d: "Rou " +0x5b8e: "Yao " +0x5b8f: "Hong " +0x5b90: "Yi " +0x5b91: "Jing " +0x5b92: "Zhun " +0x5b93: "Mi " +0x5b94: "Zhu " +0x5b95: "Dang " +0x5b96: "Hong " +0x5b97: "Zong " +0x5b98: "Guan " +0x5b99: "Zhou " +0x5b9a: "Ding " +0x5b9b: "Wan " +0x5b9c: "Yi " +0x5b9d: "Bao " +0x5b9e: "Shi " +0x5b9f: "Shi " +0x5ba0: "Chong " +0x5ba1: "Shen " +0x5ba2: "Ke " +0x5ba3: "Xuan " +0x5ba4: "Shi " +0x5ba5: "You " +0x5ba6: "Huan " +0x5ba7: "Yi " +0x5ba8: "Tiao " +0x5ba9: "Shi " +0x5baa: "Xian " +0x5bab: "Gong " +0x5bac: "Cheng " +0x5bad: "Qun " +0x5bae: "Gong " +0x5baf: "Xiao " +0x5bb0: "Zai " +0x5bb1: "Zha " +0x5bb2: "Bao " +0x5bb3: "Hai " +0x5bb4: "Yan " +0x5bb5: "Xiao " +0x5bb6: "Jia " +0x5bb7: "Shen " +0x5bb8: "Chen " +0x5bb9: "Rong " +0x5bba: "Huang " +0x5bbb: "Mi " +0x5bbc: "Kou " +0x5bbd: "Kuan " +0x5bbe: "Bin " +0x5bbf: "Su " +0x5bc0: "Cai " +0x5bc1: "Zan " +0x5bc2: "Ji " +0x5bc3: "Yuan " +0x5bc4: "Ji " +0x5bc5: "Yin " +0x5bc6: "Mi " +0x5bc7: "Kou " +0x5bc8: "Qing " +0x5bc9: "Que " +0x5bca: "Zhen " +0x5bcb: "Jian " +0x5bcc: "Fu " +0x5bcd: "Ning " +0x5bce: "Bing " +0x5bcf: "Huan " +0x5bd0: "Mei " +0x5bd1: "Qin " +0x5bd2: "Han " +0x5bd3: "Yu " +0x5bd4: "Shi " +0x5bd5: "Ning " +0x5bd6: "Qin " +0x5bd7: "Ning " +0x5bd8: "Zhi " +0x5bd9: "Yu " +0x5bda: "Bao " +0x5bdb: "Kuan " +0x5bdc: "Ning " +0x5bdd: "Qin " +0x5bde: "Mo " +0x5bdf: "Cha " +0x5be0: "Ju " +0x5be1: "Gua " +0x5be2: "Qin " +0x5be3: "Hu " +0x5be4: "Wu " +0x5be5: "Liao " +0x5be6: "Shi " +0x5be7: "Zhu " +0x5be8: "Zhai " +0x5be9: "Shen " +0x5bea: "Wei " +0x5beb: "Xie " +0x5bec: "Kuan " +0x5bed: "Hui " +0x5bee: "Liao " +0x5bef: "Jun " +0x5bf0: "Huan " +0x5bf1: "Yi " +0x5bf2: "Yi " +0x5bf3: "Bao " +0x5bf4: "Qin " +0x5bf5: "Chong " +0x5bf6: "Bao " +0x5bf7: "Feng " +0x5bf8: "Cun " +0x5bf9: "Dui " +0x5bfa: "Si " +0x5bfb: "Xun " +0x5bfc: "Dao " +0x5bfd: "Lu " +0x5bfe: "Dui " +0x5bff: "Shou " +/* x05c */ +0x5c00: "Po " +0x5c01: "Feng " +0x5c02: "Zhuan " +0x5c03: "Fu " +0x5c04: "She " +0x5c05: "Ke " +0x5c06: "Jiang " +0x5c07: "Jiang " +0x5c08: "Zhuan " +0x5c09: "Wei " +0x5c0a: "Zun " +0x5c0b: "Xun " +0x5c0c: "Shu " +0x5c0d: "Dui " +0x5c0e: "Dao " +0x5c0f: "Xiao " +0x5c10: "Ji " +0x5c11: "Shao " +0x5c12: "Er " +0x5c13: "Er " +0x5c14: "Er " +0x5c15: "Ga " +0x5c16: "Jian " +0x5c17: "Shu " +0x5c18: "Chen " +0x5c19: "Shang " +0x5c1a: "Shang " +0x5c1b: "Mo " +0x5c1c: "Ga " +0x5c1d: "Chang " +0x5c1e: "Liao " +0x5c1f: "Xian " +0x5c20: "Xian " +0x5c21: "[?] " +0x5c22: "Wang " +0x5c23: "Wang " +0x5c24: "You " +0x5c25: "Liao " +0x5c26: "Liao " +0x5c27: "Yao " +0x5c28: "Mang " +0x5c29: "Wang " +0x5c2a: "Wang " +0x5c2b: "Wang " +0x5c2c: "Ga " +0x5c2d: "Yao " +0x5c2e: "Duo " +0x5c2f: "Kui " +0x5c30: "Zhong " +0x5c31: "Jiu " +0x5c32: "Gan " +0x5c33: "Gu " +0x5c34: "Gan " +0x5c35: "Tui " +0x5c36: "Gan " +0x5c37: "Gan " +0x5c38: "Shi " +0x5c39: "Yin " +0x5c3a: "Chi " +0x5c3b: "Kao " +0x5c3c: "Ni " +0x5c3d: "Jin " +0x5c3e: "Wei " +0x5c3f: "Niao " +0x5c40: "Ju " +0x5c41: "Pi " +0x5c42: "Ceng " +0x5c43: "Xi " +0x5c44: "Bi " +0x5c45: "Ju " +0x5c46: "Jie " +0x5c47: "Tian " +0x5c48: "Qu " +0x5c49: "Ti " +0x5c4a: "Jie " +0x5c4b: "Wu " +0x5c4c: "Diao " +0x5c4d: "Shi " +0x5c4e: "Shi " +0x5c4f: "Ping " +0x5c50: "Ji " +0x5c51: "Xie " +0x5c52: "Chen " +0x5c53: "Xi " +0x5c54: "Ni " +0x5c55: "Zhan " +0x5c56: "Xi " +0x5c57: "[?] " +0x5c58: "Man " +0x5c59: "E " +0x5c5a: "Lou " +0x5c5b: "Ping " +0x5c5c: "Ti " +0x5c5d: "Fei " +0x5c5e: "Shu " +0x5c5f: "Xie " +0x5c60: "Tu " +0x5c61: "Lu " +0x5c62: "Lu " +0x5c63: "Xi " +0x5c64: "Ceng " +0x5c65: "Lu " +0x5c66: "Ju " +0x5c67: "Xie " +0x5c68: "Ju " +0x5c69: "Jue " +0x5c6a: "Liao " +0x5c6b: "Jue " +0x5c6c: "Shu " +0x5c6d: "Xi " +0x5c6e: "Che " +0x5c6f: "Tun " +0x5c70: "Ni " +0x5c71: "Shan " +0x5c72: "[?] " +0x5c73: "Xian " +0x5c74: "Li " +0x5c75: "Xue " +0x5c76: "Nata " +0x5c77: "[?] " +0x5c78: "Long " +0x5c79: "Yi " +0x5c7a: "Qi " +0x5c7b: "Ren " +0x5c7c: "Wu " +0x5c7d: "Han " +0x5c7e: "Shen " +0x5c7f: "Yu " +0x5c80: "Chu " +0x5c81: "Sui " +0x5c82: "Qi " +0x5c83: "[?] " +0x5c84: "Yue " +0x5c85: "Ban " +0x5c86: "Yao " +0x5c87: "Ang " +0x5c88: "Ya " +0x5c89: "Wu " +0x5c8a: "Jie " +0x5c8b: "E " +0x5c8c: "Ji " +0x5c8d: "Qian " +0x5c8e: "Fen " +0x5c8f: "Yuan " +0x5c90: "Qi " +0x5c91: "Cen " +0x5c92: "Qian " +0x5c93: "Qi " +0x5c94: "Cha " +0x5c95: "Jie " +0x5c96: "Qu " +0x5c97: "Gang " +0x5c98: "Xian " +0x5c99: "Ao " +0x5c9a: "Lan " +0x5c9b: "Dao " +0x5c9c: "Ba " +0x5c9d: "Zuo " +0x5c9e: "Zuo " +0x5c9f: "Yang " +0x5ca0: "Ju " +0x5ca1: "Gang " +0x5ca2: "Ke " +0x5ca3: "Gou " +0x5ca4: "Xue " +0x5ca5: "Bei " +0x5ca6: "Li " +0x5ca7: "Tiao " +0x5ca8: "Ju " +0x5ca9: "Yan " +0x5caa: "Fu " +0x5cab: "Xiu " +0x5cac: "Jia " +0x5cad: "Ling " +0x5cae: "Tuo " +0x5caf: "Pei " +0x5cb0: "You " +0x5cb1: "Dai " +0x5cb2: "Kuang " +0x5cb3: "Yue " +0x5cb4: "Qu " +0x5cb5: "Hu " +0x5cb6: "Po " +0x5cb7: "Min " +0x5cb8: "An " +0x5cb9: "Tiao " +0x5cba: "Ling " +0x5cbb: "Chi " +0x5cbc: "Yuri " +0x5cbd: "Dong " +0x5cbe: "Cem " +0x5cbf: "Kui " +0x5cc0: "Xiu " +0x5cc1: "Mao " +0x5cc2: "Tong " +0x5cc3: "Xue " +0x5cc4: "Yi " +0x5cc5: "Kura " +0x5cc6: "He " +0x5cc7: "Ke " +0x5cc8: "Luo " +0x5cc9: "E " +0x5cca: "Fu " +0x5ccb: "Xun " +0x5ccc: "Die " +0x5ccd: "Lu " +0x5cce: "An " +0x5ccf: "Er " +0x5cd0: "Gai " +0x5cd1: "Quan " +0x5cd2: "Tong " +0x5cd3: "Yi " +0x5cd4: "Mu " +0x5cd5: "Shi " +0x5cd6: "An " +0x5cd7: "Wei " +0x5cd8: "Hu " +0x5cd9: "Zhi " +0x5cda: "Mi " +0x5cdb: "Li " +0x5cdc: "Ji " +0x5cdd: "Tong " +0x5cde: "Wei " +0x5cdf: "You " +0x5ce0: "Sang " +0x5ce1: "Xia " +0x5ce2: "Li " +0x5ce3: "Yao " +0x5ce4: "Jiao " +0x5ce5: "Zheng " +0x5ce6: "Luan " +0x5ce7: "Jiao " +0x5ce8: "E " +0x5ce9: "E " +0x5cea: "Yu " +0x5ceb: "Ye " +0x5cec: "Bu " +0x5ced: "Qiao " +0x5cee: "Qun " +0x5cef: "Feng " +0x5cf0: "Feng " +0x5cf1: "Nao " +0x5cf2: "Li " +0x5cf3: "You " +0x5cf4: "Xian " +0x5cf5: "Hong " +0x5cf6: "Dao " +0x5cf7: "Shen " +0x5cf8: "Cheng " +0x5cf9: "Tu " +0x5cfa: "Geng " +0x5cfb: "Jun " +0x5cfc: "Hao " +0x5cfd: "Xia " +0x5cfe: "Yin " +0x5cff: "Yu " +/* x05d */ +0x5d00: "Lang " +0x5d01: "Kan " +0x5d02: "Lao " +0x5d03: "Lai " +0x5d04: "Xian " +0x5d05: "Que " +0x5d06: "Kong " +0x5d07: "Chong " +0x5d08: "Chong " +0x5d09: "Ta " +0x5d0a: "Lin " +0x5d0b: "Hua " +0x5d0c: "Ju " +0x5d0d: "Lai " +0x5d0e: "Qi " +0x5d0f: "Min " +0x5d10: "Kun " +0x5d11: "Kun " +0x5d12: "Zu " +0x5d13: "Gu " +0x5d14: "Cui " +0x5d15: "Ya " +0x5d16: "Ya " +0x5d17: "Gang " +0x5d18: "Lun " +0x5d19: "Lun " +0x5d1a: "Leng " +0x5d1b: "Jue " +0x5d1c: "Duo " +0x5d1d: "Zheng " +0x5d1e: "Guo " +0x5d1f: "Yin " +0x5d20: "Dong " +0x5d21: "Han " +0x5d22: "Zheng " +0x5d23: "Wei " +0x5d24: "Yao " +0x5d25: "Pi " +0x5d26: "Yan " +0x5d27: "Song " +0x5d28: "Jie " +0x5d29: "Beng " +0x5d2a: "Zu " +0x5d2b: "Jue " +0x5d2c: "Dong " +0x5d2d: "Zhan " +0x5d2e: "Gu " +0x5d2f: "Yin " +0x5d30: "[?] " +0x5d31: "Ze " +0x5d32: "Huang " +0x5d33: "Yu " +0x5d34: "Wei " +0x5d35: "Yang " +0x5d36: "Feng " +0x5d37: "Qiu " +0x5d38: "Dun " +0x5d39: "Ti " +0x5d3a: "Yi " +0x5d3b: "Zhi " +0x5d3c: "Shi " +0x5d3d: "Zai " +0x5d3e: "Yao " +0x5d3f: "E " +0x5d40: "Zhu " +0x5d41: "Kan " +0x5d42: "Lu " +0x5d43: "Yan " +0x5d44: "Mei " +0x5d45: "Gan " +0x5d46: "Ji " +0x5d47: "Ji " +0x5d48: "Huan " +0x5d49: "Ting " +0x5d4a: "Sheng " +0x5d4b: "Mei " +0x5d4c: "Qian " +0x5d4d: "Wu " +0x5d4e: "Yu " +0x5d4f: "Zong " +0x5d50: "Lan " +0x5d51: "Jue " +0x5d52: "Yan " +0x5d53: "Yan " +0x5d54: "Wei " +0x5d55: "Zong " +0x5d56: "Cha " +0x5d57: "Sui " +0x5d58: "Rong " +0x5d59: "Yamashina " +0x5d5a: "Qin " +0x5d5b: "Yu " +0x5d5c: "Kewashii " +0x5d5d: "Lou " +0x5d5e: "Tu " +0x5d5f: "Dui " +0x5d60: "Xi " +0x5d61: "Weng " +0x5d62: "Cang " +0x5d63: "Dang " +0x5d64: "Hong " +0x5d65: "Jie " +0x5d66: "Ai " +0x5d67: "Liu " +0x5d68: "Wu " +0x5d69: "Song " +0x5d6a: "Qiao " +0x5d6b: "Zi " +0x5d6c: "Wei " +0x5d6d: "Beng " +0x5d6e: "Dian " +0x5d6f: "Cuo " +0x5d70: "Qian " +0x5d71: "Yong " +0x5d72: "Nie " +0x5d73: "Cuo " +0x5d74: "Ji " +0x5d75: "[?] " +0x5d76: "Tao " +0x5d77: "Song " +0x5d78: "Zong " +0x5d79: "Jiang " +0x5d7a: "Liao " +0x5d7b: "Kang " +0x5d7c: "Chan " +0x5d7d: "Die " +0x5d7e: "Cen " +0x5d7f: "Ding " +0x5d80: "Tu " +0x5d81: "Lou " +0x5d82: "Zhang " +0x5d83: "Zhan " +0x5d84: "Zhan " +0x5d85: "Ao " +0x5d86: "Cao " +0x5d87: "Qu " +0x5d88: "Qiang " +0x5d89: "Zui " +0x5d8a: "Zui " +0x5d8b: "Dao " +0x5d8c: "Dao " +0x5d8d: "Xi " +0x5d8e: "Yu " +0x5d8f: "Bo " +0x5d90: "Long " +0x5d91: "Xiang " +0x5d92: "Ceng " +0x5d93: "Bo " +0x5d94: "Qin " +0x5d95: "Jiao " +0x5d96: "Yan " +0x5d97: "Lao " +0x5d98: "Zhan " +0x5d99: "Lin " +0x5d9a: "Liao " +0x5d9b: "Liao " +0x5d9c: "Jin " +0x5d9d: "Deng " +0x5d9e: "Duo " +0x5d9f: "Zun " +0x5da0: "Jiao " +0x5da1: "Gui " +0x5da2: "Yao " +0x5da3: "Qiao " +0x5da4: "Yao " +0x5da5: "Jue " +0x5da6: "Zhan " +0x5da7: "Yi " +0x5da8: "Xue " +0x5da9: "Nao " +0x5daa: "Ye " +0x5dab: "Ye " +0x5dac: "Yi " +0x5dad: "E " +0x5dae: "Xian " +0x5daf: "Ji " +0x5db0: "Xie " +0x5db1: "Ke " +0x5db2: "Xi " +0x5db3: "Di " +0x5db4: "Ao " +0x5db5: "Zui " +0x5db6: "[?] " +0x5db7: "Ni " +0x5db8: "Rong " +0x5db9: "Dao " +0x5dba: "Ling " +0x5dbb: "Za " +0x5dbc: "Yu " +0x5dbd: "Yue " +0x5dbe: "Yin " +0x5dbf: "[?] " +0x5dc0: "Jie " +0x5dc1: "Li " +0x5dc2: "Sui " +0x5dc3: "Long " +0x5dc4: "Long " +0x5dc5: "Dian " +0x5dc6: "Ying " +0x5dc7: "Xi " +0x5dc8: "Ju " +0x5dc9: "Chan " +0x5dca: "Ying " +0x5dcb: "Kui " +0x5dcc: "Yan " +0x5dcd: "Wei " +0x5dce: "Nao " +0x5dcf: "Quan " +0x5dd0: "Chao " +0x5dd1: "Cuan " +0x5dd2: "Luan " +0x5dd3: "Dian " +0x5dd4: "Dian " +0x5dd5: "[?] " +0x5dd6: "Yan " +0x5dd7: "Yan " +0x5dd8: "Yan " +0x5dd9: "Nao " +0x5dda: "Yan " +0x5ddb: "Chuan " +0x5ddc: "Gui " +0x5ddd: "Chuan " +0x5dde: "Zhou " +0x5ddf: "Huang " +0x5de0: "Jing " +0x5de1: "Xun " +0x5de2: "Chao " +0x5de3: "Chao " +0x5de4: "Lie " +0x5de5: "Gong " +0x5de6: "Zuo " +0x5de7: "Qiao " +0x5de8: "Ju " +0x5de9: "Gong " +0x5dea: "Kek " +0x5deb: "Wu " +0x5dec: "Pwu " +0x5ded: "Pwu " +0x5dee: "Chai " +0x5def: "Qiu " +0x5df0: "Qiu " +0x5df1: "Ji " +0x5df2: "Yi " +0x5df3: "Si " +0x5df4: "Ba " +0x5df5: "Zhi " +0x5df6: "Zhao " +0x5df7: "Xiang " +0x5df8: "Yi " +0x5df9: "Jin " +0x5dfa: "Xun " +0x5dfb: "Juan " +0x5dfc: "Phas " +0x5dfd: "Xun " +0x5dfe: "Jin " +0x5dff: "Fu " +/* x05e */ +0x5e00: "Za " +0x5e01: "Bi " +0x5e02: "Shi " +0x5e03: "Bu " +0x5e04: "Ding " +0x5e05: "Shuai " +0x5e06: "Fan " +0x5e07: "Nie " +0x5e08: "Shi " +0x5e09: "Fen " +0x5e0a: "Pa " +0x5e0b: "Zhi " +0x5e0c: "Xi " +0x5e0d: "Hu " +0x5e0e: "Dan " +0x5e0f: "Wei " +0x5e10: "Zhang " +0x5e11: "Tang " +0x5e12: "Dai " +0x5e13: "Ma " +0x5e14: "Pei " +0x5e15: "Pa " +0x5e16: "Tie " +0x5e17: "Fu " +0x5e18: "Lian " +0x5e19: "Zhi " +0x5e1a: "Zhou " +0x5e1b: "Bo " +0x5e1c: "Zhi " +0x5e1d: "Di " +0x5e1e: "Mo " +0x5e1f: "Yi " +0x5e20: "Yi " +0x5e21: "Ping " +0x5e22: "Qia " +0x5e23: "Juan " +0x5e24: "Ru " +0x5e25: "Shuai " +0x5e26: "Dai " +0x5e27: "Zheng " +0x5e28: "Shui " +0x5e29: "Qiao " +0x5e2a: "Zhen " +0x5e2b: "Shi " +0x5e2c: "Qun " +0x5e2d: "Xi " +0x5e2e: "Bang " +0x5e2f: "Dai " +0x5e30: "Gui " +0x5e31: "Chou " +0x5e32: "Ping " +0x5e33: "Zhang " +0x5e34: "Sha " +0x5e35: "Wan " +0x5e36: "Dai " +0x5e37: "Wei " +0x5e38: "Chang " +0x5e39: "Sha " +0x5e3a: "Qi " +0x5e3b: "Ze " +0x5e3c: "Guo " +0x5e3d: "Mao " +0x5e3e: "Du " +0x5e3f: "Hou " +0x5e40: "Zheng " +0x5e41: "Xu " +0x5e42: "Mi " +0x5e43: "Wei " +0x5e44: "Wo " +0x5e45: "Fu " +0x5e46: "Yi " +0x5e47: "Bang " +0x5e48: "Ping " +0x5e49: "Tazuna " +0x5e4a: "Gong " +0x5e4b: "Pan " +0x5e4c: "Huang " +0x5e4d: "Dao " +0x5e4e: "Mi " +0x5e4f: "Jia " +0x5e50: "Teng " +0x5e51: "Hui " +0x5e52: "Zhong " +0x5e53: "Shan " +0x5e54: "Man " +0x5e55: "Mu " +0x5e56: "Biao " +0x5e57: "Guo " +0x5e58: "Ze " +0x5e59: "Mu " +0x5e5a: "Bang " +0x5e5b: "Zhang " +0x5e5c: "Jiong " +0x5e5d: "Chan " +0x5e5e: "Fu " +0x5e5f: "Zhi " +0x5e60: "Hu " +0x5e61: "Fan " +0x5e62: "Chuang " +0x5e63: "Bi " +0x5e64: "Hei " +0x5e65: "[?] " +0x5e66: "Mi " +0x5e67: "Qiao " +0x5e68: "Chan " +0x5e69: "Fen " +0x5e6a: "Meng " +0x5e6b: "Bang " +0x5e6c: "Chou " +0x5e6d: "Mie " +0x5e6e: "Chu " +0x5e6f: "Jie " +0x5e70: "Xian " +0x5e71: "Lan " +0x5e72: "Gan " +0x5e73: "Ping " +0x5e74: "Nian " +0x5e75: "Qian " +0x5e76: "Bing " +0x5e77: "Bing " +0x5e78: "Xing " +0x5e79: "Gan " +0x5e7a: "Yao " +0x5e7b: "Huan " +0x5e7c: "You " +0x5e7d: "You " +0x5e7e: "Ji " +0x5e7f: "Yan " +0x5e80: "Pi " +0x5e81: "Ting " +0x5e82: "Ze " +0x5e83: "Guang " +0x5e84: "Zhuang " +0x5e85: "Mo " +0x5e86: "Qing " +0x5e87: "Bi " +0x5e88: "Qin " +0x5e89: "Dun " +0x5e8a: "Chuang " +0x5e8b: "Gui " +0x5e8c: "Ya " +0x5e8d: "Bai " +0x5e8e: "Jie " +0x5e8f: "Xu " +0x5e90: "Lu " +0x5e91: "Wu " +0x5e92: "[?] " +0x5e93: "Ku " +0x5e94: "Ying " +0x5e95: "Di " +0x5e96: "Pao " +0x5e97: "Dian " +0x5e98: "Ya " +0x5e99: "Miao " +0x5e9a: "Geng " +0x5e9b: "Ci " +0x5e9c: "Fu " +0x5e9d: "Tong " +0x5e9e: "Pang " +0x5e9f: "Fei " +0x5ea0: "Xiang " +0x5ea1: "Yi " +0x5ea2: "Zhi " +0x5ea3: "Tiao " +0x5ea4: "Zhi " +0x5ea5: "Xiu " +0x5ea6: "Du " +0x5ea7: "Zuo " +0x5ea8: "Xiao " +0x5ea9: "Tu " +0x5eaa: "Gui " +0x5eab: "Ku " +0x5eac: "Pang " +0x5ead: "Ting " +0x5eae: "You " +0x5eaf: "Bu " +0x5eb0: "Ding " +0x5eb1: "Cheng " +0x5eb2: "Lai " +0x5eb3: "Bei " +0x5eb4: "Ji " +0x5eb5: "An " +0x5eb6: "Shu " +0x5eb7: "Kang " +0x5eb8: "Yong " +0x5eb9: "Tuo " +0x5eba: "Song " +0x5ebb: "Shu " +0x5ebc: "Qing " +0x5ebd: "Yu " +0x5ebe: "Yu " +0x5ebf: "Miao " +0x5ec0: "Sou " +0x5ec1: "Ce " +0x5ec2: "Xiang " +0x5ec3: "Fei " +0x5ec4: "Jiu " +0x5ec5: "He " +0x5ec6: "Hui " +0x5ec7: "Liu " +0x5ec8: "Sha " +0x5ec9: "Lian " +0x5eca: "Lang " +0x5ecb: "Sou " +0x5ecc: "Jian " +0x5ecd: "Pou " +0x5ece: "Qing " +0x5ecf: "Jiu " +0x5ed0: "Jiu " +0x5ed1: "Qin " +0x5ed2: "Ao " +0x5ed3: "Kuo " +0x5ed4: "Lou " +0x5ed5: "Yin " +0x5ed6: "Liao " +0x5ed7: "Dai " +0x5ed8: "Lu " +0x5ed9: "Yi " +0x5eda: "Chu " +0x5edb: "Chan " +0x5edc: "Tu " +0x5edd: "Si " +0x5ede: "Xin " +0x5edf: "Miao " +0x5ee0: "Chang " +0x5ee1: "Wu " +0x5ee2: "Fei " +0x5ee3: "Guang " +0x5ee4: "Koc " +0x5ee5: "Kuai " +0x5ee6: "Bi " +0x5ee7: "Qiang " +0x5ee8: "Xie " +0x5ee9: "Lin " +0x5eea: "Lin " +0x5eeb: "Liao " +0x5eec: "Lu " +0x5eed: "[?] " +0x5eee: "Ying " +0x5eef: "Xian " +0x5ef0: "Ting " +0x5ef1: "Yong " +0x5ef2: "Li " +0x5ef3: "Ting " +0x5ef4: "Yin " +0x5ef5: "Xun " +0x5ef6: "Yan " +0x5ef7: "Ting " +0x5ef8: "Di " +0x5ef9: "Po " +0x5efa: "Jian " +0x5efb: "Hui " +0x5efc: "Nai " +0x5efd: "Hui " +0x5efe: "Gong " +0x5eff: "Nian " +/* x05f */ +0x5f00: "Kai " +0x5f01: "Bian " +0x5f02: "Yi " +0x5f03: "Qi " +0x5f04: "Nong " +0x5f05: "Fen " +0x5f06: "Ju " +0x5f07: "Yan " +0x5f08: "Yi " +0x5f09: "Zang " +0x5f0a: "Bi " +0x5f0b: "Yi " +0x5f0c: "Yi " +0x5f0d: "Er " +0x5f0e: "San " +0x5f0f: "Shi " +0x5f10: "Er " +0x5f11: "Shi " +0x5f12: "Shi " +0x5f13: "Gong " +0x5f14: "Diao " +0x5f15: "Yin " +0x5f16: "Hu " +0x5f17: "Fu " +0x5f18: "Hong " +0x5f19: "Wu " +0x5f1a: "Tui " +0x5f1b: "Chi " +0x5f1c: "Jiang " +0x5f1d: "Ba " +0x5f1e: "Shen " +0x5f1f: "Di " +0x5f20: "Zhang " +0x5f21: "Jue " +0x5f22: "Tao " +0x5f23: "Fu " +0x5f24: "Di " +0x5f25: "Mi " +0x5f26: "Xian " +0x5f27: "Hu " +0x5f28: "Chao " +0x5f29: "Nu " +0x5f2a: "Jing " +0x5f2b: "Zhen " +0x5f2c: "Yi " +0x5f2d: "Mi " +0x5f2e: "Quan " +0x5f2f: "Wan " +0x5f30: "Shao " +0x5f31: "Ruo " +0x5f32: "Xuan " +0x5f33: "Jing " +0x5f34: "Dun " +0x5f35: "Zhang " +0x5f36: "Jiang " +0x5f37: "Qiang " +0x5f38: "Peng " +0x5f39: "Dan " +0x5f3a: "Qiang " +0x5f3b: "Bi " +0x5f3c: "Bi " +0x5f3d: "She " +0x5f3e: "Dan " +0x5f3f: "Jian " +0x5f40: "Gou " +0x5f41: "Sei " +0x5f42: "Fa " +0x5f43: "Bi " +0x5f44: "Kou " +0x5f45: "Nagi " +0x5f46: "Bie " +0x5f47: "Xiao " +0x5f48: "Dan " +0x5f49: "Kuo " +0x5f4a: "Qiang " +0x5f4b: "Hong " +0x5f4c: "Mi " +0x5f4d: "Kuo " +0x5f4e: "Wan " +0x5f4f: "Jue " +0x5f50: "Ji " +0x5f51: "Ji " +0x5f52: "Gui " +0x5f53: "Dang " +0x5f54: "Lu " +0x5f55: "Lu " +0x5f56: "Tuan " +0x5f57: "Hui " +0x5f58: "Zhi " +0x5f59: "Hui " +0x5f5a: "Hui " +0x5f5b: "Yi " +0x5f5c: "Yi " +0x5f5d: "Yi " +0x5f5e: "Yi " +0x5f5f: "Huo " +0x5f60: "Huo " +0x5f61: "Shan " +0x5f62: "Xing " +0x5f63: "Wen " +0x5f64: "Tong " +0x5f65: "Yan " +0x5f66: "Yan " +0x5f67: "Yu " +0x5f68: "Chi " +0x5f69: "Cai " +0x5f6a: "Biao " +0x5f6b: "Diao " +0x5f6c: "Bin " +0x5f6d: "Peng " +0x5f6e: "Yong " +0x5f6f: "Piao " +0x5f70: "Zhang " +0x5f71: "Ying " +0x5f72: "Chi " +0x5f73: "Chi " +0x5f74: "Zhuo " +0x5f75: "Tuo " +0x5f76: "Ji " +0x5f77: "Pang " +0x5f78: "Zhong " +0x5f79: "Yi " +0x5f7a: "Wang " +0x5f7b: "Che " +0x5f7c: "Bi " +0x5f7d: "Chi " +0x5f7e: "Ling " +0x5f7f: "Fu " +0x5f80: "Wang " +0x5f81: "Zheng " +0x5f82: "Cu " +0x5f83: "Wang " +0x5f84: "Jing " +0x5f85: "Dai " +0x5f86: "Xi " +0x5f87: "Xun " +0x5f88: "Hen " +0x5f89: "Yang " +0x5f8a: "Huai " +0x5f8b: "Lu " +0x5f8c: "Hou " +0x5f8d: "Wa " +0x5f8e: "Cheng " +0x5f8f: "Zhi " +0x5f90: "Xu " +0x5f91: "Jing " +0x5f92: "Tu " +0x5f93: "Cong " +0x5f94: "[?] " +0x5f95: "Lai " +0x5f96: "Cong " +0x5f97: "De " +0x5f98: "Pai " +0x5f99: "Xi " +0x5f9a: "[?] " +0x5f9b: "Qi " +0x5f9c: "Chang " +0x5f9d: "Zhi " +0x5f9e: "Cong " +0x5f9f: "Zhou " +0x5fa0: "Lai " +0x5fa1: "Yu " +0x5fa2: "Xie " +0x5fa3: "Jie " +0x5fa4: "Jian " +0x5fa5: "Chi " +0x5fa6: "Jia " +0x5fa7: "Bian " +0x5fa8: "Huang " +0x5fa9: "Fu " +0x5faa: "Xun " +0x5fab: "Wei " +0x5fac: "Pang " +0x5fad: "Yao " +0x5fae: "Wei " +0x5faf: "Xi " +0x5fb0: "Zheng " +0x5fb1: "Piao " +0x5fb2: "Chi " +0x5fb3: "De " +0x5fb4: "Zheng " +0x5fb5: "Zheng " +0x5fb6: "Bie " +0x5fb7: "De " +0x5fb8: "Chong " +0x5fb9: "Che " +0x5fba: "Jiao " +0x5fbb: "Wei " +0x5fbc: "Jiao " +0x5fbd: "Hui " +0x5fbe: "Mei " +0x5fbf: "Long " +0x5fc0: "Xiang " +0x5fc1: "Bao " +0x5fc2: "Qu " +0x5fc3: "Xin " +0x5fc4: "Shu " +0x5fc5: "Bi " +0x5fc6: "Yi " +0x5fc7: "Le " +0x5fc8: "Ren " +0x5fc9: "Dao " +0x5fca: "Ding " +0x5fcb: "Gai " +0x5fcc: "Ji " +0x5fcd: "Ren " +0x5fce: "Ren " +0x5fcf: "Chan " +0x5fd0: "Tan " +0x5fd1: "Te " +0x5fd2: "Te " +0x5fd3: "Gan " +0x5fd4: "Qi " +0x5fd5: "Shi " +0x5fd6: "Cun " +0x5fd7: "Zhi " +0x5fd8: "Wang " +0x5fd9: "Mang " +0x5fda: "Xi " +0x5fdb: "Fan " +0x5fdc: "Ying " +0x5fdd: "Tian " +0x5fde: "Min " +0x5fdf: "Min " +0x5fe0: "Zhong " +0x5fe1: "Chong " +0x5fe2: "Wu " +0x5fe3: "Ji " +0x5fe4: "Wu " +0x5fe5: "Xi " +0x5fe6: "Ye " +0x5fe7: "You " +0x5fe8: "Wan " +0x5fe9: "Cong " +0x5fea: "Zhong " +0x5feb: "Kuai " +0x5fec: "Yu " +0x5fed: "Bian " +0x5fee: "Zhi " +0x5fef: "Qi " +0x5ff0: "Cui " +0x5ff1: "Chen " +0x5ff2: "Tai " +0x5ff3: "Tun " +0x5ff4: "Qian " +0x5ff5: "Nian " +0x5ff6: "Hun " +0x5ff7: "Xiong " +0x5ff8: "Niu " +0x5ff9: "Wang " +0x5ffa: "Xian " +0x5ffb: "Xin " +0x5ffc: "Kang " +0x5ffd: "Hu " +0x5ffe: "Kai " +0x5fff: "Fen " +/* x060 */ +0x6000: "Huai " +0x6001: "Tai " +0x6002: "Song " +0x6003: "Wu " +0x6004: "Ou " +0x6005: "Chang " +0x6006: "Chuang " +0x6007: "Ju " +0x6008: "Yi " +0x6009: "Bao " +0x600a: "Chao " +0x600b: "Min " +0x600c: "Pei " +0x600d: "Zuo " +0x600e: "Zen " +0x600f: "Yang " +0x6010: "Kou " +0x6011: "Ban " +0x6012: "Nu " +0x6013: "Nao " +0x6014: "Zheng " +0x6015: "Pa " +0x6016: "Bu " +0x6017: "Tie " +0x6018: "Gu " +0x6019: "Hu " +0x601a: "Ju " +0x601b: "Da " +0x601c: "Lian " +0x601d: "Si " +0x601e: "Chou " +0x601f: "Di " +0x6020: "Dai " +0x6021: "Yi " +0x6022: "Tu " +0x6023: "You " +0x6024: "Fu " +0x6025: "Ji " +0x6026: "Peng " +0x6027: "Xing " +0x6028: "Yuan " +0x6029: "Ni " +0x602a: "Guai " +0x602b: "Fu " +0x602c: "Xi " +0x602d: "Bi " +0x602e: "You " +0x602f: "Qie " +0x6030: "Xuan " +0x6031: "Cong " +0x6032: "Bing " +0x6033: "Huang " +0x6034: "Xu " +0x6035: "Chu " +0x6036: "Pi " +0x6037: "Xi " +0x6038: "Xi " +0x6039: "Tan " +0x603a: "Koraeru " +0x603b: "Zong " +0x603c: "Dui " +0x603d: "[?] " +0x603e: "Ki " +0x603f: "Yi " +0x6040: "Chi " +0x6041: "Ren " +0x6042: "Xun " +0x6043: "Shi " +0x6044: "Xi " +0x6045: "Lao " +0x6046: "Heng " +0x6047: "Kuang " +0x6048: "Mu " +0x6049: "Zhi " +0x604a: "Xie " +0x604b: "Lian " +0x604c: "Tiao " +0x604d: "Huang " +0x604e: "Die " +0x604f: "Hao " +0x6050: "Kong " +0x6051: "Gui " +0x6052: "Heng " +0x6053: "Xi " +0x6054: "Xiao " +0x6055: "Shu " +0x6056: "S " +0x6057: "Kua " +0x6058: "Qiu " +0x6059: "Yang " +0x605a: "Hui " +0x605b: "Hui " +0x605c: "Chi " +0x605d: "Jia " +0x605e: "Yi " +0x605f: "Xiong " +0x6060: "Guai " +0x6061: "Lin " +0x6062: "Hui " +0x6063: "Zi " +0x6064: "Xu " +0x6065: "Chi " +0x6066: "Xiang " +0x6067: "Nu " +0x6068: "Hen " +0x6069: "En " +0x606a: "Ke " +0x606b: "Tong " +0x606c: "Tian " +0x606d: "Gong " +0x606e: "Quan " +0x606f: "Xi " +0x6070: "Qia " +0x6071: "Yue " +0x6072: "Peng " +0x6073: "Ken " +0x6074: "De " +0x6075: "Hui " +0x6076: "E " +0x6077: "Kyuu " +0x6078: "Tong " +0x6079: "Yan " +0x607a: "Kai " +0x607b: "Ce " +0x607c: "Nao " +0x607d: "Yun " +0x607e: "Mang " +0x607f: "Yong " +0x6080: "Yong " +0x6081: "Yuan " +0x6082: "Pi " +0x6083: "Kun " +0x6084: "Qiao " +0x6085: "Yue " +0x6086: "Yu " +0x6087: "Yu " +0x6088: "Jie " +0x6089: "Xi " +0x608a: "Zhe " +0x608b: "Lin " +0x608c: "Ti " +0x608d: "Han " +0x608e: "Hao " +0x608f: "Qie " +0x6090: "Ti " +0x6091: "Bu " +0x6092: "Yi " +0x6093: "Qian " +0x6094: "Hui " +0x6095: "Xi " +0x6096: "Bei " +0x6097: "Man " +0x6098: "Yi " +0x6099: "Heng " +0x609a: "Song " +0x609b: "Quan " +0x609c: "Cheng " +0x609d: "Hui " +0x609e: "Wu " +0x609f: "Wu " +0x60a0: "You " +0x60a1: "Li " +0x60a2: "Liang " +0x60a3: "Huan " +0x60a4: "Cong " +0x60a5: "Yi " +0x60a6: "Yue " +0x60a7: "Li " +0x60a8: "Nin " +0x60a9: "Nao " +0x60aa: "E " +0x60ab: "Que " +0x60ac: "Xuan " +0x60ad: "Qian " +0x60ae: "Wu " +0x60af: "Min " +0x60b0: "Cong " +0x60b1: "Fei " +0x60b2: "Bei " +0x60b3: "Duo " +0x60b4: "Cui " +0x60b5: "Chang " +0x60b6: "Men " +0x60b7: "Li " +0x60b8: "Ji " +0x60b9: "Guan " +0x60ba: "Guan " +0x60bb: "Xing " +0x60bc: "Dao " +0x60bd: "Qi " +0x60be: "Kong " +0x60bf: "Tian " +0x60c0: "Lun " +0x60c1: "Xi " +0x60c2: "Kan " +0x60c3: "Kun " +0x60c4: "Ni " +0x60c5: "Qing " +0x60c6: "Chou " +0x60c7: "Dun " +0x60c8: "Guo " +0x60c9: "Chan " +0x60ca: "Liang " +0x60cb: "Wan " +0x60cc: "Yuan " +0x60cd: "Jin " +0x60ce: "Ji " +0x60cf: "Lin " +0x60d0: "Yu " +0x60d1: "Huo " +0x60d2: "He " +0x60d3: "Quan " +0x60d4: "Tan " +0x60d5: "Ti " +0x60d6: "Ti " +0x60d7: "Nie " +0x60d8: "Wang " +0x60d9: "Chuo " +0x60da: "Bu " +0x60db: "Hun " +0x60dc: "Xi " +0x60dd: "Tang " +0x60de: "Xin " +0x60df: "Wei " +0x60e0: "Hui " +0x60e1: "E " +0x60e2: "Rui " +0x60e3: "Zong " +0x60e4: "Jian " +0x60e5: "Yong " +0x60e6: "Dian " +0x60e7: "Ju " +0x60e8: "Can " +0x60e9: "Cheng " +0x60ea: "De " +0x60eb: "Bei " +0x60ec: "Qie " +0x60ed: "Can " +0x60ee: "Dan " +0x60ef: "Guan " +0x60f0: "Duo " +0x60f1: "Nao " +0x60f2: "Yun " +0x60f3: "Xiang " +0x60f4: "Zhui " +0x60f5: "Die " +0x60f6: "Huang " +0x60f7: "Chun " +0x60f8: "Qiong " +0x60f9: "Re " +0x60fa: "Xing " +0x60fb: "Ce " +0x60fc: "Bian " +0x60fd: "Hun " +0x60fe: "Zong " +0x60ff: "Ti " +/* x061 */ +0x6100: "Qiao " +0x6101: "Chou " +0x6102: "Bei " +0x6103: "Xuan " +0x6104: "Wei " +0x6105: "Ge " +0x6106: "Qian " +0x6107: "Wei " +0x6108: "Yu " +0x6109: "Yu " +0x610a: "Bi " +0x610b: "Xuan " +0x610c: "Huan " +0x610d: "Min " +0x610e: "Bi " +0x610f: "Yi " +0x6110: "Mian " +0x6111: "Yong " +0x6112: "Kai " +0x6113: "Dang " +0x6114: "Yin " +0x6115: "E " +0x6116: "Chen " +0x6117: "Mou " +0x6118: "Ke " +0x6119: "Ke " +0x611a: "Yu " +0x611b: "Ai " +0x611c: "Qie " +0x611d: "Yan " +0x611e: "Nuo " +0x611f: "Gan " +0x6120: "Yun " +0x6121: "Zong " +0x6122: "Sai " +0x6123: "Leng " +0x6124: "Fen " +0x6125: "[?] " +0x6126: "Kui " +0x6127: "Kui " +0x6128: "Que " +0x6129: "Gong " +0x612a: "Yun " +0x612b: "Su " +0x612c: "Su " +0x612d: "Qi " +0x612e: "Yao " +0x612f: "Song " +0x6130: "Huang " +0x6131: "Ji " +0x6132: "Gu " +0x6133: "Ju " +0x6134: "Chuang " +0x6135: "Ni " +0x6136: "Xie " +0x6137: "Kai " +0x6138: "Zheng " +0x6139: "Yong " +0x613a: "Cao " +0x613b: "Sun " +0x613c: "Shen " +0x613d: "Bo " +0x613e: "Kai " +0x613f: "Yuan " +0x6140: "Xie " +0x6141: "Hun " +0x6142: "Yong " +0x6143: "Yang " +0x6144: "Li " +0x6145: "Sao " +0x6146: "Tao " +0x6147: "Yin " +0x6148: "Ci " +0x6149: "Xu " +0x614a: "Qian " +0x614b: "Tai " +0x614c: "Huang " +0x614d: "Yun " +0x614e: "Shen " +0x614f: "Ming " +0x6150: "[?] " +0x6151: "She " +0x6152: "Cong " +0x6153: "Piao " +0x6154: "Mo " +0x6155: "Mu " +0x6156: "Guo " +0x6157: "Chi " +0x6158: "Can " +0x6159: "Can " +0x615a: "Can " +0x615b: "Cui " +0x615c: "Min " +0x615d: "Te " +0x615e: "Zhang " +0x615f: "Tong " +0x6160: "Ao " +0x6161: "Shuang " +0x6162: "Man " +0x6163: "Guan " +0x6164: "Que " +0x6165: "Zao " +0x6166: "Jiu " +0x6167: "Hui " +0x6168: "Kai " +0x6169: "Lian " +0x616a: "Ou " +0x616b: "Song " +0x616c: "Jin " +0x616d: "Yin " +0x616e: "Lu " +0x616f: "Shang " +0x6170: "Wei " +0x6171: "Tuan " +0x6172: "Man " +0x6173: "Qian " +0x6174: "She " +0x6175: "Yong " +0x6176: "Qing " +0x6177: "Kang " +0x6178: "Di " +0x6179: "Zhi " +0x617a: "Lou " +0x617b: "Juan " +0x617c: "Qi " +0x617d: "Qi " +0x617e: "Yu " +0x617f: "Ping " +0x6180: "Liao " +0x6181: "Cong " +0x6182: "You " +0x6183: "Chong " +0x6184: "Zhi " +0x6185: "Tong " +0x6186: "Cheng " +0x6187: "Qi " +0x6188: "Qu " +0x6189: "Peng " +0x618a: "Bei " +0x618b: "Bie " +0x618c: "Chun " +0x618d: "Jiao " +0x618e: "Zeng " +0x618f: "Chi " +0x6190: "Lian " +0x6191: "Ping " +0x6192: "Kui " +0x6193: "Hui " +0x6194: "Qiao " +0x6195: "Cheng " +0x6196: "Yin " +0x6197: "Yin " +0x6198: "Xi " +0x6199: "Xi " +0x619a: "Dan " +0x619b: "Tan " +0x619c: "Duo " +0x619d: "Dui " +0x619e: "Dui " +0x619f: "Su " +0x61a0: "Jue " +0x61a1: "Ce " +0x61a2: "Xiao " +0x61a3: "Fan " +0x61a4: "Fen " +0x61a5: "Lao " +0x61a6: "Lao " +0x61a7: "Chong " +0x61a8: "Han " +0x61a9: "Qi " +0x61aa: "Xian " +0x61ab: "Min " +0x61ac: "Jing " +0x61ad: "Liao " +0x61ae: "Wu " +0x61af: "Can " +0x61b0: "Jue " +0x61b1: "Cu " +0x61b2: "Xian " +0x61b3: "Tan " +0x61b4: "Sheng " +0x61b5: "Pi " +0x61b6: "Yi " +0x61b7: "Chu " +0x61b8: "Xian " +0x61b9: "Nao " +0x61ba: "Dan " +0x61bb: "Tan " +0x61bc: "Jing " +0x61bd: "Song " +0x61be: "Han " +0x61bf: "Jiao " +0x61c0: "Wai " +0x61c1: "Huan " +0x61c2: "Dong " +0x61c3: "Qin " +0x61c4: "Qin " +0x61c5: "Qu " +0x61c6: "Cao " +0x61c7: "Ken " +0x61c8: "Xie " +0x61c9: "Ying " +0x61ca: "Ao " +0x61cb: "Mao " +0x61cc: "Yi " +0x61cd: "Lin " +0x61ce: "Se " +0x61cf: "Jun " +0x61d0: "Huai " +0x61d1: "Men " +0x61d2: "Lan " +0x61d3: "Ai " +0x61d4: "Lin " +0x61d5: "Yan " +0x61d6: "Gua " +0x61d7: "Xia " +0x61d8: "Chi " +0x61d9: "Yu " +0x61da: "Yin " +0x61db: "Dai " +0x61dc: "Meng " +0x61dd: "Ai " +0x61de: "Meng " +0x61df: "Dui " +0x61e0: "Qi " +0x61e1: "Mo " +0x61e2: "Lan " +0x61e3: "Men " +0x61e4: "Chou " +0x61e5: "Zhi " +0x61e6: "Nuo " +0x61e7: "Nuo " +0x61e8: "Yan " +0x61e9: "Yang " +0x61ea: "Bo " +0x61eb: "Zhi " +0x61ec: "Kuang " +0x61ed: "Kuang " +0x61ee: "You " +0x61ef: "Fu " +0x61f0: "Liu " +0x61f1: "Mie " +0x61f2: "Cheng " +0x61f3: "[?] " +0x61f4: "Chan " +0x61f5: "Meng " +0x61f6: "Lan " +0x61f7: "Huai " +0x61f8: "Xuan " +0x61f9: "Rang " +0x61fa: "Chan " +0x61fb: "Ji " +0x61fc: "Ju " +0x61fd: "Huan " +0x61fe: "She " +0x61ff: "Yi " +/* x062 */ +0x6200: "Lian " +0x6201: "Nan " +0x6202: "Mi " +0x6203: "Tang " +0x6204: "Jue " +0x6205: "Gang " +0x6206: "Gang " +0x6207: "Gang " +0x6208: "Ge " +0x6209: "Yue " +0x620a: "Wu " +0x620b: "Jian " +0x620c: "Xu " +0x620d: "Shu " +0x620e: "Rong " +0x620f: "Xi " +0x6210: "Cheng " +0x6211: "Wo " +0x6212: "Jie " +0x6213: "Ge " +0x6214: "Jian " +0x6215: "Qiang " +0x6216: "Huo " +0x6217: "Qiang " +0x6218: "Zhan " +0x6219: "Dong " +0x621a: "Qi " +0x621b: "Jia " +0x621c: "Die " +0x621d: "Zei " +0x621e: "Jia " +0x621f: "Ji " +0x6220: "Shi " +0x6221: "Kan " +0x6222: "Ji " +0x6223: "Kui " +0x6224: "Gai " +0x6225: "Deng " +0x6226: "Zhan " +0x6227: "Chuang " +0x6228: "Ge " +0x6229: "Jian " +0x622a: "Jie " +0x622b: "Yu " +0x622c: "Jian " +0x622d: "Yan " +0x622e: "Lu " +0x622f: "Xi " +0x6230: "Zhan " +0x6231: "Xi " +0x6232: "Xi " +0x6233: "Chuo " +0x6234: "Dai " +0x6235: "Qu " +0x6236: "Hu " +0x6237: "Hu " +0x6238: "Hu " +0x6239: "E " +0x623a: "Shi " +0x623b: "Li " +0x623c: "Mao " +0x623d: "Hu " +0x623e: "Li " +0x623f: "Fang " +0x6240: "Suo " +0x6241: "Bian " +0x6242: "Dian " +0x6243: "Jiong " +0x6244: "Shang " +0x6245: "Yi " +0x6246: "Yi " +0x6247: "Shan " +0x6248: "Hu " +0x6249: "Fei " +0x624a: "Yan " +0x624b: "Shou " +0x624c: "T " +0x624d: "Cai " +0x624e: "Zha " +0x624f: "Qiu " +0x6250: "Le " +0x6251: "Bu " +0x6252: "Ba " +0x6253: "Da " +0x6254: "Reng " +0x6255: "Fu " +0x6256: "Hameru " +0x6257: "Zai " +0x6258: "Tuo " +0x6259: "Zhang " +0x625a: "Diao " +0x625b: "Kang " +0x625c: "Yu " +0x625d: "Ku " +0x625e: "Han " +0x625f: "Shen " +0x6260: "Cha " +0x6261: "Yi " +0x6262: "Gu " +0x6263: "Kou " +0x6264: "Wu " +0x6265: "Tuo " +0x6266: "Qian " +0x6267: "Zhi " +0x6268: "Ren " +0x6269: "Kuo " +0x626a: "Men " +0x626b: "Sao " +0x626c: "Yang " +0x626d: "Niu " +0x626e: "Ban " +0x626f: "Che " +0x6270: "Rao " +0x6271: "Xi " +0x6272: "Qian " +0x6273: "Ban " +0x6274: "Jia " +0x6275: "Yu " +0x6276: "Fu " +0x6277: "Ao " +0x6278: "Xi " +0x6279: "Pi " +0x627a: "Zhi " +0x627b: "Zi " +0x627c: "E " +0x627d: "Dun " +0x627e: "Zhao " +0x627f: "Cheng " +0x6280: "Ji " +0x6281: "Yan " +0x6282: "Kuang " +0x6283: "Bian " +0x6284: "Chao " +0x6285: "Ju " +0x6286: "Wen " +0x6287: "Hu " +0x6288: "Yue " +0x6289: "Jue " +0x628a: "Ba " +0x628b: "Qin " +0x628c: "Zhen " +0x628d: "Zheng " +0x628e: "Yun " +0x628f: "Wan " +0x6290: "Nu " +0x6291: "Yi " +0x6292: "Shu " +0x6293: "Zhua " +0x6294: "Pou " +0x6295: "Tou " +0x6296: "Dou " +0x6297: "Kang " +0x6298: "Zhe " +0x6299: "Pou " +0x629a: "Fu " +0x629b: "Pao " +0x629c: "Ba " +0x629d: "Ao " +0x629e: "Ze " +0x629f: "Tuan " +0x62a0: "Kou " +0x62a1: "Lun " +0x62a2: "Qiang " +0x62a3: "[?] " +0x62a4: "Hu " +0x62a5: "Bao " +0x62a6: "Bing " +0x62a7: "Zhi " +0x62a8: "Peng " +0x62a9: "Tan " +0x62aa: "Pu " +0x62ab: "Pi " +0x62ac: "Tai " +0x62ad: "Yao " +0x62ae: "Zhen " +0x62af: "Zha " +0x62b0: "Yang " +0x62b1: "Bao " +0x62b2: "He " +0x62b3: "Ni " +0x62b4: "Yi " +0x62b5: "Di " +0x62b6: "Chi " +0x62b7: "Pi " +0x62b8: "Za " +0x62b9: "Mo " +0x62ba: "Mo " +0x62bb: "Shen " +0x62bc: "Ya " +0x62bd: "Chou " +0x62be: "Qu " +0x62bf: "Min " +0x62c0: "Chu " +0x62c1: "Jia " +0x62c2: "Fu " +0x62c3: "Zhan " +0x62c4: "Zhu " +0x62c5: "Dan " +0x62c6: "Chai " +0x62c7: "Mu " +0x62c8: "Nian " +0x62c9: "La " +0x62ca: "Fu " +0x62cb: "Pao " +0x62cc: "Ban " +0x62cd: "Pai " +0x62ce: "Ling " +0x62cf: "Na " +0x62d0: "Guai " +0x62d1: "Qian " +0x62d2: "Ju " +0x62d3: "Tuo " +0x62d4: "Ba " +0x62d5: "Tuo " +0x62d6: "Tuo " +0x62d7: "Ao " +0x62d8: "Ju " +0x62d9: "Zhuo " +0x62da: "Pan " +0x62db: "Zhao " +0x62dc: "Bai " +0x62dd: "Bai " +0x62de: "Di " +0x62df: "Ni " +0x62e0: "Ju " +0x62e1: "Kuo " +0x62e2: "Long " +0x62e3: "Jian " +0x62e4: "[?] " +0x62e5: "Yong " +0x62e6: "Lan " +0x62e7: "Ning " +0x62e8: "Bo " +0x62e9: "Ze " +0x62ea: "Qian " +0x62eb: "Hen " +0x62ec: "Gua " +0x62ed: "Shi " +0x62ee: "Jie " +0x62ef: "Zheng " +0x62f0: "Nin " +0x62f1: "Gong " +0x62f2: "Gong " +0x62f3: "Quan " +0x62f4: "Shuan " +0x62f5: "Cun " +0x62f6: "Zan " +0x62f7: "Kao " +0x62f8: "Chi " +0x62f9: "Xie " +0x62fa: "Ce " +0x62fb: "Hui " +0x62fc: "Pin " +0x62fd: "Zhuai " +0x62fe: "Shi " +0x62ff: "Na " +/* x063 */ +0x6300: "Bo " +0x6301: "Chi " +0x6302: "Gua " +0x6303: "Zhi " +0x6304: "Kuo " +0x6305: "Duo " +0x6306: "Duo " +0x6307: "Zhi " +0x6308: "Qie " +0x6309: "An " +0x630a: "Nong " +0x630b: "Zhen " +0x630c: "Ge " +0x630d: "Jiao " +0x630e: "Ku " +0x630f: "Dong " +0x6310: "Ru " +0x6311: "Tiao " +0x6312: "Lie " +0x6313: "Zha " +0x6314: "Lu " +0x6315: "Die " +0x6316: "Wa " +0x6317: "Jue " +0x6318: "Mushiru " +0x6319: "Ju " +0x631a: "Zhi " +0x631b: "Luan " +0x631c: "Ya " +0x631d: "Zhua " +0x631e: "Ta " +0x631f: "Xie " +0x6320: "Nao " +0x6321: "Dang " +0x6322: "Jiao " +0x6323: "Zheng " +0x6324: "Ji " +0x6325: "Hui " +0x6326: "Xun " +0x6327: "Ku " +0x6328: "Ai " +0x6329: "Tuo " +0x632a: "Nuo " +0x632b: "Cuo " +0x632c: "Bo " +0x632d: "Geng " +0x632e: "Ti " +0x632f: "Zhen " +0x6330: "Cheng " +0x6331: "Suo " +0x6332: "Suo " +0x6333: "Keng " +0x6334: "Mei " +0x6335: "Long " +0x6336: "Ju " +0x6337: "Peng " +0x6338: "Jian " +0x6339: "Yi " +0x633a: "Ting " +0x633b: "Shan " +0x633c: "Nuo " +0x633d: "Wan " +0x633e: "Xie " +0x633f: "Cha " +0x6340: "Feng " +0x6341: "Jiao " +0x6342: "Wu " +0x6343: "Jun " +0x6344: "Jiu " +0x6345: "Tong " +0x6346: "Kun " +0x6347: "Huo " +0x6348: "Tu " +0x6349: "Zhuo " +0x634a: "Pou " +0x634b: "Le " +0x634c: "Ba " +0x634d: "Han " +0x634e: "Shao " +0x634f: "Nie " +0x6350: "Juan " +0x6351: "Ze " +0x6352: "Song " +0x6353: "Ye " +0x6354: "Jue " +0x6355: "Bu " +0x6356: "Huan " +0x6357: "Bu " +0x6358: "Zun " +0x6359: "Yi " +0x635a: "Zhai " +0x635b: "Lu " +0x635c: "Sou " +0x635d: "Tuo " +0x635e: "Lao " +0x635f: "Sun " +0x6360: "Bang " +0x6361: "Jian " +0x6362: "Huan " +0x6363: "Dao " +0x6364: "[?] " +0x6365: "Wan " +0x6366: "Qin " +0x6367: "Peng " +0x6368: "She " +0x6369: "Lie " +0x636a: "Min " +0x636b: "Men " +0x636c: "Fu " +0x636d: "Bai " +0x636e: "Ju " +0x636f: "Dao " +0x6370: "Wo " +0x6371: "Ai " +0x6372: "Juan " +0x6373: "Yue " +0x6374: "Zong " +0x6375: "Chen " +0x6376: "Chui " +0x6377: "Jie " +0x6378: "Tu " +0x6379: "Ben " +0x637a: "Na " +0x637b: "Nian " +0x637c: "Nuo " +0x637d: "Zu " +0x637e: "Wo " +0x637f: "Xi " +0x6380: "Xian " +0x6381: "Cheng " +0x6382: "Dian " +0x6383: "Sao " +0x6384: "Lun " +0x6385: "Qing " +0x6386: "Gang " +0x6387: "Duo " +0x6388: "Shou " +0x6389: "Diao " +0x638a: "Pou " +0x638b: "Di " +0x638c: "Zhang " +0x638d: "Gun " +0x638e: "Ji " +0x638f: "Tao " +0x6390: "Qia " +0x6391: "Qi " +0x6392: "Pai " +0x6393: "Shu " +0x6394: "Qian " +0x6395: "Ling " +0x6396: "Yi " +0x6397: "Ya " +0x6398: "Jue " +0x6399: "Zheng " +0x639a: "Liang " +0x639b: "Gua " +0x639c: "Yi " +0x639d: "Huo " +0x639e: "Shan " +0x639f: "Zheng " +0x63a0: "Lue " +0x63a1: "Cai " +0x63a2: "Tan " +0x63a3: "Che " +0x63a4: "Bing " +0x63a5: "Jie " +0x63a6: "Ti " +0x63a7: "Kong " +0x63a8: "Tui " +0x63a9: "Yan " +0x63aa: "Cuo " +0x63ab: "Zou " +0x63ac: "Ju " +0x63ad: "Tian " +0x63ae: "Qian " +0x63af: "Ken " +0x63b0: "Bai " +0x63b1: "Shou " +0x63b2: "Jie " +0x63b3: "Lu " +0x63b4: "Guo " +0x63b5: "Haba " +0x63b6: "[?] " +0x63b7: "Zhi " +0x63b8: "Dan " +0x63b9: "Mang " +0x63ba: "Xian " +0x63bb: "Sao " +0x63bc: "Guan " +0x63bd: "Peng " +0x63be: "Yuan " +0x63bf: "Nuo " +0x63c0: "Jian " +0x63c1: "Zhen " +0x63c2: "Jiu " +0x63c3: "Jian " +0x63c4: "Yu " +0x63c5: "Yan " +0x63c6: "Kui " +0x63c7: "Nan " +0x63c8: "Hong " +0x63c9: "Rou " +0x63ca: "Pi " +0x63cb: "Wei " +0x63cc: "Sai " +0x63cd: "Zou " +0x63ce: "Xuan " +0x63cf: "Miao " +0x63d0: "Ti " +0x63d1: "Nie " +0x63d2: "Cha " +0x63d3: "Shi " +0x63d4: "Zong " +0x63d5: "Zhen " +0x63d6: "Yi " +0x63d7: "Shun " +0x63d8: "Heng " +0x63d9: "Bian " +0x63da: "Yang " +0x63db: "Huan " +0x63dc: "Yan " +0x63dd: "Zuan " +0x63de: "An " +0x63df: "Xu " +0x63e0: "Ya " +0x63e1: "Wo " +0x63e2: "Ke " +0x63e3: "Chuai " +0x63e4: "Ji " +0x63e5: "Ti " +0x63e6: "La " +0x63e7: "La " +0x63e8: "Cheng " +0x63e9: "Kai " +0x63ea: "Jiu " +0x63eb: "Jiu " +0x63ec: "Tu " +0x63ed: "Jie " +0x63ee: "Hui " +0x63ef: "Geng " +0x63f0: "Chong " +0x63f1: "Shuo " +0x63f2: "She " +0x63f3: "Xie " +0x63f4: "Yuan " +0x63f5: "Qian " +0x63f6: "Ye " +0x63f7: "Cha " +0x63f8: "Zha " +0x63f9: "Bei " +0x63fa: "Yao " +0x63fb: "[?] " +0x63fc: "[?] " +0x63fd: "Lan " +0x63fe: "Wen " +0x63ff: "Qin " +/* x064 */ +0x6400: "Chan " +0x6401: "Ge " +0x6402: "Lou " +0x6403: "Zong " +0x6404: "Geng " +0x6405: "Jiao " +0x6406: "Gou " +0x6407: "Qin " +0x6408: "Yong " +0x6409: "Que " +0x640a: "Chou " +0x640b: "Chi " +0x640c: "Zhan " +0x640d: "Sun " +0x640e: "Sun " +0x640f: "Bo " +0x6410: "Chu " +0x6411: "Rong " +0x6412: "Beng " +0x6413: "Cuo " +0x6414: "Sao " +0x6415: "Ke " +0x6416: "Yao " +0x6417: "Dao " +0x6418: "Zhi " +0x6419: "Nu " +0x641a: "Xie " +0x641b: "Jian " +0x641c: "Sou " +0x641d: "Qiu " +0x641e: "Gao " +0x641f: "Xian " +0x6420: "Shuo " +0x6421: "Sang " +0x6422: "Jin " +0x6423: "Mie " +0x6424: "E " +0x6425: "Chui " +0x6426: "Nuo " +0x6427: "Shan " +0x6428: "Ta " +0x6429: "Jie " +0x642a: "Tang " +0x642b: "Pan " +0x642c: "Ban " +0x642d: "Da " +0x642e: "Li " +0x642f: "Tao " +0x6430: "Hu " +0x6431: "Zhi " +0x6432: "Wa " +0x6433: "Xia " +0x6434: "Qian " +0x6435: "Wen " +0x6436: "Qiang " +0x6437: "Tian " +0x6438: "Zhen " +0x6439: "E " +0x643a: "Xi " +0x643b: "Nuo " +0x643c: "Quan " +0x643d: "Cha " +0x643e: "Zha " +0x643f: "Ge " +0x6440: "Wu " +0x6441: "En " +0x6442: "She " +0x6443: "Kang " +0x6444: "She " +0x6445: "Shu " +0x6446: "Bai " +0x6447: "Yao " +0x6448: "Bin " +0x6449: "Sou " +0x644a: "Tan " +0x644b: "Sa " +0x644c: "Chan " +0x644d: "Suo " +0x644e: "Liao " +0x644f: "Chong " +0x6450: "Chuang " +0x6451: "Guo " +0x6452: "Bing " +0x6453: "Feng " +0x6454: "Shuai " +0x6455: "Di " +0x6456: "Qi " +0x6457: "Sou " +0x6458: "Zhai " +0x6459: "Lian " +0x645a: "Tang " +0x645b: "Chi " +0x645c: "Guan " +0x645d: "Lu " +0x645e: "Luo " +0x645f: "Lou " +0x6460: "Zong " +0x6461: "Gai " +0x6462: "Hu " +0x6463: "Zha " +0x6464: "Chuang " +0x6465: "Tang " +0x6466: "Hua " +0x6467: "Cui " +0x6468: "Nai " +0x6469: "Mo " +0x646a: "Jiang " +0x646b: "Gui " +0x646c: "Ying " +0x646d: "Zhi " +0x646e: "Ao " +0x646f: "Zhi " +0x6470: "Nie " +0x6471: "Man " +0x6472: "Shan " +0x6473: "Kou " +0x6474: "Shu " +0x6475: "Suo " +0x6476: "Tuan " +0x6477: "Jiao " +0x6478: "Mo " +0x6479: "Mo " +0x647a: "Zhe " +0x647b: "Xian " +0x647c: "Keng " +0x647d: "Piao " +0x647e: "Jiang " +0x647f: "Yin " +0x6480: "Gou " +0x6481: "Qian " +0x6482: "Lue " +0x6483: "Ji " +0x6484: "Ying " +0x6485: "Jue " +0x6486: "Pie " +0x6487: "Pie " +0x6488: "Lao " +0x6489: "Dun " +0x648a: "Xian " +0x648b: "Ruan " +0x648c: "Kui " +0x648d: "Zan " +0x648e: "Yi " +0x648f: "Xun " +0x6490: "Cheng " +0x6491: "Cheng " +0x6492: "Sa " +0x6493: "Nao " +0x6494: "Heng " +0x6495: "Si " +0x6496: "Qian " +0x6497: "Huang " +0x6498: "Da " +0x6499: "Zun " +0x649a: "Nian " +0x649b: "Lin " +0x649c: "Zheng " +0x649d: "Hui " +0x649e: "Zhuang " +0x649f: "Jiao " +0x64a0: "Ji " +0x64a1: "Cao " +0x64a2: "Dan " +0x64a3: "Dan " +0x64a4: "Che " +0x64a5: "Bo " +0x64a6: "Che " +0x64a7: "Jue " +0x64a8: "Xiao " +0x64a9: "Liao " +0x64aa: "Ben " +0x64ab: "Fu " +0x64ac: "Qiao " +0x64ad: "Bo " +0x64ae: "Cuo " +0x64af: "Zhuo " +0x64b0: "Zhuan " +0x64b1: "Tuo " +0x64b2: "Pu " +0x64b3: "Qin " +0x64b4: "Dun " +0x64b5: "Nian " +0x64b6: "[?] " +0x64b7: "Xie " +0x64b8: "Lu " +0x64b9: "Jiao " +0x64ba: "Cuan " +0x64bb: "Ta " +0x64bc: "Han " +0x64bd: "Qiao " +0x64be: "Zhua " +0x64bf: "Jian " +0x64c0: "Gan " +0x64c1: "Yong " +0x64c2: "Lei " +0x64c3: "Kuo " +0x64c4: "Lu " +0x64c5: "Shan " +0x64c6: "Zhuo " +0x64c7: "Ze " +0x64c8: "Pu " +0x64c9: "Chuo " +0x64ca: "Ji " +0x64cb: "Dang " +0x64cc: "Suo " +0x64cd: "Cao " +0x64ce: "Qing " +0x64cf: "Jing " +0x64d0: "Huan " +0x64d1: "Jie " +0x64d2: "Qin " +0x64d3: "Kuai " +0x64d4: "Dan " +0x64d5: "Xi " +0x64d6: "Ge " +0x64d7: "Pi " +0x64d8: "Bo " +0x64d9: "Ao " +0x64da: "Ju " +0x64db: "Ye " +0x64dc: "[?] " +0x64dd: "Mang " +0x64de: "Sou " +0x64df: "Mi " +0x64e0: "Ji " +0x64e1: "Tai " +0x64e2: "Zhuo " +0x64e3: "Dao " +0x64e4: "Xing " +0x64e5: "Lan " +0x64e6: "Ca " +0x64e7: "Ju " +0x64e8: "Ye " +0x64e9: "Ru " +0x64ea: "Ye " +0x64eb: "Ye " +0x64ec: "Ni " +0x64ed: "Hu " +0x64ee: "Ji " +0x64ef: "Bin " +0x64f0: "Ning " +0x64f1: "Ge " +0x64f2: "Zhi " +0x64f3: "Jie " +0x64f4: "Kuo " +0x64f5: "Mo " +0x64f6: "Jian " +0x64f7: "Xie " +0x64f8: "Lie " +0x64f9: "Tan " +0x64fa: "Bai " +0x64fb: "Sou " +0x64fc: "Lu " +0x64fd: "Lue " +0x64fe: "Rao " +0x64ff: "Zhi " +/* x065 */ +0x6500: "Pan " +0x6501: "Yang " +0x6502: "Lei " +0x6503: "Sa " +0x6504: "Shu " +0x6505: "Zan " +0x6506: "Nian " +0x6507: "Xian " +0x6508: "Jun " +0x6509: "Huo " +0x650a: "Li " +0x650b: "La " +0x650c: "Han " +0x650d: "Ying " +0x650e: "Lu " +0x650f: "Long " +0x6510: "Qian " +0x6511: "Qian " +0x6512: "Zan " +0x6513: "Qian " +0x6514: "Lan " +0x6515: "San " +0x6516: "Ying " +0x6517: "Mei " +0x6518: "Rang " +0x6519: "Chan " +0x651a: "[?] " +0x651b: "Cuan " +0x651c: "Xi " +0x651d: "She " +0x651e: "Luo " +0x651f: "Jun " +0x6520: "Mi " +0x6521: "Li " +0x6522: "Zan " +0x6523: "Luan " +0x6524: "Tan " +0x6525: "Zuan " +0x6526: "Li " +0x6527: "Dian " +0x6528: "Wa " +0x6529: "Dang " +0x652a: "Jiao " +0x652b: "Jue " +0x652c: "Lan " +0x652d: "Li " +0x652e: "Nang " +0x652f: "Zhi " +0x6530: "Gui " +0x6531: "Gui " +0x6532: "Qi " +0x6533: "Xin " +0x6534: "Pu " +0x6535: "Sui " +0x6536: "Shou " +0x6537: "Kao " +0x6538: "You " +0x6539: "Gai " +0x653a: "Yi " +0x653b: "Gong " +0x653c: "Gan " +0x653d: "Ban " +0x653e: "Fang " +0x653f: "Zheng " +0x6540: "Bo " +0x6541: "Dian " +0x6542: "Kou " +0x6543: "Min " +0x6544: "Wu " +0x6545: "Gu " +0x6546: "He " +0x6547: "Ce " +0x6548: "Xiao " +0x6549: "Mi " +0x654a: "Chu " +0x654b: "Ge " +0x654c: "Di " +0x654d: "Xu " +0x654e: "Jiao " +0x654f: "Min " +0x6550: "Chen " +0x6551: "Jiu " +0x6552: "Zhen " +0x6553: "Duo " +0x6554: "Yu " +0x6555: "Chi " +0x6556: "Ao " +0x6557: "Bai " +0x6558: "Xu " +0x6559: "Jiao " +0x655a: "Duo " +0x655b: "Lian " +0x655c: "Nie " +0x655d: "Bi " +0x655e: "Chang " +0x655f: "Dian " +0x6560: "Duo " +0x6561: "Yi " +0x6562: "Gan " +0x6563: "San " +0x6564: "Ke " +0x6565: "Yan " +0x6566: "Dun " +0x6567: "Qi " +0x6568: "Dou " +0x6569: "Xiao " +0x656a: "Duo " +0x656b: "Jiao " +0x656c: "Jing " +0x656d: "Yang " +0x656e: "Xia " +0x656f: "Min " +0x6570: "Shu " +0x6571: "Ai " +0x6572: "Qiao " +0x6573: "Ai " +0x6574: "Zheng " +0x6575: "Di " +0x6576: "Zhen " +0x6577: "Fu " +0x6578: "Shu " +0x6579: "Liao " +0x657a: "Qu " +0x657b: "Xiong " +0x657c: "Xi " +0x657d: "Jiao " +0x657e: "Sen " +0x657f: "Jiao " +0x6580: "Zhuo " +0x6581: "Yi " +0x6582: "Lian " +0x6583: "Bi " +0x6584: "Li " +0x6585: "Xiao " +0x6586: "Xiao " +0x6587: "Wen " +0x6588: "Xue " +0x6589: "Qi " +0x658a: "Qi " +0x658b: "Zhai " +0x658c: "Bin " +0x658d: "Jue " +0x658e: "Zhai " +0x658f: "[?] " +0x6590: "Fei " +0x6591: "Ban " +0x6592: "Ban " +0x6593: "Lan " +0x6594: "Yu " +0x6595: "Lan " +0x6596: "Wei " +0x6597: "Dou " +0x6598: "Sheng " +0x6599: "Liao " +0x659a: "Jia " +0x659b: "Hu " +0x659c: "Xie " +0x659d: "Jia " +0x659e: "Yu " +0x659f: "Zhen " +0x65a0: "Jiao " +0x65a1: "Wo " +0x65a2: "Tou " +0x65a3: "Chu " +0x65a4: "Jin " +0x65a5: "Chi " +0x65a6: "Yin " +0x65a7: "Fu " +0x65a8: "Qiang " +0x65a9: "Zhan " +0x65aa: "Qu " +0x65ab: "Zhuo " +0x65ac: "Zhan " +0x65ad: "Duan " +0x65ae: "Zhuo " +0x65af: "Si " +0x65b0: "Xin " +0x65b1: "Zhuo " +0x65b2: "Zhuo " +0x65b3: "Qin " +0x65b4: "Lin " +0x65b5: "Zhuo " +0x65b6: "Chu " +0x65b7: "Duan " +0x65b8: "Zhu " +0x65b9: "Fang " +0x65ba: "Xie " +0x65bb: "Hang " +0x65bc: "Yu " +0x65bd: "Shi " +0x65be: "Pei " +0x65bf: "You " +0x65c0: "Mye " +0x65c1: "Pang " +0x65c2: "Qi " +0x65c3: "Zhan " +0x65c4: "Mao " +0x65c5: "Lu " +0x65c6: "Pei " +0x65c7: "Pi " +0x65c8: "Liu " +0x65c9: "Fu " +0x65ca: "Fang " +0x65cb: "Xuan " +0x65cc: "Jing " +0x65cd: "Jing " +0x65ce: "Ni " +0x65cf: "Zu " +0x65d0: "Zhao " +0x65d1: "Yi " +0x65d2: "Liu " +0x65d3: "Shao " +0x65d4: "Jian " +0x65d5: "Es " +0x65d6: "Yi " +0x65d7: "Qi " +0x65d8: "Zhi " +0x65d9: "Fan " +0x65da: "Piao " +0x65db: "Fan " +0x65dc: "Zhan " +0x65dd: "Guai " +0x65de: "Sui " +0x65df: "Yu " +0x65e0: "Wu " +0x65e1: "Ji " +0x65e2: "Ji " +0x65e3: "Ji " +0x65e4: "Huo " +0x65e5: "Ri " +0x65e6: "Dan " +0x65e7: "Jiu " +0x65e8: "Zhi " +0x65e9: "Zao " +0x65ea: "Xie " +0x65eb: "Tiao " +0x65ec: "Xun " +0x65ed: "Xu " +0x65ee: "Xu " +0x65ef: "Xu " +0x65f0: "Gan " +0x65f1: "Han " +0x65f2: "Tai " +0x65f3: "Di " +0x65f4: "Xu " +0x65f5: "Chan " +0x65f6: "Shi " +0x65f7: "Kuang " +0x65f8: "Yang " +0x65f9: "Shi " +0x65fa: "Wang " +0x65fb: "Min " +0x65fc: "Min " +0x65fd: "Tun " +0x65fe: "Chun " +0x65ff: "Wu " +/* x066 */ +0x6600: "Yun " +0x6601: "Bei " +0x6602: "Ang " +0x6603: "Ze " +0x6604: "Ban " +0x6605: "Jie " +0x6606: "Kun " +0x6607: "Sheng " +0x6608: "Hu " +0x6609: "Fang " +0x660a: "Hao " +0x660b: "Gui " +0x660c: "Chang " +0x660d: "Xuan " +0x660e: "Ming " +0x660f: "Hun " +0x6610: "Fen " +0x6611: "Qin " +0x6612: "Hu " +0x6613: "Yi " +0x6614: "Xi " +0x6615: "Xin " +0x6616: "Yan " +0x6617: "Ze " +0x6618: "Fang " +0x6619: "Tan " +0x661a: "Shen " +0x661b: "Ju " +0x661c: "Yang " +0x661d: "Zan " +0x661e: "Bing " +0x661f: "Xing " +0x6620: "Ying " +0x6621: "Xuan " +0x6622: "Pei " +0x6623: "Zhen " +0x6624: "Ling " +0x6625: "Chun " +0x6626: "Hao " +0x6627: "Mei " +0x6628: "Zuo " +0x6629: "Mo " +0x662a: "Bian " +0x662b: "Xu " +0x662c: "Hun " +0x662d: "Zhao " +0x662e: "Zong " +0x662f: "Shi " +0x6630: "Shi " +0x6631: "Yu " +0x6632: "Fei " +0x6633: "Die " +0x6634: "Mao " +0x6635: "Ni " +0x6636: "Chang " +0x6637: "Wen " +0x6638: "Dong " +0x6639: "Ai " +0x663a: "Bing " +0x663b: "Ang " +0x663c: "Zhou " +0x663d: "Long " +0x663e: "Xian " +0x663f: "Kuang " +0x6640: "Tiao " +0x6641: "Chao " +0x6642: "Shi " +0x6643: "Huang " +0x6644: "Huang " +0x6645: "Xuan " +0x6646: "Kui " +0x6647: "Xu " +0x6648: "Jiao " +0x6649: "Jin " +0x664a: "Zhi " +0x664b: "Jin " +0x664c: "Shang " +0x664d: "Tong " +0x664e: "Hong " +0x664f: "Yan " +0x6650: "Gai " +0x6651: "Xiang " +0x6652: "Shai " +0x6653: "Xiao " +0x6654: "Ye " +0x6655: "Yun " +0x6656: "Hui " +0x6657: "Han " +0x6658: "Han " +0x6659: "Jun " +0x665a: "Wan " +0x665b: "Xian " +0x665c: "Kun " +0x665d: "Zhou " +0x665e: "Xi " +0x665f: "Cheng " +0x6660: "Sheng " +0x6661: "Bu " +0x6662: "Zhe " +0x6663: "Zhe " +0x6664: "Wu " +0x6665: "Han " +0x6666: "Hui " +0x6667: "Hao " +0x6668: "Chen " +0x6669: "Wan " +0x666a: "Tian " +0x666b: "Zhuo " +0x666c: "Zui " +0x666d: "Zhou " +0x666e: "Pu " +0x666f: "Jing " +0x6670: "Xi " +0x6671: "Shan " +0x6672: "Yi " +0x6673: "Xi " +0x6674: "Qing " +0x6675: "Qi " +0x6676: "Jing " +0x6677: "Gui " +0x6678: "Zhen " +0x6679: "Yi " +0x667a: "Zhi " +0x667b: "An " +0x667c: "Wan " +0x667d: "Lin " +0x667e: "Liang " +0x667f: "Chang " +0x6680: "Wang " +0x6681: "Xiao " +0x6682: "Zan " +0x6683: "Hi " +0x6684: "Xuan " +0x6685: "Xuan " +0x6686: "Yi " +0x6687: "Xia " +0x6688: "Yun " +0x6689: "Hui " +0x668a: "Fu " +0x668b: "Min " +0x668c: "Kui " +0x668d: "He " +0x668e: "Ying " +0x668f: "Du " +0x6690: "Wei " +0x6691: "Shu " +0x6692: "Qing " +0x6693: "Mao " +0x6694: "Nan " +0x6695: "Jian " +0x6696: "Nuan " +0x6697: "An " +0x6698: "Yang " +0x6699: "Chun " +0x669a: "Yao " +0x669b: "Suo " +0x669c: "Jin " +0x669d: "Ming " +0x669e: "Jiao " +0x669f: "Kai " +0x66a0: "Gao " +0x66a1: "Weng " +0x66a2: "Chang " +0x66a3: "Qi " +0x66a4: "Hao " +0x66a5: "Yan " +0x66a6: "Li " +0x66a7: "Ai " +0x66a8: "Ji " +0x66a9: "Gui " +0x66aa: "Men " +0x66ab: "Zan " +0x66ac: "Xie " +0x66ad: "Hao " +0x66ae: "Mu " +0x66af: "Mo " +0x66b0: "Cong " +0x66b1: "Ni " +0x66b2: "Zhang " +0x66b3: "Hui " +0x66b4: "Bao " +0x66b5: "Han " +0x66b6: "Xuan " +0x66b7: "Chuan " +0x66b8: "Liao " +0x66b9: "Xian " +0x66ba: "Dan " +0x66bb: "Jing " +0x66bc: "Pie " +0x66bd: "Lin " +0x66be: "Tun " +0x66bf: "Xi " +0x66c0: "Yi " +0x66c1: "Ji " +0x66c2: "Huang " +0x66c3: "Tai " +0x66c4: "Ye " +0x66c5: "Ye " +0x66c6: "Li " +0x66c7: "Tan " +0x66c8: "Tong " +0x66c9: "Xiao " +0x66ca: "Fei " +0x66cb: "Qin " +0x66cc: "Zhao " +0x66cd: "Hao " +0x66ce: "Yi " +0x66cf: "Xiang " +0x66d0: "Xing " +0x66d1: "Sen " +0x66d2: "Jiao " +0x66d3: "Bao " +0x66d4: "Jing " +0x66d5: "Yian " +0x66d6: "Ai " +0x66d7: "Ye " +0x66d8: "Ru " +0x66d9: "Shu " +0x66da: "Meng " +0x66db: "Xun " +0x66dc: "Yao " +0x66dd: "Pu " +0x66de: "Li " +0x66df: "Chen " +0x66e0: "Kuang " +0x66e1: "Die " +0x66e2: "[?] " +0x66e3: "Yan " +0x66e4: "Huo " +0x66e5: "Lu " +0x66e6: "Xi " +0x66e7: "Rong " +0x66e8: "Long " +0x66e9: "Nang " +0x66ea: "Luo " +0x66eb: "Luan " +0x66ec: "Shai " +0x66ed: "Tang " +0x66ee: "Yan " +0x66ef: "Chu " +0x66f0: "Yue " +0x66f1: "Yue " +0x66f2: "Qu " +0x66f3: "Yi " +0x66f4: "Geng " +0x66f5: "Ye " +0x66f6: "Hu " +0x66f7: "He " +0x66f8: "Shu " +0x66f9: "Cao " +0x66fa: "Cao " +0x66fb: "Noboru " +0x66fc: "Man " +0x66fd: "Ceng " +0x66fe: "Ceng " +0x66ff: "Ti " +/* x067 */ +0x6700: "Zui " +0x6701: "Can " +0x6702: "Xu " +0x6703: "Hui " +0x6704: "Yin " +0x6705: "Qie " +0x6706: "Fen " +0x6707: "Pi " +0x6708: "Yue " +0x6709: "You " +0x670a: "Ruan " +0x670b: "Peng " +0x670c: "Ban " +0x670d: "Fu " +0x670e: "Ling " +0x670f: "Fei " +0x6710: "Qu " +0x6711: "[?] " +0x6712: "Nu " +0x6713: "Tiao " +0x6714: "Shuo " +0x6715: "Zhen " +0x6716: "Lang " +0x6717: "Lang " +0x6718: "Juan " +0x6719: "Ming " +0x671a: "Huang " +0x671b: "Wang " +0x671c: "Tun " +0x671d: "Zhao " +0x671e: "Ji " +0x671f: "Qi " +0x6720: "Ying " +0x6721: "Zong " +0x6722: "Wang " +0x6723: "Tong " +0x6724: "Lang " +0x6725: "[?] " +0x6726: "Meng " +0x6727: "Long " +0x6728: "Mu " +0x6729: "Deng " +0x672a: "Wei " +0x672b: "Mo " +0x672c: "Ben " +0x672d: "Zha " +0x672e: "Zhu " +0x672f: "Zhu " +0x6730: "[?] " +0x6731: "Zhu " +0x6732: "Ren " +0x6733: "Ba " +0x6734: "Po " +0x6735: "Duo " +0x6736: "Duo " +0x6737: "Dao " +0x6738: "Li " +0x6739: "Qiu " +0x673a: "Ji " +0x673b: "Jiu " +0x673c: "Bi " +0x673d: "Xiu " +0x673e: "Ting " +0x673f: "Ci " +0x6740: "Sha " +0x6741: "Eburi " +0x6742: "Za " +0x6743: "Quan " +0x6744: "Qian " +0x6745: "Yu " +0x6746: "Gan " +0x6747: "Wu " +0x6748: "Cha " +0x6749: "Shan " +0x674a: "Xun " +0x674b: "Fan " +0x674c: "Wu " +0x674d: "Zi " +0x674e: "Li " +0x674f: "Xing " +0x6750: "Cai " +0x6751: "Cun " +0x6752: "Ren " +0x6753: "Shao " +0x6754: "Tuo " +0x6755: "Di " +0x6756: "Zhang " +0x6757: "Mang " +0x6758: "Chi " +0x6759: "Yi " +0x675a: "Gu " +0x675b: "Gong " +0x675c: "Du " +0x675d: "Yi " +0x675e: "Qi " +0x675f: "Shu " +0x6760: "Gang " +0x6761: "Tiao " +0x6762: "Moku " +0x6763: "Soma " +0x6764: "Tochi " +0x6765: "Lai " +0x6766: "Sugi " +0x6767: "Mang " +0x6768: "Yang " +0x6769: "Ma " +0x676a: "Miao " +0x676b: "Si " +0x676c: "Yuan " +0x676d: "Hang " +0x676e: "Fei " +0x676f: "Bei " +0x6770: "Jie " +0x6771: "Dong " +0x6772: "Gao " +0x6773: "Yao " +0x6774: "Xian " +0x6775: "Chu " +0x6776: "Qun " +0x6777: "Pa " +0x6778: "Shu " +0x6779: "Hua " +0x677a: "Xin " +0x677b: "Chou " +0x677c: "Zhu " +0x677d: "Chou " +0x677e: "Song " +0x677f: "Ban " +0x6780: "Song " +0x6781: "Ji " +0x6782: "Yue " +0x6783: "Jin " +0x6784: "Gou " +0x6785: "Ji " +0x6786: "Mao " +0x6787: "Pi " +0x6788: "Bi " +0x6789: "Wang " +0x678a: "Ang " +0x678b: "Fang " +0x678c: "Fen " +0x678d: "Yi " +0x678e: "Fu " +0x678f: "Nan " +0x6790: "Xi " +0x6791: "Hu " +0x6792: "Ya " +0x6793: "Dou " +0x6794: "Xun " +0x6795: "Zhen " +0x6796: "Yao " +0x6797: "Lin " +0x6798: "Rui " +0x6799: "E " +0x679a: "Mei " +0x679b: "Zhao " +0x679c: "Guo " +0x679d: "Zhi " +0x679e: "Cong " +0x679f: "Yun " +0x67a0: "Waku " +0x67a1: "Dou " +0x67a2: "Shu " +0x67a3: "Zao " +0x67a4: "[?] " +0x67a5: "Li " +0x67a6: "Haze " +0x67a7: "Jian " +0x67a8: "Cheng " +0x67a9: "Matsu " +0x67aa: "Qiang " +0x67ab: "Feng " +0x67ac: "Nan " +0x67ad: "Xiao " +0x67ae: "Xian " +0x67af: "Ku " +0x67b0: "Ping " +0x67b1: "Yi " +0x67b2: "Xi " +0x67b3: "Zhi " +0x67b4: "Guai " +0x67b5: "Xiao " +0x67b6: "Jia " +0x67b7: "Jia " +0x67b8: "Gou " +0x67b9: "Fu " +0x67ba: "Mo " +0x67bb: "Yi " +0x67bc: "Ye " +0x67bd: "Ye " +0x67be: "Shi " +0x67bf: "Nie " +0x67c0: "Bi " +0x67c1: "Duo " +0x67c2: "Yi " +0x67c3: "Ling " +0x67c4: "Bing " +0x67c5: "Ni " +0x67c6: "La " +0x67c7: "He " +0x67c8: "Pan " +0x67c9: "Fan " +0x67ca: "Zhong " +0x67cb: "Dai " +0x67cc: "Ci " +0x67cd: "Yang " +0x67ce: "Fu " +0x67cf: "Bo " +0x67d0: "Mou " +0x67d1: "Gan " +0x67d2: "Qi " +0x67d3: "Ran " +0x67d4: "Rou " +0x67d5: "Mao " +0x67d6: "Zhao " +0x67d7: "Song " +0x67d8: "Zhe " +0x67d9: "Xia " +0x67da: "You " +0x67db: "Shen " +0x67dc: "Ju " +0x67dd: "Tuo " +0x67de: "Zuo " +0x67df: "Nan " +0x67e0: "Ning " +0x67e1: "Yong " +0x67e2: "Di " +0x67e3: "Zhi " +0x67e4: "Zha " +0x67e5: "Cha " +0x67e6: "Dan " +0x67e7: "Gu " +0x67e8: "Pu " +0x67e9: "Jiu " +0x67ea: "Ao " +0x67eb: "Fu " +0x67ec: "Jian " +0x67ed: "Bo " +0x67ee: "Duo " +0x67ef: "Ke " +0x67f0: "Nai " +0x67f1: "Zhu " +0x67f2: "Bi " +0x67f3: "Liu " +0x67f4: "Chai " +0x67f5: "Zha " +0x67f6: "Si " +0x67f7: "Zhu " +0x67f8: "Pei " +0x67f9: "Shi " +0x67fa: "Guai " +0x67fb: "Cha " +0x67fc: "Yao " +0x67fd: "Jue " +0x67fe: "Jiu " +0x67ff: "Shi " +/* x068 */ +0x6800: "Zhi " +0x6801: "Liu " +0x6802: "Mei " +0x6803: "Hoy " +0x6804: "Rong " +0x6805: "Zha " +0x6806: "[?] " +0x6807: "Biao " +0x6808: "Zhan " +0x6809: "Jie " +0x680a: "Long " +0x680b: "Dong " +0x680c: "Lu " +0x680d: "Sayng " +0x680e: "Li " +0x680f: "Lan " +0x6810: "Yong " +0x6811: "Shu " +0x6812: "Xun " +0x6813: "Shuan " +0x6814: "Qi " +0x6815: "Zhen " +0x6816: "Qi " +0x6817: "Li " +0x6818: "Yi " +0x6819: "Xiang " +0x681a: "Zhen " +0x681b: "Li " +0x681c: "Su " +0x681d: "Gua " +0x681e: "Kan " +0x681f: "Bing " +0x6820: "Ren " +0x6821: "Xiao " +0x6822: "Bo " +0x6823: "Ren " +0x6824: "Bing " +0x6825: "Zi " +0x6826: "Chou " +0x6827: "Yi " +0x6828: "Jie " +0x6829: "Xu " +0x682a: "Zhu " +0x682b: "Jian " +0x682c: "Zui " +0x682d: "Er " +0x682e: "Er " +0x682f: "You " +0x6830: "Fa " +0x6831: "Gong " +0x6832: "Kao " +0x6833: "Lao " +0x6834: "Zhan " +0x6835: "Li " +0x6836: "Yin " +0x6837: "Yang " +0x6838: "He " +0x6839: "Gen " +0x683a: "Zhi " +0x683b: "Chi " +0x683c: "Ge " +0x683d: "Zai " +0x683e: "Luan " +0x683f: "Fu " +0x6840: "Jie " +0x6841: "Hang " +0x6842: "Gui " +0x6843: "Tao " +0x6844: "Guang " +0x6845: "Wei " +0x6846: "Kuang " +0x6847: "Ru " +0x6848: "An " +0x6849: "An " +0x684a: "Juan " +0x684b: "Yi " +0x684c: "Zhuo " +0x684d: "Ku " +0x684e: "Zhi " +0x684f: "Qiong " +0x6850: "Tong " +0x6851: "Sang " +0x6852: "Sang " +0x6853: "Huan " +0x6854: "Jie " +0x6855: "Jiu " +0x6856: "Xue " +0x6857: "Duo " +0x6858: "Zhui " +0x6859: "Yu " +0x685a: "Zan " +0x685b: "Kasei " +0x685c: "Ying " +0x685d: "Masu " +0x685e: "[?] " +0x685f: "Zhan " +0x6860: "Ya " +0x6861: "Nao " +0x6862: "Zhen " +0x6863: "Dang " +0x6864: "Qi " +0x6865: "Qiao " +0x6866: "Hua " +0x6867: "Kuai " +0x6868: "Jiang " +0x6869: "Zhuang " +0x686a: "Xun " +0x686b: "Suo " +0x686c: "Sha " +0x686d: "Zhen " +0x686e: "Bei " +0x686f: "Ting " +0x6870: "Gua " +0x6871: "Jing " +0x6872: "Bo " +0x6873: "Ben " +0x6874: "Fu " +0x6875: "Rui " +0x6876: "Tong " +0x6877: "Jue " +0x6878: "Xi " +0x6879: "Lang " +0x687a: "Liu " +0x687b: "Feng " +0x687c: "Qi " +0x687d: "Wen " +0x687e: "Jun " +0x687f: "Gan " +0x6880: "Cu " +0x6881: "Liang " +0x6882: "Qiu " +0x6883: "Ting " +0x6884: "You " +0x6885: "Mei " +0x6886: "Bang " +0x6887: "Long " +0x6888: "Peng " +0x6889: "Zhuang " +0x688a: "Di " +0x688b: "Xuan " +0x688c: "Tu " +0x688d: "Zao " +0x688e: "Ao " +0x688f: "Gu " +0x6890: "Bi " +0x6891: "Di " +0x6892: "Han " +0x6893: "Zi " +0x6894: "Zhi " +0x6895: "Ren " +0x6896: "Bei " +0x6897: "Geng " +0x6898: "Jian " +0x6899: "Huan " +0x689a: "Wan " +0x689b: "Nuo " +0x689c: "Jia " +0x689d: "Tiao " +0x689e: "Ji " +0x689f: "Xiao " +0x68a0: "Lu " +0x68a1: "Huan " +0x68a2: "Shao " +0x68a3: "Cen " +0x68a4: "Fen " +0x68a5: "Song " +0x68a6: "Meng " +0x68a7: "Wu " +0x68a8: "Li " +0x68a9: "Li " +0x68aa: "Dou " +0x68ab: "Cen " +0x68ac: "Ying " +0x68ad: "Suo " +0x68ae: "Ju " +0x68af: "Ti " +0x68b0: "Jie " +0x68b1: "Kun " +0x68b2: "Zhuo " +0x68b3: "Shu " +0x68b4: "Chan " +0x68b5: "Fan " +0x68b6: "Wei " +0x68b7: "Jing " +0x68b8: "Li " +0x68b9: "Bing " +0x68ba: "Fumoto " +0x68bb: "Shikimi " +0x68bc: "Tao " +0x68bd: "Zhi " +0x68be: "Lai " +0x68bf: "Lian " +0x68c0: "Jian " +0x68c1: "Zhuo " +0x68c2: "Ling " +0x68c3: "Li " +0x68c4: "Qi " +0x68c5: "Bing " +0x68c6: "Zhun " +0x68c7: "Cong " +0x68c8: "Qian " +0x68c9: "Mian " +0x68ca: "Qi " +0x68cb: "Qi " +0x68cc: "Cai " +0x68cd: "Gun " +0x68ce: "Chan " +0x68cf: "Te " +0x68d0: "Fei " +0x68d1: "Pai " +0x68d2: "Bang " +0x68d3: "Pou " +0x68d4: "Hun " +0x68d5: "Zong " +0x68d6: "Cheng " +0x68d7: "Zao " +0x68d8: "Ji " +0x68d9: "Li " +0x68da: "Peng " +0x68db: "Yu " +0x68dc: "Yu " +0x68dd: "Gu " +0x68de: "Hun " +0x68df: "Dong " +0x68e0: "Tang " +0x68e1: "Gang " +0x68e2: "Wang " +0x68e3: "Di " +0x68e4: "Xi " +0x68e5: "Fan " +0x68e6: "Cheng " +0x68e7: "Zhan " +0x68e8: "Qi " +0x68e9: "Yuan " +0x68ea: "Yan " +0x68eb: "Yu " +0x68ec: "Quan " +0x68ed: "Yi " +0x68ee: "Sen " +0x68ef: "Ren " +0x68f0: "Chui " +0x68f1: "Leng " +0x68f2: "Qi " +0x68f3: "Zhuo " +0x68f4: "Fu " +0x68f5: "Ke " +0x68f6: "Lai " +0x68f7: "Zou " +0x68f8: "Zou " +0x68f9: "Zhuo " +0x68fa: "Guan " +0x68fb: "Fen " +0x68fc: "Fen " +0x68fd: "Chen " +0x68fe: "Qiong " +0x68ff: "Nie " +/* x069 */ +0x6900: "Wan " +0x6901: "Guo " +0x6902: "Lu " +0x6903: "Hao " +0x6904: "Jie " +0x6905: "Yi " +0x6906: "Chou " +0x6907: "Ju " +0x6908: "Ju " +0x6909: "Cheng " +0x690a: "Zuo " +0x690b: "Liang " +0x690c: "Qiang " +0x690d: "Zhi " +0x690e: "Zhui " +0x690f: "Ya " +0x6910: "Ju " +0x6911: "Bei " +0x6912: "Jiao " +0x6913: "Zhuo " +0x6914: "Zi " +0x6915: "Bin " +0x6916: "Peng " +0x6917: "Ding " +0x6918: "Chu " +0x6919: "Chang " +0x691a: "Kunugi " +0x691b: "Momiji " +0x691c: "Jian " +0x691d: "Gui " +0x691e: "Xi " +0x691f: "Du " +0x6920: "Qian " +0x6921: "Kunugi " +0x6922: "Soko " +0x6923: "Shide " +0x6924: "Luo " +0x6925: "Zhi " +0x6926: "Ken " +0x6927: "Myeng " +0x6928: "Tafu " +0x6929: "[?] " +0x692a: "Peng " +0x692b: "Zhan " +0x692c: "[?] " +0x692d: "Tuo " +0x692e: "Sen " +0x692f: "Duo " +0x6930: "Ye " +0x6931: "Fou " +0x6932: "Wei " +0x6933: "Wei " +0x6934: "Duan " +0x6935: "Jia " +0x6936: "Zong " +0x6937: "Jian " +0x6938: "Yi " +0x6939: "Shen " +0x693a: "Xi " +0x693b: "Yan " +0x693c: "Yan " +0x693d: "Chuan " +0x693e: "Zhan " +0x693f: "Chun " +0x6940: "Yu " +0x6941: "He " +0x6942: "Zha " +0x6943: "Wo " +0x6944: "Pian " +0x6945: "Bi " +0x6946: "Yao " +0x6947: "Huo " +0x6948: "Xu " +0x6949: "Ruo " +0x694a: "Yang " +0x694b: "La " +0x694c: "Yan " +0x694d: "Ben " +0x694e: "Hun " +0x694f: "Kui " +0x6950: "Jie " +0x6951: "Kui " +0x6952: "Si " +0x6953: "Feng " +0x6954: "Xie " +0x6955: "Tuo " +0x6956: "Zhi " +0x6957: "Jian " +0x6958: "Mu " +0x6959: "Mao " +0x695a: "Chu " +0x695b: "Hu " +0x695c: "Hu " +0x695d: "Lian " +0x695e: "Leng " +0x695f: "Ting " +0x6960: "Nan " +0x6961: "Yu " +0x6962: "You " +0x6963: "Mei " +0x6964: "Song " +0x6965: "Xuan " +0x6966: "Xuan " +0x6967: "Ying " +0x6968: "Zhen " +0x6969: "Pian " +0x696a: "Ye " +0x696b: "Ji " +0x696c: "Jie " +0x696d: "Ye " +0x696e: "Chu " +0x696f: "Shun " +0x6970: "Yu " +0x6971: "Cou " +0x6972: "Wei " +0x6973: "Mei " +0x6974: "Di " +0x6975: "Ji " +0x6976: "Jie " +0x6977: "Kai " +0x6978: "Qiu " +0x6979: "Ying " +0x697a: "Rou " +0x697b: "Heng " +0x697c: "Lou " +0x697d: "Le " +0x697e: "Hazou " +0x697f: "Katsura " +0x6980: "Pin " +0x6981: "Muro " +0x6982: "Gai " +0x6983: "Tan " +0x6984: "Lan " +0x6985: "Yun " +0x6986: "Yu " +0x6987: "Chen " +0x6988: "Lu " +0x6989: "Ju " +0x698a: "Sakaki " +0x698b: "[?] " +0x698c: "Pi " +0x698d: "Xie " +0x698e: "Jia " +0x698f: "Yi " +0x6990: "Zhan " +0x6991: "Fu " +0x6992: "Nai " +0x6993: "Mi " +0x6994: "Lang " +0x6995: "Rong " +0x6996: "Gu " +0x6997: "Jian " +0x6998: "Ju " +0x6999: "Ta " +0x699a: "Yao " +0x699b: "Zhen " +0x699c: "Bang " +0x699d: "Sha " +0x699e: "Yuan " +0x699f: "Zi " +0x69a0: "Ming " +0x69a1: "Su " +0x69a2: "Jia " +0x69a3: "Yao " +0x69a4: "Jie " +0x69a5: "Huang " +0x69a6: "Gan " +0x69a7: "Fei " +0x69a8: "Zha " +0x69a9: "Qian " +0x69aa: "Ma " +0x69ab: "Sun " +0x69ac: "Yuan " +0x69ad: "Xie " +0x69ae: "Rong " +0x69af: "Shi " +0x69b0: "Zhi " +0x69b1: "Cui " +0x69b2: "Yun " +0x69b3: "Ting " +0x69b4: "Liu " +0x69b5: "Rong " +0x69b6: "Tang " +0x69b7: "Que " +0x69b8: "Zhai " +0x69b9: "Si " +0x69ba: "Sheng " +0x69bb: "Ta " +0x69bc: "Ke " +0x69bd: "Xi " +0x69be: "Gu " +0x69bf: "Qi " +0x69c0: "Kao " +0x69c1: "Gao " +0x69c2: "Sun " +0x69c3: "Pan " +0x69c4: "Tao " +0x69c5: "Ge " +0x69c6: "Xun " +0x69c7: "Dian " +0x69c8: "Nou " +0x69c9: "Ji " +0x69ca: "Shuo " +0x69cb: "Gou " +0x69cc: "Chui " +0x69cd: "Qiang " +0x69ce: "Cha " +0x69cf: "Qian " +0x69d0: "Huai " +0x69d1: "Mei " +0x69d2: "Xu " +0x69d3: "Gang " +0x69d4: "Gao " +0x69d5: "Zhuo " +0x69d6: "Tuo " +0x69d7: "Hashi " +0x69d8: "Yang " +0x69d9: "Dian " +0x69da: "Jia " +0x69db: "Jian " +0x69dc: "Zui " +0x69dd: "Kashi " +0x69de: "Ori " +0x69df: "Bin " +0x69e0: "Zhu " +0x69e1: "[?] " +0x69e2: "Xi " +0x69e3: "Qi " +0x69e4: "Lian " +0x69e5: "Hui " +0x69e6: "Yong " +0x69e7: "Qian " +0x69e8: "Guo " +0x69e9: "Gai " +0x69ea: "Gai " +0x69eb: "Tuan " +0x69ec: "Hua " +0x69ed: "Cu " +0x69ee: "Sen " +0x69ef: "Cui " +0x69f0: "Beng " +0x69f1: "You " +0x69f2: "Hu " +0x69f3: "Jiang " +0x69f4: "Hu " +0x69f5: "Huan " +0x69f6: "Kui " +0x69f7: "Yi " +0x69f8: "Nie " +0x69f9: "Gao " +0x69fa: "Kang " +0x69fb: "Gui " +0x69fc: "Gui " +0x69fd: "Cao " +0x69fe: "Man " +0x69ff: "Jin " +/* x06a */ +0x6a00: "Di " +0x6a01: "Zhuang " +0x6a02: "Le " +0x6a03: "Lang " +0x6a04: "Chen " +0x6a05: "Cong " +0x6a06: "Li " +0x6a07: "Xiu " +0x6a08: "Qing " +0x6a09: "Shuang " +0x6a0a: "Fan " +0x6a0b: "Tong " +0x6a0c: "Guan " +0x6a0d: "Ji " +0x6a0e: "Suo " +0x6a0f: "Lei " +0x6a10: "Lu " +0x6a11: "Liang " +0x6a12: "Mi " +0x6a13: "Lou " +0x6a14: "Chao " +0x6a15: "Su " +0x6a16: "Ke " +0x6a17: "Shu " +0x6a18: "Tang " +0x6a19: "Biao " +0x6a1a: "Lu " +0x6a1b: "Jiu " +0x6a1c: "Shu " +0x6a1d: "Zha " +0x6a1e: "Shu " +0x6a1f: "Zhang " +0x6a20: "Men " +0x6a21: "Mo " +0x6a22: "Niao " +0x6a23: "Yang " +0x6a24: "Tiao " +0x6a25: "Peng " +0x6a26: "Zhu " +0x6a27: "Sha " +0x6a28: "Xi " +0x6a29: "Quan " +0x6a2a: "Heng " +0x6a2b: "Jian " +0x6a2c: "Cong " +0x6a2d: "[?] " +0x6a2e: "Hokuso " +0x6a2f: "Qiang " +0x6a30: "Tara " +0x6a31: "Ying " +0x6a32: "Er " +0x6a33: "Xin " +0x6a34: "Zhi " +0x6a35: "Qiao " +0x6a36: "Zui " +0x6a37: "Cong " +0x6a38: "Pu " +0x6a39: "Shu " +0x6a3a: "Hua " +0x6a3b: "Kui " +0x6a3c: "Zhen " +0x6a3d: "Zun " +0x6a3e: "Yue " +0x6a3f: "Zhan " +0x6a40: "Xi " +0x6a41: "Xun " +0x6a42: "Dian " +0x6a43: "Fa " +0x6a44: "Gan " +0x6a45: "Mo " +0x6a46: "Wu " +0x6a47: "Qiao " +0x6a48: "Nao " +0x6a49: "Lin " +0x6a4a: "Liu " +0x6a4b: "Qiao " +0x6a4c: "Xian " +0x6a4d: "Run " +0x6a4e: "Fan " +0x6a4f: "Zhan " +0x6a50: "Tuo " +0x6a51: "Lao " +0x6a52: "Yun " +0x6a53: "Shun " +0x6a54: "Tui " +0x6a55: "Cheng " +0x6a56: "Tang " +0x6a57: "Meng " +0x6a58: "Ju " +0x6a59: "Cheng " +0x6a5a: "Su " +0x6a5b: "Jue " +0x6a5c: "Jue " +0x6a5d: "Tan " +0x6a5e: "Hui " +0x6a5f: "Ji " +0x6a60: "Nuo " +0x6a61: "Xiang " +0x6a62: "Tuo " +0x6a63: "Ning " +0x6a64: "Rui " +0x6a65: "Zhu " +0x6a66: "Chuang " +0x6a67: "Zeng " +0x6a68: "Fen " +0x6a69: "Qiong " +0x6a6a: "Ran " +0x6a6b: "Heng " +0x6a6c: "Cen " +0x6a6d: "Gu " +0x6a6e: "Liu " +0x6a6f: "Lao " +0x6a70: "Gao " +0x6a71: "Chu " +0x6a72: "Zusa " +0x6a73: "Nude " +0x6a74: "Ca " +0x6a75: "San " +0x6a76: "Ji " +0x6a77: "Dou " +0x6a78: "Shou " +0x6a79: "Lu " +0x6a7a: "[?] " +0x6a7b: "[?] " +0x6a7c: "Yuan " +0x6a7d: "Ta " +0x6a7e: "Shu " +0x6a7f: "Jiang " +0x6a80: "Tan " +0x6a81: "Lin " +0x6a82: "Nong " +0x6a83: "Yin " +0x6a84: "Xi " +0x6a85: "Sui " +0x6a86: "Shan " +0x6a87: "Zui " +0x6a88: "Xuan " +0x6a89: "Cheng " +0x6a8a: "Gan " +0x6a8b: "Ju " +0x6a8c: "Zui " +0x6a8d: "Yi " +0x6a8e: "Qin " +0x6a8f: "Pu " +0x6a90: "Yan " +0x6a91: "Lei " +0x6a92: "Feng " +0x6a93: "Hui " +0x6a94: "Dang " +0x6a95: "Ji " +0x6a96: "Sui " +0x6a97: "Bo " +0x6a98: "Bi " +0x6a99: "Ding " +0x6a9a: "Chu " +0x6a9b: "Zhua " +0x6a9c: "Kuai " +0x6a9d: "Ji " +0x6a9e: "Jie " +0x6a9f: "Jia " +0x6aa0: "Qing " +0x6aa1: "Zhe " +0x6aa2: "Jian " +0x6aa3: "Qiang " +0x6aa4: "Dao " +0x6aa5: "Yi " +0x6aa6: "Biao " +0x6aa7: "Song " +0x6aa8: "She " +0x6aa9: "Lin " +0x6aaa: "Kunugi " +0x6aab: "Cha " +0x6aac: "Meng " +0x6aad: "Yin " +0x6aae: "Tao " +0x6aaf: "Tai " +0x6ab0: "Mian " +0x6ab1: "Qi " +0x6ab2: "Toan " +0x6ab3: "Bin " +0x6ab4: "Huo " +0x6ab5: "Ji " +0x6ab6: "Qian " +0x6ab7: "Mi " +0x6ab8: "Ning " +0x6ab9: "Yi " +0x6aba: "Gao " +0x6abb: "Jian " +0x6abc: "Yin " +0x6abd: "Er " +0x6abe: "Qing " +0x6abf: "Yan " +0x6ac0: "Qi " +0x6ac1: "Mi " +0x6ac2: "Zhao " +0x6ac3: "Gui " +0x6ac4: "Chun " +0x6ac5: "Ji " +0x6ac6: "Kui " +0x6ac7: "Po " +0x6ac8: "Deng " +0x6ac9: "Chu " +0x6aca: "[?] " +0x6acb: "Mian " +0x6acc: "You " +0x6acd: "Zhi " +0x6ace: "Guang " +0x6acf: "Qian " +0x6ad0: "Lei " +0x6ad1: "Lei " +0x6ad2: "Sa " +0x6ad3: "Lu " +0x6ad4: "Li " +0x6ad5: "Cuan " +0x6ad6: "Lu " +0x6ad7: "Mie " +0x6ad8: "Hui " +0x6ad9: "Ou " +0x6ada: "Lu " +0x6adb: "Jie " +0x6adc: "Gao " +0x6add: "Du " +0x6ade: "Yuan " +0x6adf: "Li " +0x6ae0: "Fei " +0x6ae1: "Zhuo " +0x6ae2: "Sou " +0x6ae3: "Lian " +0x6ae4: "Tamo " +0x6ae5: "Chu " +0x6ae6: "[?] " +0x6ae7: "Zhu " +0x6ae8: "Lu " +0x6ae9: "Yan " +0x6aea: "Li " +0x6aeb: "Zhu " +0x6aec: "Chen " +0x6aed: "Jie " +0x6aee: "E " +0x6aef: "Su " +0x6af0: "Huai " +0x6af1: "Nie " +0x6af2: "Yu " +0x6af3: "Long " +0x6af4: "Lai " +0x6af5: "[?] " +0x6af6: "Xian " +0x6af7: "Kwi " +0x6af8: "Ju " +0x6af9: "Xiao " +0x6afa: "Ling " +0x6afb: "Ying " +0x6afc: "Jian " +0x6afd: "Yin " +0x6afe: "You " +0x6aff: "Ying " +/* x06b */ +0x6b00: "Xiang " +0x6b01: "Nong " +0x6b02: "Bo " +0x6b03: "Chan " +0x6b04: "Lan " +0x6b05: "Ju " +0x6b06: "Shuang " +0x6b07: "She " +0x6b08: "Wei " +0x6b09: "Cong " +0x6b0a: "Quan " +0x6b0b: "Qu " +0x6b0c: "Cang " +0x6b0d: "[?] " +0x6b0e: "Yu " +0x6b0f: "Luo " +0x6b10: "Li " +0x6b11: "Zan " +0x6b12: "Luan " +0x6b13: "Dang " +0x6b14: "Jue " +0x6b15: "Em " +0x6b16: "Lan " +0x6b17: "Lan " +0x6b18: "Zhu " +0x6b19: "Lei " +0x6b1a: "Li " +0x6b1b: "Ba " +0x6b1c: "Nang " +0x6b1d: "Yu " +0x6b1e: "Ling " +0x6b1f: "Tsuki " +0x6b20: "Qian " +0x6b21: "Ci " +0x6b22: "Huan " +0x6b23: "Xin " +0x6b24: "Yu " +0x6b25: "Yu " +0x6b26: "Qian " +0x6b27: "Ou " +0x6b28: "Xu " +0x6b29: "Chao " +0x6b2a: "Chu " +0x6b2b: "Chi " +0x6b2c: "Kai " +0x6b2d: "Yi " +0x6b2e: "Jue " +0x6b2f: "Xi " +0x6b30: "Xu " +0x6b31: "Xia " +0x6b32: "Yu " +0x6b33: "Kuai " +0x6b34: "Lang " +0x6b35: "Kuan " +0x6b36: "Shuo " +0x6b37: "Xi " +0x6b38: "Ai " +0x6b39: "Yi " +0x6b3a: "Qi " +0x6b3b: "Hu " +0x6b3c: "Chi " +0x6b3d: "Qin " +0x6b3e: "Kuan " +0x6b3f: "Kan " +0x6b40: "Kuan " +0x6b41: "Kan " +0x6b42: "Chuan " +0x6b43: "Sha " +0x6b44: "Gua " +0x6b45: "Yin " +0x6b46: "Xin " +0x6b47: "Xie " +0x6b48: "Yu " +0x6b49: "Qian " +0x6b4a: "Xiao " +0x6b4b: "Yi " +0x6b4c: "Ge " +0x6b4d: "Wu " +0x6b4e: "Tan " +0x6b4f: "Jin " +0x6b50: "Ou " +0x6b51: "Hu " +0x6b52: "Ti " +0x6b53: "Huan " +0x6b54: "Xu " +0x6b55: "Pen " +0x6b56: "Xi " +0x6b57: "Xiao " +0x6b58: "Xu " +0x6b59: "Xi " +0x6b5a: "Sen " +0x6b5b: "Lian " +0x6b5c: "Chu " +0x6b5d: "Yi " +0x6b5e: "Kan " +0x6b5f: "Yu " +0x6b60: "Chuo " +0x6b61: "Huan " +0x6b62: "Zhi " +0x6b63: "Zheng " +0x6b64: "Ci " +0x6b65: "Bu " +0x6b66: "Wu " +0x6b67: "Qi " +0x6b68: "Bu " +0x6b69: "Bu " +0x6b6a: "Wai " +0x6b6b: "Ju " +0x6b6c: "Qian " +0x6b6d: "Chi " +0x6b6e: "Se " +0x6b6f: "Chi " +0x6b70: "Se " +0x6b71: "Zhong " +0x6b72: "Sui " +0x6b73: "Sui " +0x6b74: "Li " +0x6b75: "Cuo " +0x6b76: "Yu " +0x6b77: "Li " +0x6b78: "Gui " +0x6b79: "Dai " +0x6b7a: "Dai " +0x6b7b: "Si " +0x6b7c: "Jian " +0x6b7d: "Zhe " +0x6b7e: "Mo " +0x6b7f: "Mo " +0x6b80: "Yao " +0x6b81: "Mo " +0x6b82: "Cu " +0x6b83: "Yang " +0x6b84: "Tian " +0x6b85: "Sheng " +0x6b86: "Dai " +0x6b87: "Shang " +0x6b88: "Xu " +0x6b89: "Xun " +0x6b8a: "Shu " +0x6b8b: "Can " +0x6b8c: "Jue " +0x6b8d: "Piao " +0x6b8e: "Qia " +0x6b8f: "Qiu " +0x6b90: "Su " +0x6b91: "Qing " +0x6b92: "Yun " +0x6b93: "Lian " +0x6b94: "Yi " +0x6b95: "Fou " +0x6b96: "Zhi " +0x6b97: "Ye " +0x6b98: "Can " +0x6b99: "Hun " +0x6b9a: "Dan " +0x6b9b: "Ji " +0x6b9c: "Ye " +0x6b9d: "Zhen " +0x6b9e: "Yun " +0x6b9f: "Wen " +0x6ba0: "Chou " +0x6ba1: "Bin " +0x6ba2: "Ti " +0x6ba3: "Jin " +0x6ba4: "Shang " +0x6ba5: "Yin " +0x6ba6: "Diao " +0x6ba7: "Cu " +0x6ba8: "Hui " +0x6ba9: "Cuan " +0x6baa: "Yi " +0x6bab: "Dan " +0x6bac: "Du " +0x6bad: "Jiang " +0x6bae: "Lian " +0x6baf: "Bin " +0x6bb0: "Du " +0x6bb1: "Tsukusu " +0x6bb2: "Jian " +0x6bb3: "Shu " +0x6bb4: "Ou " +0x6bb5: "Duan " +0x6bb6: "Zhu " +0x6bb7: "Yin " +0x6bb8: "Qing " +0x6bb9: "Yi " +0x6bba: "Sha " +0x6bbb: "Que " +0x6bbc: "Ke " +0x6bbd: "Yao " +0x6bbe: "Jun " +0x6bbf: "Dian " +0x6bc0: "Hui " +0x6bc1: "Hui " +0x6bc2: "Gu " +0x6bc3: "Que " +0x6bc4: "Ji " +0x6bc5: "Yi " +0x6bc6: "Ou " +0x6bc7: "Hui " +0x6bc8: "Duan " +0x6bc9: "Yi " +0x6bca: "Xiao " +0x6bcb: "Wu " +0x6bcc: "Guan " +0x6bcd: "Mu " +0x6bce: "Mei " +0x6bcf: "Mei " +0x6bd0: "Ai " +0x6bd1: "Zuo " +0x6bd2: "Du " +0x6bd3: "Yu " +0x6bd4: "Bi " +0x6bd5: "Bi " +0x6bd6: "Bi " +0x6bd7: "Pi " +0x6bd8: "Pi " +0x6bd9: "Bi " +0x6bda: "Chan " +0x6bdb: "Mao " +0x6bdc: "[?] " +0x6bdd: "[?] " +0x6bde: "Pu " +0x6bdf: "Mushiru " +0x6be0: "Jia " +0x6be1: "Zhan " +0x6be2: "Sai " +0x6be3: "Mu " +0x6be4: "Tuo " +0x6be5: "Xun " +0x6be6: "Er " +0x6be7: "Rong " +0x6be8: "Xian " +0x6be9: "Ju " +0x6bea: "Mu " +0x6beb: "Hao " +0x6bec: "Qiu " +0x6bed: "Dou " +0x6bee: "Mushiru " +0x6bef: "Tan " +0x6bf0: "Pei " +0x6bf1: "Ju " +0x6bf2: "Duo " +0x6bf3: "Cui " +0x6bf4: "Bi " +0x6bf5: "San " +0x6bf6: "[?] " +0x6bf7: "Mao " +0x6bf8: "Sui " +0x6bf9: "Yu " +0x6bfa: "Yu " +0x6bfb: "Tuo " +0x6bfc: "He " +0x6bfd: "Jian " +0x6bfe: "Ta " +0x6bff: "San " +/* x06c */ +0x6c00: "Lu " +0x6c01: "Mu " +0x6c02: "Li " +0x6c03: "Tong " +0x6c04: "Rong " +0x6c05: "Chang " +0x6c06: "Pu " +0x6c07: "Luo " +0x6c08: "Zhan " +0x6c09: "Sao " +0x6c0a: "Zhan " +0x6c0b: "Meng " +0x6c0c: "Luo " +0x6c0d: "Qu " +0x6c0e: "Die " +0x6c0f: "Shi " +0x6c10: "Di " +0x6c11: "Min " +0x6c12: "Jue " +0x6c13: "Mang " +0x6c14: "Qi " +0x6c15: "Pie " +0x6c16: "Nai " +0x6c17: "Qi " +0x6c18: "Dao " +0x6c19: "Xian " +0x6c1a: "Chuan " +0x6c1b: "Fen " +0x6c1c: "Ri " +0x6c1d: "Nei " +0x6c1e: "[?] " +0x6c1f: "Fu " +0x6c20: "Shen " +0x6c21: "Dong " +0x6c22: "Qing " +0x6c23: "Qi " +0x6c24: "Yin " +0x6c25: "Xi " +0x6c26: "Hai " +0x6c27: "Yang " +0x6c28: "An " +0x6c29: "Ya " +0x6c2a: "Ke " +0x6c2b: "Qing " +0x6c2c: "Ya " +0x6c2d: "Dong " +0x6c2e: "Dan " +0x6c2f: "Lu " +0x6c30: "Qing " +0x6c31: "Yang " +0x6c32: "Yun " +0x6c33: "Yun " +0x6c34: "Shui " +0x6c35: "San " +0x6c36: "Zheng " +0x6c37: "Bing " +0x6c38: "Yong " +0x6c39: "Dang " +0x6c3a: "Shitamizu " +0x6c3b: "Le " +0x6c3c: "Ni " +0x6c3d: "Tun " +0x6c3e: "Fan " +0x6c3f: "Gui " +0x6c40: "Ting " +0x6c41: "Zhi " +0x6c42: "Qiu " +0x6c43: "Bin " +0x6c44: "Ze " +0x6c45: "Mian " +0x6c46: "Cuan " +0x6c47: "Hui " +0x6c48: "Diao " +0x6c49: "Yi " +0x6c4a: "Cha " +0x6c4b: "Zhuo " +0x6c4c: "Chuan " +0x6c4d: "Wan " +0x6c4e: "Fan " +0x6c4f: "Dai " +0x6c50: "Xi " +0x6c51: "Tuo " +0x6c52: "Mang " +0x6c53: "Qiu " +0x6c54: "Qi " +0x6c55: "Shan " +0x6c56: "Pai " +0x6c57: "Han " +0x6c58: "Qian " +0x6c59: "Wu " +0x6c5a: "Wu " +0x6c5b: "Xun " +0x6c5c: "Si " +0x6c5d: "Ru " +0x6c5e: "Gong " +0x6c5f: "Jiang " +0x6c60: "Chi " +0x6c61: "Wu " +0x6c62: "Tsuchi " +0x6c63: "[?] " +0x6c64: "Tang " +0x6c65: "Zhi " +0x6c66: "Chi " +0x6c67: "Qian " +0x6c68: "Mi " +0x6c69: "Yu " +0x6c6a: "Wang " +0x6c6b: "Qing " +0x6c6c: "Jing " +0x6c6d: "Rui " +0x6c6e: "Jun " +0x6c6f: "Hong " +0x6c70: "Tai " +0x6c71: "Quan " +0x6c72: "Ji " +0x6c73: "Bian " +0x6c74: "Bian " +0x6c75: "Gan " +0x6c76: "Wen " +0x6c77: "Zhong " +0x6c78: "Fang " +0x6c79: "Xiong " +0x6c7a: "Jue " +0x6c7b: "Hang " +0x6c7c: "Niou " +0x6c7d: "Qi " +0x6c7e: "Fen " +0x6c7f: "Xu " +0x6c80: "Xu " +0x6c81: "Qin " +0x6c82: "Yi " +0x6c83: "Wo " +0x6c84: "Yun " +0x6c85: "Yuan " +0x6c86: "Hang " +0x6c87: "Yan " +0x6c88: "Chen " +0x6c89: "Chen " +0x6c8a: "Dan " +0x6c8b: "You " +0x6c8c: "Dun " +0x6c8d: "Hu " +0x6c8e: "Huo " +0x6c8f: "Qie " +0x6c90: "Mu " +0x6c91: "Rou " +0x6c92: "Mei " +0x6c93: "Ta " +0x6c94: "Mian " +0x6c95: "Wu " +0x6c96: "Chong " +0x6c97: "Tian " +0x6c98: "Bi " +0x6c99: "Sha " +0x6c9a: "Zhi " +0x6c9b: "Pei " +0x6c9c: "Pan " +0x6c9d: "Zhui " +0x6c9e: "Za " +0x6c9f: "Gou " +0x6ca0: "Liu " +0x6ca1: "Mei " +0x6ca2: "Ze " +0x6ca3: "Feng " +0x6ca4: "Ou " +0x6ca5: "Li " +0x6ca6: "Lun " +0x6ca7: "Cang " +0x6ca8: "Feng " +0x6ca9: "Wei " +0x6caa: "Hu " +0x6cab: "Mo " +0x6cac: "Mei " +0x6cad: "Shu " +0x6cae: "Ju " +0x6caf: "Zan " +0x6cb0: "Tuo " +0x6cb1: "Tuo " +0x6cb2: "Tuo " +0x6cb3: "He " +0x6cb4: "Li " +0x6cb5: "Mi " +0x6cb6: "Yi " +0x6cb7: "Fa " +0x6cb8: "Fei " +0x6cb9: "You " +0x6cba: "Tian " +0x6cbb: "Zhi " +0x6cbc: "Zhao " +0x6cbd: "Gu " +0x6cbe: "Zhan " +0x6cbf: "Yan " +0x6cc0: "Si " +0x6cc1: "Kuang " +0x6cc2: "Jiong " +0x6cc3: "Ju " +0x6cc4: "Xie " +0x6cc5: "Qiu " +0x6cc6: "Yi " +0x6cc7: "Jia " +0x6cc8: "Zhong " +0x6cc9: "Quan " +0x6cca: "Bo " +0x6ccb: "Hui " +0x6ccc: "Mi " +0x6ccd: "Ben " +0x6cce: "Zhuo " +0x6ccf: "Chu " +0x6cd0: "Le " +0x6cd1: "You " +0x6cd2: "Gu " +0x6cd3: "Hong " +0x6cd4: "Gan " +0x6cd5: "Fa " +0x6cd6: "Mao " +0x6cd7: "Si " +0x6cd8: "Hu " +0x6cd9: "Ping " +0x6cda: "Ci " +0x6cdb: "Fan " +0x6cdc: "Chi " +0x6cdd: "Su " +0x6cde: "Ning " +0x6cdf: "Cheng " +0x6ce0: "Ling " +0x6ce1: "Pao " +0x6ce2: "Bo " +0x6ce3: "Qi " +0x6ce4: "Si " +0x6ce5: "Ni " +0x6ce6: "Ju " +0x6ce7: "Yue " +0x6ce8: "Zhu " +0x6ce9: "Sheng " +0x6cea: "Lei " +0x6ceb: "Xuan " +0x6cec: "Xue " +0x6ced: "Fu " +0x6cee: "Pan " +0x6cef: "Min " +0x6cf0: "Tai " +0x6cf1: "Yang " +0x6cf2: "Ji " +0x6cf3: "Yong " +0x6cf4: "Guan " +0x6cf5: "Beng " +0x6cf6: "Xue " +0x6cf7: "Long " +0x6cf8: "Lu " +0x6cf9: "[?] " +0x6cfa: "Bo " +0x6cfb: "Xie " +0x6cfc: "Po " +0x6cfd: "Ze " +0x6cfe: "Jing " +0x6cff: "Yin " +/* x06d */ +0x6d00: "Zhou " +0x6d01: "Ji " +0x6d02: "Yi " +0x6d03: "Hui " +0x6d04: "Hui " +0x6d05: "Zui " +0x6d06: "Cheng " +0x6d07: "Yin " +0x6d08: "Wei " +0x6d09: "Hou " +0x6d0a: "Jian " +0x6d0b: "Yang " +0x6d0c: "Lie " +0x6d0d: "Si " +0x6d0e: "Ji " +0x6d0f: "Er " +0x6d10: "Xing " +0x6d11: "Fu " +0x6d12: "Sa " +0x6d13: "Suo " +0x6d14: "Zhi " +0x6d15: "Yin " +0x6d16: "Wu " +0x6d17: "Xi " +0x6d18: "Kao " +0x6d19: "Zhu " +0x6d1a: "Jiang " +0x6d1b: "Luo " +0x6d1c: "[?] " +0x6d1d: "An " +0x6d1e: "Dong " +0x6d1f: "Yi " +0x6d20: "Mou " +0x6d21: "Lei " +0x6d22: "Yi " +0x6d23: "Mi " +0x6d24: "Quan " +0x6d25: "Jin " +0x6d26: "Mo " +0x6d27: "Wei " +0x6d28: "Xiao " +0x6d29: "Xie " +0x6d2a: "Hong " +0x6d2b: "Xu " +0x6d2c: "Shuo " +0x6d2d: "Kuang " +0x6d2e: "Tao " +0x6d2f: "Qie " +0x6d30: "Ju " +0x6d31: "Er " +0x6d32: "Zhou " +0x6d33: "Ru " +0x6d34: "Ping " +0x6d35: "Xun " +0x6d36: "Xiong " +0x6d37: "Zhi " +0x6d38: "Guang " +0x6d39: "Huan " +0x6d3a: "Ming " +0x6d3b: "Huo " +0x6d3c: "Wa " +0x6d3d: "Qia " +0x6d3e: "Pai " +0x6d3f: "Wu " +0x6d40: "Qu " +0x6d41: "Liu " +0x6d42: "Yi " +0x6d43: "Jia " +0x6d44: "Jing " +0x6d45: "Qian " +0x6d46: "Jiang " +0x6d47: "Jiao " +0x6d48: "Cheng " +0x6d49: "Shi " +0x6d4a: "Zhuo " +0x6d4b: "Ce " +0x6d4c: "Pal " +0x6d4d: "Kuai " +0x6d4e: "Ji " +0x6d4f: "Liu " +0x6d50: "Chan " +0x6d51: "Hun " +0x6d52: "Hu " +0x6d53: "Nong " +0x6d54: "Xun " +0x6d55: "Jin " +0x6d56: "Lie " +0x6d57: "Qiu " +0x6d58: "Wei " +0x6d59: "Zhe " +0x6d5a: "Jun " +0x6d5b: "Han " +0x6d5c: "Bang " +0x6d5d: "Mang " +0x6d5e: "Zhuo " +0x6d5f: "You " +0x6d60: "Xi " +0x6d61: "Bo " +0x6d62: "Dou " +0x6d63: "Wan " +0x6d64: "Hong " +0x6d65: "Yi " +0x6d66: "Pu " +0x6d67: "Ying " +0x6d68: "Lan " +0x6d69: "Hao " +0x6d6a: "Lang " +0x6d6b: "Han " +0x6d6c: "Li " +0x6d6d: "Geng " +0x6d6e: "Fu " +0x6d6f: "Wu " +0x6d70: "Lian " +0x6d71: "Chun " +0x6d72: "Feng " +0x6d73: "Yi " +0x6d74: "Yu " +0x6d75: "Tong " +0x6d76: "Lao " +0x6d77: "Hai " +0x6d78: "Jin " +0x6d79: "Jia " +0x6d7a: "Chong " +0x6d7b: "Weng " +0x6d7c: "Mei " +0x6d7d: "Sui " +0x6d7e: "Cheng " +0x6d7f: "Pei " +0x6d80: "Xian " +0x6d81: "Shen " +0x6d82: "Tu " +0x6d83: "Kun " +0x6d84: "Pin " +0x6d85: "Nie " +0x6d86: "Han " +0x6d87: "Jing " +0x6d88: "Xiao " +0x6d89: "She " +0x6d8a: "Nian " +0x6d8b: "Tu " +0x6d8c: "Yong " +0x6d8d: "Xiao " +0x6d8e: "Xian " +0x6d8f: "Ting " +0x6d90: "E " +0x6d91: "Su " +0x6d92: "Tun " +0x6d93: "Juan " +0x6d94: "Cen " +0x6d95: "Ti " +0x6d96: "Li " +0x6d97: "Shui " +0x6d98: "Si " +0x6d99: "Lei " +0x6d9a: "Shui " +0x6d9b: "Tao " +0x6d9c: "Du " +0x6d9d: "Lao " +0x6d9e: "Lai " +0x6d9f: "Lian " +0x6da0: "Wei " +0x6da1: "Wo " +0x6da2: "Yun " +0x6da3: "Huan " +0x6da4: "Di " +0x6da5: "[?] " +0x6da6: "Run " +0x6da7: "Jian " +0x6da8: "Zhang " +0x6da9: "Se " +0x6daa: "Fu " +0x6dab: "Guan " +0x6dac: "Xing " +0x6dad: "Shou " +0x6dae: "Shuan " +0x6daf: "Ya " +0x6db0: "Chuo " +0x6db1: "Zhang " +0x6db2: "Ye " +0x6db3: "Kong " +0x6db4: "Wo " +0x6db5: "Han " +0x6db6: "Tuo " +0x6db7: "Dong " +0x6db8: "He " +0x6db9: "Wo " +0x6dba: "Ju " +0x6dbb: "Gan " +0x6dbc: "Liang " +0x6dbd: "Hun " +0x6dbe: "Ta " +0x6dbf: "Zhuo " +0x6dc0: "Dian " +0x6dc1: "Qie " +0x6dc2: "De " +0x6dc3: "Juan " +0x6dc4: "Zi " +0x6dc5: "Xi " +0x6dc6: "Yao " +0x6dc7: "Qi " +0x6dc8: "Gu " +0x6dc9: "Guo " +0x6dca: "Han " +0x6dcb: "Lin " +0x6dcc: "Tang " +0x6dcd: "Zhou " +0x6dce: "Peng " +0x6dcf: "Hao " +0x6dd0: "Chang " +0x6dd1: "Shu " +0x6dd2: "Qi " +0x6dd3: "Fang " +0x6dd4: "Chi " +0x6dd5: "Lu " +0x6dd6: "Nao " +0x6dd7: "Ju " +0x6dd8: "Tao " +0x6dd9: "Cong " +0x6dda: "Lei " +0x6ddb: "Zhi " +0x6ddc: "Peng " +0x6ddd: "Fei " +0x6dde: "Song " +0x6ddf: "Tian " +0x6de0: "Pi " +0x6de1: "Dan " +0x6de2: "Yu " +0x6de3: "Ni " +0x6de4: "Yu " +0x6de5: "Lu " +0x6de6: "Gan " +0x6de7: "Mi " +0x6de8: "Jing " +0x6de9: "Ling " +0x6dea: "Lun " +0x6deb: "Yin " +0x6dec: "Cui " +0x6ded: "Qu " +0x6dee: "Huai " +0x6def: "Yu " +0x6df0: "Nian " +0x6df1: "Shen " +0x6df2: "Piao " +0x6df3: "Chun " +0x6df4: "Wa " +0x6df5: "Yuan " +0x6df6: "Lai " +0x6df7: "Hun " +0x6df8: "Qing " +0x6df9: "Yan " +0x6dfa: "Qian " +0x6dfb: "Tian " +0x6dfc: "Miao " +0x6dfd: "Zhi " +0x6dfe: "Yin " +0x6dff: "Mi " +/* x06e */ +0x6e00: "Ben " +0x6e01: "Yuan " +0x6e02: "Wen " +0x6e03: "Re " +0x6e04: "Fei " +0x6e05: "Qing " +0x6e06: "Yuan " +0x6e07: "Ke " +0x6e08: "Ji " +0x6e09: "She " +0x6e0a: "Yuan " +0x6e0b: "Shibui " +0x6e0c: "Lu " +0x6e0d: "Zi " +0x6e0e: "Du " +0x6e0f: "[?] " +0x6e10: "Jian " +0x6e11: "Min " +0x6e12: "Pi " +0x6e13: "Tani " +0x6e14: "Yu " +0x6e15: "Yuan " +0x6e16: "Shen " +0x6e17: "Shen " +0x6e18: "Rou " +0x6e19: "Huan " +0x6e1a: "Zhu " +0x6e1b: "Jian " +0x6e1c: "Nuan " +0x6e1d: "Yu " +0x6e1e: "Qiu " +0x6e1f: "Ting " +0x6e20: "Qu " +0x6e21: "Du " +0x6e22: "Feng " +0x6e23: "Zha " +0x6e24: "Bo " +0x6e25: "Wo " +0x6e26: "Wo " +0x6e27: "Di " +0x6e28: "Wei " +0x6e29: "Wen " +0x6e2a: "Ru " +0x6e2b: "Xie " +0x6e2c: "Ce " +0x6e2d: "Wei " +0x6e2e: "Ge " +0x6e2f: "Gang " +0x6e30: "Yan " +0x6e31: "Hong " +0x6e32: "Xuan " +0x6e33: "Mi " +0x6e34: "Ke " +0x6e35: "Mao " +0x6e36: "Ying " +0x6e37: "Yan " +0x6e38: "You " +0x6e39: "Hong " +0x6e3a: "Miao " +0x6e3b: "Xing " +0x6e3c: "Mei " +0x6e3d: "Zai " +0x6e3e: "Hun " +0x6e3f: "Nai " +0x6e40: "Kui " +0x6e41: "Shi " +0x6e42: "E " +0x6e43: "Pai " +0x6e44: "Mei " +0x6e45: "Lian " +0x6e46: "Qi " +0x6e47: "Qi " +0x6e48: "Mei " +0x6e49: "Tian " +0x6e4a: "Cou " +0x6e4b: "Wei " +0x6e4c: "Can " +0x6e4d: "Tuan " +0x6e4e: "Mian " +0x6e4f: "Hui " +0x6e50: "Mo " +0x6e51: "Xu " +0x6e52: "Ji " +0x6e53: "Pen " +0x6e54: "Jian " +0x6e55: "Jian " +0x6e56: "Hu " +0x6e57: "Feng " +0x6e58: "Xiang " +0x6e59: "Yi " +0x6e5a: "Yin " +0x6e5b: "Zhan " +0x6e5c: "Shi " +0x6e5d: "Jie " +0x6e5e: "Cheng " +0x6e5f: "Huang " +0x6e60: "Tan " +0x6e61: "Yu " +0x6e62: "Bi " +0x6e63: "Min " +0x6e64: "Shi " +0x6e65: "Tu " +0x6e66: "Sheng " +0x6e67: "Yong " +0x6e68: "Qu " +0x6e69: "Zhong " +0x6e6a: "Suei " +0x6e6b: "Jiu " +0x6e6c: "Jiao " +0x6e6d: "Qiou " +0x6e6e: "Yin " +0x6e6f: "Tang " +0x6e70: "Long " +0x6e71: "Huo " +0x6e72: "Yuan " +0x6e73: "Nan " +0x6e74: "Ban " +0x6e75: "You " +0x6e76: "Quan " +0x6e77: "Chui " +0x6e78: "Liang " +0x6e79: "Chan " +0x6e7a: "Yan " +0x6e7b: "Chun " +0x6e7c: "Nie " +0x6e7d: "Zi " +0x6e7e: "Wan " +0x6e7f: "Shi " +0x6e80: "Man " +0x6e81: "Ying " +0x6e82: "Ratsu " +0x6e83: "Kui " +0x6e84: "[?] " +0x6e85: "Jian " +0x6e86: "Xu " +0x6e87: "Lu " +0x6e88: "Gui " +0x6e89: "Gai " +0x6e8a: "[?] " +0x6e8b: "[?] " +0x6e8c: "Po " +0x6e8d: "Jin " +0x6e8e: "Gui " +0x6e8f: "Tang " +0x6e90: "Yuan " +0x6e91: "Suo " +0x6e92: "Yuan " +0x6e93: "Lian " +0x6e94: "Yao " +0x6e95: "Meng " +0x6e96: "Zhun " +0x6e97: "Sheng " +0x6e98: "Ke " +0x6e99: "Tai " +0x6e9a: "Da " +0x6e9b: "Wa " +0x6e9c: "Liu " +0x6e9d: "Gou " +0x6e9e: "Sao " +0x6e9f: "Ming " +0x6ea0: "Zha " +0x6ea1: "Shi " +0x6ea2: "Yi " +0x6ea3: "Lun " +0x6ea4: "Ma " +0x6ea5: "Pu " +0x6ea6: "Wei " +0x6ea7: "Li " +0x6ea8: "Cai " +0x6ea9: "Wu " +0x6eaa: "Xi " +0x6eab: "Wen " +0x6eac: "Qiang " +0x6ead: "Ze " +0x6eae: "Shi " +0x6eaf: "Su " +0x6eb0: "Yi " +0x6eb1: "Zhen " +0x6eb2: "Sou " +0x6eb3: "Yun " +0x6eb4: "Xiu " +0x6eb5: "Yin " +0x6eb6: "Rong " +0x6eb7: "Hun " +0x6eb8: "Su " +0x6eb9: "Su " +0x6eba: "Ni " +0x6ebb: "Ta " +0x6ebc: "Shi " +0x6ebd: "Ru " +0x6ebe: "Wei " +0x6ebf: "Pan " +0x6ec0: "Chu " +0x6ec1: "Chu " +0x6ec2: "Pang " +0x6ec3: "Weng " +0x6ec4: "Cang " +0x6ec5: "Mie " +0x6ec6: "He " +0x6ec7: "Dian " +0x6ec8: "Hao " +0x6ec9: "Huang " +0x6eca: "Xi " +0x6ecb: "Zi " +0x6ecc: "Di " +0x6ecd: "Zhi " +0x6ece: "Ying " +0x6ecf: "Fu " +0x6ed0: "Jie " +0x6ed1: "Hua " +0x6ed2: "Ge " +0x6ed3: "Zi " +0x6ed4: "Tao " +0x6ed5: "Teng " +0x6ed6: "Sui " +0x6ed7: "Bi " +0x6ed8: "Jiao " +0x6ed9: "Hui " +0x6eda: "Gun " +0x6edb: "Yin " +0x6edc: "Gao " +0x6edd: "Long " +0x6ede: "Zhi " +0x6edf: "Yan " +0x6ee0: "She " +0x6ee1: "Man " +0x6ee2: "Ying " +0x6ee3: "Chun " +0x6ee4: "Lu " +0x6ee5: "Lan " +0x6ee6: "Luan " +0x6ee7: "[?] " +0x6ee8: "Bin " +0x6ee9: "Tan " +0x6eea: "Yu " +0x6eeb: "Sou " +0x6eec: "Hu " +0x6eed: "Bi " +0x6eee: "Biao " +0x6eef: "Zhi " +0x6ef0: "Jiang " +0x6ef1: "Kou " +0x6ef2: "Shen " +0x6ef3: "Shang " +0x6ef4: "Di " +0x6ef5: "Mi " +0x6ef6: "Ao " +0x6ef7: "Lu " +0x6ef8: "Hu " +0x6ef9: "Hu " +0x6efa: "You " +0x6efb: "Chan " +0x6efc: "Fan " +0x6efd: "Yong " +0x6efe: "Gun " +0x6eff: "Man " +/* x06f */ +0x6f00: "Qing " +0x6f01: "Yu " +0x6f02: "Piao " +0x6f03: "Ji " +0x6f04: "Ya " +0x6f05: "Jiao " +0x6f06: "Qi " +0x6f07: "Xi " +0x6f08: "Ji " +0x6f09: "Lu " +0x6f0a: "Lu " +0x6f0b: "Long " +0x6f0c: "Jin " +0x6f0d: "Guo " +0x6f0e: "Cong " +0x6f0f: "Lou " +0x6f10: "Zhi " +0x6f11: "Gai " +0x6f12: "Qiang " +0x6f13: "Li " +0x6f14: "Yan " +0x6f15: "Cao " +0x6f16: "Jiao " +0x6f17: "Cong " +0x6f18: "Qun " +0x6f19: "Tuan " +0x6f1a: "Ou " +0x6f1b: "Teng " +0x6f1c: "Ye " +0x6f1d: "Xi " +0x6f1e: "Mi " +0x6f1f: "Tang " +0x6f20: "Mo " +0x6f21: "Shang " +0x6f22: "Han " +0x6f23: "Lian " +0x6f24: "Lan " +0x6f25: "Wa " +0x6f26: "Li " +0x6f27: "Qian " +0x6f28: "Feng " +0x6f29: "Xuan " +0x6f2a: "Yi " +0x6f2b: "Man " +0x6f2c: "Zi " +0x6f2d: "Mang " +0x6f2e: "Kang " +0x6f2f: "Lei " +0x6f30: "Peng " +0x6f31: "Shu " +0x6f32: "Zhang " +0x6f33: "Zhang " +0x6f34: "Chong " +0x6f35: "Xu " +0x6f36: "Huan " +0x6f37: "Kuo " +0x6f38: "Jian " +0x6f39: "Yan " +0x6f3a: "Chuang " +0x6f3b: "Liao " +0x6f3c: "Cui " +0x6f3d: "Ti " +0x6f3e: "Yang " +0x6f3f: "Jiang " +0x6f40: "Cong " +0x6f41: "Ying " +0x6f42: "Hong " +0x6f43: "Xun " +0x6f44: "Shu " +0x6f45: "Guan " +0x6f46: "Ying " +0x6f47: "Xiao " +0x6f48: "[?] " +0x6f49: "[?] " +0x6f4a: "Xu " +0x6f4b: "Lian " +0x6f4c: "Zhi " +0x6f4d: "Wei " +0x6f4e: "Pi " +0x6f4f: "Jue " +0x6f50: "Jiao " +0x6f51: "Po " +0x6f52: "Dang " +0x6f53: "Hui " +0x6f54: "Jie " +0x6f55: "Wu " +0x6f56: "Pa " +0x6f57: "Ji " +0x6f58: "Pan " +0x6f59: "Gui " +0x6f5a: "Xiao " +0x6f5b: "Qian " +0x6f5c: "Qian " +0x6f5d: "Xi " +0x6f5e: "Lu " +0x6f5f: "Xi " +0x6f60: "Xuan " +0x6f61: "Dun " +0x6f62: "Huang " +0x6f63: "Min " +0x6f64: "Run " +0x6f65: "Su " +0x6f66: "Liao " +0x6f67: "Zhen " +0x6f68: "Zhong " +0x6f69: "Yi " +0x6f6a: "Di " +0x6f6b: "Wan " +0x6f6c: "Dan " +0x6f6d: "Tan " +0x6f6e: "Chao " +0x6f6f: "Xun " +0x6f70: "Kui " +0x6f71: "Yie " +0x6f72: "Shao " +0x6f73: "Tu " +0x6f74: "Zhu " +0x6f75: "San " +0x6f76: "Hei " +0x6f77: "Bi " +0x6f78: "Shan " +0x6f79: "Chan " +0x6f7a: "Chan " +0x6f7b: "Shu " +0x6f7c: "Tong " +0x6f7d: "Pu " +0x6f7e: "Lin " +0x6f7f: "Wei " +0x6f80: "Se " +0x6f81: "Se " +0x6f82: "Cheng " +0x6f83: "Jiong " +0x6f84: "Cheng " +0x6f85: "Hua " +0x6f86: "Jiao " +0x6f87: "Lao " +0x6f88: "Che " +0x6f89: "Gan " +0x6f8a: "Cun " +0x6f8b: "Heng " +0x6f8c: "Si " +0x6f8d: "Shu " +0x6f8e: "Peng " +0x6f8f: "Han " +0x6f90: "Yun " +0x6f91: "Liu " +0x6f92: "Hong " +0x6f93: "Fu " +0x6f94: "Hao " +0x6f95: "He " +0x6f96: "Xian " +0x6f97: "Jian " +0x6f98: "Shan " +0x6f99: "Xi " +0x6f9a: "Oki " +0x6f9b: "[?] " +0x6f9c: "Lan " +0x6f9d: "[?] " +0x6f9e: "Yu " +0x6f9f: "Lin " +0x6fa0: "Min " +0x6fa1: "Zao " +0x6fa2: "Dang " +0x6fa3: "Wan " +0x6fa4: "Ze " +0x6fa5: "Xie " +0x6fa6: "Yu " +0x6fa7: "Li " +0x6fa8: "Shi " +0x6fa9: "Xue " +0x6faa: "Ling " +0x6fab: "Man " +0x6fac: "Zi " +0x6fad: "Yong " +0x6fae: "Kuai " +0x6faf: "Can " +0x6fb0: "Lian " +0x6fb1: "Dian " +0x6fb2: "Ye " +0x6fb3: "Ao " +0x6fb4: "Huan " +0x6fb5: "Zhen " +0x6fb6: "Chan " +0x6fb7: "Man " +0x6fb8: "Dan " +0x6fb9: "Dan " +0x6fba: "Yi " +0x6fbb: "Sui " +0x6fbc: "Pi " +0x6fbd: "Ju " +0x6fbe: "Ta " +0x6fbf: "Qin " +0x6fc0: "Ji " +0x6fc1: "Zhuo " +0x6fc2: "Lian " +0x6fc3: "Nong " +0x6fc4: "Guo " +0x6fc5: "Jin " +0x6fc6: "Fen " +0x6fc7: "Se " +0x6fc8: "Ji " +0x6fc9: "Sui " +0x6fca: "Hui " +0x6fcb: "Chu " +0x6fcc: "Ta " +0x6fcd: "Song " +0x6fce: "Ding " +0x6fcf: "[?] " +0x6fd0: "Zhu " +0x6fd1: "Lai " +0x6fd2: "Bin " +0x6fd3: "Lian " +0x6fd4: "Mi " +0x6fd5: "Shi " +0x6fd6: "Shu " +0x6fd7: "Mi " +0x6fd8: "Ning " +0x6fd9: "Ying " +0x6fda: "Ying " +0x6fdb: "Meng " +0x6fdc: "Jin " +0x6fdd: "Qi " +0x6fde: "Pi " +0x6fdf: "Ji " +0x6fe0: "Hao " +0x6fe1: "Ru " +0x6fe2: "Zui " +0x6fe3: "Wo " +0x6fe4: "Tao " +0x6fe5: "Yin " +0x6fe6: "Yin " +0x6fe7: "Dui " +0x6fe8: "Ci " +0x6fe9: "Huo " +0x6fea: "Jing " +0x6feb: "Lan " +0x6fec: "Jun " +0x6fed: "Ai " +0x6fee: "Pu " +0x6fef: "Zhuo " +0x6ff0: "Wei " +0x6ff1: "Bin " +0x6ff2: "Gu " +0x6ff3: "Qian " +0x6ff4: "Xing " +0x6ff5: "Hama " +0x6ff6: "Kuo " +0x6ff7: "Fei " +0x6ff8: "[?] " +0x6ff9: "Boku " +0x6ffa: "Jian " +0x6ffb: "Wei " +0x6ffc: "Luo " +0x6ffd: "Zan " +0x6ffe: "Lu " +0x6fff: "Li " +/* x070 */ +0x7000: "You " +0x7001: "Yang " +0x7002: "Lu " +0x7003: "Si " +0x7004: "Jie " +0x7005: "Ying " +0x7006: "Du " +0x7007: "Wang " +0x7008: "Hui " +0x7009: "Xie " +0x700a: "Pan " +0x700b: "Shen " +0x700c: "Biao " +0x700d: "Chan " +0x700e: "Mo " +0x700f: "Liu " +0x7010: "Jian " +0x7011: "Pu " +0x7012: "Se " +0x7013: "Cheng " +0x7014: "Gu " +0x7015: "Bin " +0x7016: "Huo " +0x7017: "Xian " +0x7018: "Lu " +0x7019: "Qin " +0x701a: "Han " +0x701b: "Ying " +0x701c: "Yong " +0x701d: "Li " +0x701e: "Jing " +0x701f: "Xiao " +0x7020: "Ying " +0x7021: "Sui " +0x7022: "Wei " +0x7023: "Xie " +0x7024: "Huai " +0x7025: "Hao " +0x7026: "Zhu " +0x7027: "Long " +0x7028: "Lai " +0x7029: "Dui " +0x702a: "Fan " +0x702b: "Hu " +0x702c: "Lai " +0x702d: "[?] " +0x702e: "[?] " +0x702f: "Ying " +0x7030: "Mi " +0x7031: "Ji " +0x7032: "Lian " +0x7033: "Jian " +0x7034: "Ying " +0x7035: "Fen " +0x7036: "Lin " +0x7037: "Yi " +0x7038: "Jian " +0x7039: "Yue " +0x703a: "Chan " +0x703b: "Dai " +0x703c: "Rang " +0x703d: "Jian " +0x703e: "Lan " +0x703f: "Fan " +0x7040: "Shuang " +0x7041: "Yuan " +0x7042: "Zhuo " +0x7043: "Feng " +0x7044: "She " +0x7045: "Lei " +0x7046: "Lan " +0x7047: "Cong " +0x7048: "Qu " +0x7049: "Yong " +0x704a: "Qian " +0x704b: "Fa " +0x704c: "Guan " +0x704d: "Que " +0x704e: "Yan " +0x704f: "Hao " +0x7050: "Hyeng " +0x7051: "Sa " +0x7052: "Zan " +0x7053: "Luan " +0x7054: "Yan " +0x7055: "Li " +0x7056: "Mi " +0x7057: "Shan " +0x7058: "Tan " +0x7059: "Dang " +0x705a: "Jiao " +0x705b: "Chan " +0x705c: "[?] " +0x705d: "Hao " +0x705e: "Ba " +0x705f: "Zhu " +0x7060: "Lan " +0x7061: "Lan " +0x7062: "Nang " +0x7063: "Wan " +0x7064: "Luan " +0x7065: "Xun " +0x7066: "Xian " +0x7067: "Yan " +0x7068: "Gan " +0x7069: "Yan " +0x706a: "Yu " +0x706b: "Huo " +0x706c: "Si " +0x706d: "Mie " +0x706e: "Guang " +0x706f: "Deng " +0x7070: "Hui " +0x7071: "Xiao " +0x7072: "Xiao " +0x7073: "Hu " +0x7074: "Hong " +0x7075: "Ling " +0x7076: "Zao " +0x7077: "Zhuan " +0x7078: "Jiu " +0x7079: "Zha " +0x707a: "Xie " +0x707b: "Chi " +0x707c: "Zhuo " +0x707d: "Zai " +0x707e: "Zai " +0x707f: "Can " +0x7080: "Yang " +0x7081: "Qi " +0x7082: "Zhong " +0x7083: "Fen " +0x7084: "Niu " +0x7085: "Jiong " +0x7086: "Wen " +0x7087: "Po " +0x7088: "Yi " +0x7089: "Lu " +0x708a: "Chui " +0x708b: "Pi " +0x708c: "Kai " +0x708d: "Pan " +0x708e: "Yan " +0x708f: "Kai " +0x7090: "Pang " +0x7091: "Mu " +0x7092: "Chao " +0x7093: "Liao " +0x7094: "Gui " +0x7095: "Kang " +0x7096: "Tun " +0x7097: "Guang " +0x7098: "Xin " +0x7099: "Zhi " +0x709a: "Guang " +0x709b: "Guang " +0x709c: "Wei " +0x709d: "Qiang " +0x709e: "[?] " +0x709f: "Da " +0x70a0: "Xia " +0x70a1: "Zheng " +0x70a2: "Zhu " +0x70a3: "Ke " +0x70a4: "Zhao " +0x70a5: "Fu " +0x70a6: "Ba " +0x70a7: "Duo " +0x70a8: "Duo " +0x70a9: "Ling " +0x70aa: "Zhuo " +0x70ab: "Xuan " +0x70ac: "Ju " +0x70ad: "Tan " +0x70ae: "Pao " +0x70af: "Jiong " +0x70b0: "Pao " +0x70b1: "Tai " +0x70b2: "Tai " +0x70b3: "Bing " +0x70b4: "Yang " +0x70b5: "Tong " +0x70b6: "Han " +0x70b7: "Zhu " +0x70b8: "Zha " +0x70b9: "Dian " +0x70ba: "Wei " +0x70bb: "Shi " +0x70bc: "Lian " +0x70bd: "Chi " +0x70be: "Huang " +0x70bf: "[?] " +0x70c0: "Hu " +0x70c1: "Shuo " +0x70c2: "Lan " +0x70c3: "Jing " +0x70c4: "Jiao " +0x70c5: "Xu " +0x70c6: "Xing " +0x70c7: "Quan " +0x70c8: "Lie " +0x70c9: "Huan " +0x70ca: "Yang " +0x70cb: "Xiao " +0x70cc: "Xiu " +0x70cd: "Xian " +0x70ce: "Yin " +0x70cf: "Wu " +0x70d0: "Zhou " +0x70d1: "Yao " +0x70d2: "Shi " +0x70d3: "Wei " +0x70d4: "Tong " +0x70d5: "Xue " +0x70d6: "Zai " +0x70d7: "Kai " +0x70d8: "Hong " +0x70d9: "Luo " +0x70da: "Xia " +0x70db: "Zhu " +0x70dc: "Xuan " +0x70dd: "Zheng " +0x70de: "Po " +0x70df: "Yan " +0x70e0: "Hui " +0x70e1: "Guang " +0x70e2: "Zhe " +0x70e3: "Hui " +0x70e4: "Kao " +0x70e5: "[?] " +0x70e6: "Fan " +0x70e7: "Shao " +0x70e8: "Ye " +0x70e9: "Hui " +0x70ea: "[?] " +0x70eb: "Tang " +0x70ec: "Jin " +0x70ed: "Re " +0x70ee: "[?] " +0x70ef: "Xi " +0x70f0: "Fu " +0x70f1: "Jiong " +0x70f2: "Che " +0x70f3: "Pu " +0x70f4: "Jing " +0x70f5: "Zhuo " +0x70f6: "Ting " +0x70f7: "Wan " +0x70f8: "Hai " +0x70f9: "Peng " +0x70fa: "Lang " +0x70fb: "Shan " +0x70fc: "Hu " +0x70fd: "Feng " +0x70fe: "Chi " +0x70ff: "Rong " +/* x071 */ +0x7100: "Hu " +0x7101: "Xi " +0x7102: "Shu " +0x7103: "He " +0x7104: "Xun " +0x7105: "Ku " +0x7106: "Jue " +0x7107: "Xiao " +0x7108: "Xi " +0x7109: "Yan " +0x710a: "Han " +0x710b: "Zhuang " +0x710c: "Jun " +0x710d: "Di " +0x710e: "Xie " +0x710f: "Ji " +0x7110: "Wu " +0x7111: "[?] " +0x7112: "[?] " +0x7113: "Han " +0x7114: "Yan " +0x7115: "Huan " +0x7116: "Men " +0x7117: "Ju " +0x7118: "Chou " +0x7119: "Bei " +0x711a: "Fen " +0x711b: "Lin " +0x711c: "Kun " +0x711d: "Hun " +0x711e: "Tun " +0x711f: "Xi " +0x7120: "Cui " +0x7121: "Wu " +0x7122: "Hong " +0x7123: "Ju " +0x7124: "Fu " +0x7125: "Wo " +0x7126: "Jiao " +0x7127: "Cong " +0x7128: "Feng " +0x7129: "Ping " +0x712a: "Qiong " +0x712b: "Ruo " +0x712c: "Xi " +0x712d: "Qiong " +0x712e: "Xin " +0x712f: "Zhuo " +0x7130: "Yan " +0x7131: "Yan " +0x7132: "Yi " +0x7133: "Jue " +0x7134: "Yu " +0x7135: "Gang " +0x7136: "Ran " +0x7137: "Pi " +0x7138: "Gu " +0x7139: "[?] " +0x713a: "Sheng " +0x713b: "Chang " +0x713c: "Shao " +0x713d: "[?] " +0x713e: "[?] " +0x713f: "[?] " +0x7140: "[?] " +0x7141: "Chen " +0x7142: "He " +0x7143: "Kui " +0x7144: "Zhong " +0x7145: "Duan " +0x7146: "Xia " +0x7147: "Hui " +0x7148: "Feng " +0x7149: "Lian " +0x714a: "Xuan " +0x714b: "Xing " +0x714c: "Huang " +0x714d: "Jiao " +0x714e: "Jian " +0x714f: "Bi " +0x7150: "Ying " +0x7151: "Zhu " +0x7152: "Wei " +0x7153: "Tuan " +0x7154: "Tian " +0x7155: "Xi " +0x7156: "Nuan " +0x7157: "Nuan " +0x7158: "Chan " +0x7159: "Yan " +0x715a: "Jiong " +0x715b: "Jiong " +0x715c: "Yu " +0x715d: "Mei " +0x715e: "Sha " +0x715f: "Wei " +0x7160: "Ye " +0x7161: "Xin " +0x7162: "Qiong " +0x7163: "Rou " +0x7164: "Mei " +0x7165: "Huan " +0x7166: "Xu " +0x7167: "Zhao " +0x7168: "Wei " +0x7169: "Fan " +0x716a: "Qiu " +0x716b: "Sui " +0x716c: "Yang " +0x716d: "Lie " +0x716e: "Zhu " +0x716f: "Jie " +0x7170: "Gao " +0x7171: "Gua " +0x7172: "Bao " +0x7173: "Hu " +0x7174: "Yun " +0x7175: "Xia " +0x7176: "[?] " +0x7177: "[?] " +0x7178: "Bian " +0x7179: "Gou " +0x717a: "Tui " +0x717b: "Tang " +0x717c: "Chao " +0x717d: "Shan " +0x717e: "N " +0x717f: "Bo " +0x7180: "Huang " +0x7181: "Xie " +0x7182: "Xi " +0x7183: "Wu " +0x7184: "Xi " +0x7185: "Yun " +0x7186: "He " +0x7187: "He " +0x7188: "Xi " +0x7189: "Yun " +0x718a: "Xiong " +0x718b: "Nai " +0x718c: "Shan " +0x718d: "Qiong " +0x718e: "Yao " +0x718f: "Xun " +0x7190: "Mi " +0x7191: "Lian " +0x7192: "Ying " +0x7193: "Wen " +0x7194: "Rong " +0x7195: "Oozutsu " +0x7196: "[?] " +0x7197: "Qiang " +0x7198: "Liu " +0x7199: "Xi " +0x719a: "Bi " +0x719b: "Biao " +0x719c: "Zong " +0x719d: "Lu " +0x719e: "Jian " +0x719f: "Shou " +0x71a0: "Yi " +0x71a1: "Lou " +0x71a2: "Feng " +0x71a3: "Sui " +0x71a4: "Yi " +0x71a5: "Tong " +0x71a6: "Jue " +0x71a7: "Zong " +0x71a8: "Yun " +0x71a9: "Hu " +0x71aa: "Yi " +0x71ab: "Zhi " +0x71ac: "Ao " +0x71ad: "Wei " +0x71ae: "Liao " +0x71af: "Han " +0x71b0: "Ou " +0x71b1: "Re " +0x71b2: "Jiong " +0x71b3: "Man " +0x71b4: "[?] " +0x71b5: "Shang " +0x71b6: "Cuan " +0x71b7: "Zeng " +0x71b8: "Jian " +0x71b9: "Xi " +0x71ba: "Xi " +0x71bb: "Xi " +0x71bc: "Yi " +0x71bd: "Xiao " +0x71be: "Chi " +0x71bf: "Huang " +0x71c0: "Chan " +0x71c1: "Ye " +0x71c2: "Qian " +0x71c3: "Ran " +0x71c4: "Yan " +0x71c5: "Xian " +0x71c6: "Qiao " +0x71c7: "Zun " +0x71c8: "Deng " +0x71c9: "Dun " +0x71ca: "Shen " +0x71cb: "Jiao " +0x71cc: "Fen " +0x71cd: "Si " +0x71ce: "Liao " +0x71cf: "Yu " +0x71d0: "Lin " +0x71d1: "Tong " +0x71d2: "Shao " +0x71d3: "Fen " +0x71d4: "Fan " +0x71d5: "Yan " +0x71d6: "Xun " +0x71d7: "Lan " +0x71d8: "Mei " +0x71d9: "Tang " +0x71da: "Yi " +0x71db: "Jing " +0x71dc: "Men " +0x71dd: "[?] " +0x71de: "[?] " +0x71df: "Ying " +0x71e0: "Yu " +0x71e1: "Yi " +0x71e2: "Xue " +0x71e3: "Lan " +0x71e4: "Tai " +0x71e5: "Zao " +0x71e6: "Can " +0x71e7: "Sui " +0x71e8: "Xi " +0x71e9: "Que " +0x71ea: "Cong " +0x71eb: "Lian " +0x71ec: "Hui " +0x71ed: "Zhu " +0x71ee: "Xie " +0x71ef: "Ling " +0x71f0: "Wei " +0x71f1: "Yi " +0x71f2: "Xie " +0x71f3: "Zhao " +0x71f4: "Hui " +0x71f5: "Tatsu " +0x71f6: "Nung " +0x71f7: "Lan " +0x71f8: "Ru " +0x71f9: "Xian " +0x71fa: "Kao " +0x71fb: "Xun " +0x71fc: "Jin " +0x71fd: "Chou " +0x71fe: "Chou " +0x71ff: "Yao " +/* x072 */ +0x7200: "He " +0x7201: "Lan " +0x7202: "Biao " +0x7203: "Rong " +0x7204: "Li " +0x7205: "Mo " +0x7206: "Bao " +0x7207: "Ruo " +0x7208: "Lu " +0x7209: "La " +0x720a: "Ao " +0x720b: "Xun " +0x720c: "Kuang " +0x720d: "Shuo " +0x720e: "[?] " +0x720f: "Li " +0x7210: "Lu " +0x7211: "Jue " +0x7212: "Liao " +0x7213: "Yan " +0x7214: "Xi " +0x7215: "Xie " +0x7216: "Long " +0x7217: "Ye " +0x7218: "[?] " +0x7219: "Rang " +0x721a: "Yue " +0x721b: "Lan " +0x721c: "Cong " +0x721d: "Jue " +0x721e: "Tong " +0x721f: "Guan " +0x7220: "[?] " +0x7221: "Che " +0x7222: "Mi " +0x7223: "Tang " +0x7224: "Lan " +0x7225: "Zhu " +0x7226: "[?] " +0x7227: "Ling " +0x7228: "Cuan " +0x7229: "Yu " +0x722a: "Zhua " +0x722b: "Tsumekanmuri " +0x722c: "Pa " +0x722d: "Zheng " +0x722e: "Pao " +0x722f: "Cheng " +0x7230: "Yuan " +0x7231: "Ai " +0x7232: "Wei " +0x7233: "[?] " +0x7234: "Jue " +0x7235: "Jue " +0x7236: "Fu " +0x7237: "Ye " +0x7238: "Ba " +0x7239: "Die " +0x723a: "Ye " +0x723b: "Yao " +0x723c: "Zu " +0x723d: "Shuang " +0x723e: "Er " +0x723f: "Qiang " +0x7240: "Chuang " +0x7241: "Ge " +0x7242: "Zang " +0x7243: "Die " +0x7244: "Qiang " +0x7245: "Yong " +0x7246: "Qiang " +0x7247: "Pian " +0x7248: "Ban " +0x7249: "Pan " +0x724a: "Shao " +0x724b: "Jian " +0x724c: "Pai " +0x724d: "Du " +0x724e: "Chuang " +0x724f: "Tou " +0x7250: "Zha " +0x7251: "Bian " +0x7252: "Die " +0x7253: "Bang " +0x7254: "Bo " +0x7255: "Chuang " +0x7256: "You " +0x7257: "[?] " +0x7258: "Du " +0x7259: "Ya " +0x725a: "Cheng " +0x725b: "Niu " +0x725c: "Ushihen " +0x725d: "Pin " +0x725e: "Jiu " +0x725f: "Mou " +0x7260: "Tuo " +0x7261: "Mu " +0x7262: "Lao " +0x7263: "Ren " +0x7264: "Mang " +0x7265: "Fang " +0x7266: "Mao " +0x7267: "Mu " +0x7268: "Gang " +0x7269: "Wu " +0x726a: "Yan " +0x726b: "Ge " +0x726c: "Bei " +0x726d: "Si " +0x726e: "Jian " +0x726f: "Gu " +0x7270: "You " +0x7271: "Ge " +0x7272: "Sheng " +0x7273: "Mu " +0x7274: "Di " +0x7275: "Qian " +0x7276: "Quan " +0x7277: "Quan " +0x7278: "Zi " +0x7279: "Te " +0x727a: "Xi " +0x727b: "Mang " +0x727c: "Keng " +0x727d: "Qian " +0x727e: "Wu " +0x727f: "Gu " +0x7280: "Xi " +0x7281: "Li " +0x7282: "Li " +0x7283: "Pou " +0x7284: "Ji " +0x7285: "Gang " +0x7286: "Zhi " +0x7287: "Ben " +0x7288: "Quan " +0x7289: "Run " +0x728a: "Du " +0x728b: "Ju " +0x728c: "Jia " +0x728d: "Jian " +0x728e: "Feng " +0x728f: "Pian " +0x7290: "Ke " +0x7291: "Ju " +0x7292: "Kao " +0x7293: "Chu " +0x7294: "Xi " +0x7295: "Bei " +0x7296: "Luo " +0x7297: "Jie " +0x7298: "Ma " +0x7299: "San " +0x729a: "Wei " +0x729b: "Li " +0x729c: "Dun " +0x729d: "Tong " +0x729e: "[?] " +0x729f: "Jiang " +0x72a0: "Ikenie " +0x72a1: "Li " +0x72a2: "Du " +0x72a3: "Lie " +0x72a4: "Pi " +0x72a5: "Piao " +0x72a6: "Bao " +0x72a7: "Xi " +0x72a8: "Chou " +0x72a9: "Wei " +0x72aa: "Kui " +0x72ab: "Chou " +0x72ac: "Quan " +0x72ad: "Fan " +0x72ae: "Ba " +0x72af: "Fan " +0x72b0: "Qiu " +0x72b1: "Ji " +0x72b2: "Cai " +0x72b3: "Chuo " +0x72b4: "An " +0x72b5: "Jie " +0x72b6: "Zhuang " +0x72b7: "Guang " +0x72b8: "Ma " +0x72b9: "You " +0x72ba: "Kang " +0x72bb: "Bo " +0x72bc: "Hou " +0x72bd: "Ya " +0x72be: "Yin " +0x72bf: "Huan " +0x72c0: "Zhuang " +0x72c1: "Yun " +0x72c2: "Kuang " +0x72c3: "Niu " +0x72c4: "Di " +0x72c5: "Qing " +0x72c6: "Zhong " +0x72c7: "Mu " +0x72c8: "Bei " +0x72c9: "Pi " +0x72ca: "Ju " +0x72cb: "Ni " +0x72cc: "Sheng " +0x72cd: "Pao " +0x72ce: "Xia " +0x72cf: "Tuo " +0x72d0: "Hu " +0x72d1: "Ling " +0x72d2: "Fei " +0x72d3: "Pi " +0x72d4: "Ni " +0x72d5: "Ao " +0x72d6: "You " +0x72d7: "Gou " +0x72d8: "Yue " +0x72d9: "Ju " +0x72da: "Dan " +0x72db: "Po " +0x72dc: "Gu " +0x72dd: "Xian " +0x72de: "Ning " +0x72df: "Huan " +0x72e0: "Hen " +0x72e1: "Jiao " +0x72e2: "He " +0x72e3: "Zhao " +0x72e4: "Ji " +0x72e5: "Xun " +0x72e6: "Shan " +0x72e7: "Ta " +0x72e8: "Rong " +0x72e9: "Shou " +0x72ea: "Tong " +0x72eb: "Lao " +0x72ec: "Du " +0x72ed: "Xia " +0x72ee: "Shi " +0x72ef: "Hua " +0x72f0: "Zheng " +0x72f1: "Yu " +0x72f2: "Sun " +0x72f3: "Yu " +0x72f4: "Bi " +0x72f5: "Mang " +0x72f6: "Xi " +0x72f7: "Juan " +0x72f8: "Li " +0x72f9: "Xia " +0x72fa: "Yin " +0x72fb: "Suan " +0x72fc: "Lang " +0x72fd: "Bei " +0x72fe: "Zhi " +0x72ff: "Yan " +/* x073 */ +0x7300: "Sha " +0x7301: "Li " +0x7302: "Han " +0x7303: "Xian " +0x7304: "Jing " +0x7305: "Pai " +0x7306: "Fei " +0x7307: "Yao " +0x7308: "Ba " +0x7309: "Qi " +0x730a: "Ni " +0x730b: "Biao " +0x730c: "Yin " +0x730d: "Lai " +0x730e: "Xi " +0x730f: "Jian " +0x7310: "Qiang " +0x7311: "Kun " +0x7312: "Yan " +0x7313: "Guo " +0x7314: "Zong " +0x7315: "Mi " +0x7316: "Chang " +0x7317: "Yi " +0x7318: "Zhi " +0x7319: "Zheng " +0x731a: "Ya " +0x731b: "Meng " +0x731c: "Cai " +0x731d: "Cu " +0x731e: "She " +0x731f: "Kari " +0x7320: "Cen " +0x7321: "Luo " +0x7322: "Hu " +0x7323: "Zong " +0x7324: "Ji " +0x7325: "Wei " +0x7326: "Feng " +0x7327: "Wo " +0x7328: "Yuan " +0x7329: "Xing " +0x732a: "Zhu " +0x732b: "Mao " +0x732c: "Wei " +0x732d: "Yuan " +0x732e: "Xian " +0x732f: "Tuan " +0x7330: "Ya " +0x7331: "Nao " +0x7332: "Xie " +0x7333: "Jia " +0x7334: "Hou " +0x7335: "Bian " +0x7336: "You " +0x7337: "You " +0x7338: "Mei " +0x7339: "Zha " +0x733a: "Yao " +0x733b: "Sun " +0x733c: "Bo " +0x733d: "Ming " +0x733e: "Hua " +0x733f: "Yuan " +0x7340: "Sou " +0x7341: "Ma " +0x7342: "Yuan " +0x7343: "Dai " +0x7344: "Yu " +0x7345: "Shi " +0x7346: "Hao " +0x7347: "[?] " +0x7348: "Yi " +0x7349: "Zhen " +0x734a: "Chuang " +0x734b: "Hao " +0x734c: "Man " +0x734d: "Jing " +0x734e: "Jiang " +0x734f: "Mu " +0x7350: "Zhang " +0x7351: "Chan " +0x7352: "Ao " +0x7353: "Ao " +0x7354: "Hao " +0x7355: "Cui " +0x7356: "Fen " +0x7357: "Jue " +0x7358: "Bi " +0x7359: "Bi " +0x735a: "Huang " +0x735b: "Pu " +0x735c: "Lin " +0x735d: "Yu " +0x735e: "Tong " +0x735f: "Yao " +0x7360: "Liao " +0x7361: "Shuo " +0x7362: "Xiao " +0x7363: "Swu " +0x7364: "Ton " +0x7365: "Xi " +0x7366: "Ge " +0x7367: "Juan " +0x7368: "Du " +0x7369: "Hui " +0x736a: "Kuai " +0x736b: "Xian " +0x736c: "Xie " +0x736d: "Ta " +0x736e: "Xian " +0x736f: "Xun " +0x7370: "Ning " +0x7371: "Pin " +0x7372: "Huo " +0x7373: "Nou " +0x7374: "Meng " +0x7375: "Lie " +0x7376: "Nao " +0x7377: "Guang " +0x7378: "Shou " +0x7379: "Lu " +0x737a: "Ta " +0x737b: "Xian " +0x737c: "Mi " +0x737d: "Rang " +0x737e: "Huan " +0x737f: "Nao " +0x7380: "Luo " +0x7381: "Xian " +0x7382: "Qi " +0x7383: "Jue " +0x7384: "Xuan " +0x7385: "Miao " +0x7386: "Zi " +0x7387: "Lu " +0x7388: "Lu " +0x7389: "Yu " +0x738a: "Su " +0x738b: "Wang " +0x738c: "Qiu " +0x738d: "Ga " +0x738e: "Ding " +0x738f: "Le " +0x7390: "Ba " +0x7391: "Ji " +0x7392: "Hong " +0x7393: "Di " +0x7394: "Quan " +0x7395: "Gan " +0x7396: "Jiu " +0x7397: "Yu " +0x7398: "Ji " +0x7399: "Yu " +0x739a: "Yang " +0x739b: "Ma " +0x739c: "Gong " +0x739d: "Wu " +0x739e: "Fu " +0x739f: "Wen " +0x73a0: "Jie " +0x73a1: "Ya " +0x73a2: "Fen " +0x73a3: "Bian " +0x73a4: "Beng " +0x73a5: "Yue " +0x73a6: "Jue " +0x73a7: "Yun " +0x73a8: "Jue " +0x73a9: "Wan " +0x73aa: "Jian " +0x73ab: "Mei " +0x73ac: "Dan " +0x73ad: "Pi " +0x73ae: "Wei " +0x73af: "Huan " +0x73b0: "Xian " +0x73b1: "Qiang " +0x73b2: "Ling " +0x73b3: "Dai " +0x73b4: "Yi " +0x73b5: "An " +0x73b6: "Ping " +0x73b7: "Dian " +0x73b8: "Fu " +0x73b9: "Xuan " +0x73ba: "Xi " +0x73bb: "Bo " +0x73bc: "Ci " +0x73bd: "Gou " +0x73be: "Jia " +0x73bf: "Shao " +0x73c0: "Po " +0x73c1: "Ci " +0x73c2: "Ke " +0x73c3: "Ran " +0x73c4: "Sheng " +0x73c5: "Shen " +0x73c6: "Yi " +0x73c7: "Zu " +0x73c8: "Jia " +0x73c9: "Min " +0x73ca: "Shan " +0x73cb: "Liu " +0x73cc: "Bi " +0x73cd: "Zhen " +0x73ce: "Zhen " +0x73cf: "Jue " +0x73d0: "Fa " +0x73d1: "Long " +0x73d2: "Jin " +0x73d3: "Jiao " +0x73d4: "Jian " +0x73d5: "Li " +0x73d6: "Guang " +0x73d7: "Xian " +0x73d8: "Zhou " +0x73d9: "Gong " +0x73da: "Yan " +0x73db: "Xiu " +0x73dc: "Yang " +0x73dd: "Xu " +0x73de: "Luo " +0x73df: "Su " +0x73e0: "Zhu " +0x73e1: "Qin " +0x73e2: "Ken " +0x73e3: "Xun " +0x73e4: "Bao " +0x73e5: "Er " +0x73e6: "Xiang " +0x73e7: "Yao " +0x73e8: "Xia " +0x73e9: "Heng " +0x73ea: "Gui " +0x73eb: "Chong " +0x73ec: "Xu " +0x73ed: "Ban " +0x73ee: "Pei " +0x73ef: "[?] " +0x73f0: "Dang " +0x73f1: "Ei " +0x73f2: "Hun " +0x73f3: "Wen " +0x73f4: "E " +0x73f5: "Cheng " +0x73f6: "Ti " +0x73f7: "Wu " +0x73f8: "Wu " +0x73f9: "Cheng " +0x73fa: "Jun " +0x73fb: "Mei " +0x73fc: "Bei " +0x73fd: "Ting " +0x73fe: "Xian " +0x73ff: "Chuo " +/* x074 */ +0x7400: "Han " +0x7401: "Xuan " +0x7402: "Yan " +0x7403: "Qiu " +0x7404: "Quan " +0x7405: "Lang " +0x7406: "Li " +0x7407: "Xiu " +0x7408: "Fu " +0x7409: "Liu " +0x740a: "Ye " +0x740b: "Xi " +0x740c: "Ling " +0x740d: "Li " +0x740e: "Jin " +0x740f: "Lian " +0x7410: "Suo " +0x7411: "Chiisai " +0x7412: "[?] " +0x7413: "Wan " +0x7414: "Dian " +0x7415: "Pin " +0x7416: "Zhan " +0x7417: "Cui " +0x7418: "Min " +0x7419: "Yu " +0x741a: "Ju " +0x741b: "Chen " +0x741c: "Lai " +0x741d: "Wen " +0x741e: "Sheng " +0x741f: "Wei " +0x7420: "Dian " +0x7421: "Chu " +0x7422: "Zhuo " +0x7423: "Pei " +0x7424: "Cheng " +0x7425: "Hu " +0x7426: "Qi " +0x7427: "E " +0x7428: "Kun " +0x7429: "Chang " +0x742a: "Qi " +0x742b: "Beng " +0x742c: "Wan " +0x742d: "Lu " +0x742e: "Cong " +0x742f: "Guan " +0x7430: "Yan " +0x7431: "Diao " +0x7432: "Bei " +0x7433: "Lin " +0x7434: "Qin " +0x7435: "Pi " +0x7436: "Pa " +0x7437: "Que " +0x7438: "Zhuo " +0x7439: "Qin " +0x743a: "Fa " +0x743b: "[?] " +0x743c: "Qiong " +0x743d: "Du " +0x743e: "Jie " +0x743f: "Hun " +0x7440: "Yu " +0x7441: "Mao " +0x7442: "Mei " +0x7443: "Chun " +0x7444: "Xuan " +0x7445: "Ti " +0x7446: "Xing " +0x7447: "Dai " +0x7448: "Rou " +0x7449: "Min " +0x744a: "Zhen " +0x744b: "Wei " +0x744c: "Ruan " +0x744d: "Huan " +0x744e: "Jie " +0x744f: "Chuan " +0x7450: "Jian " +0x7451: "Zhuan " +0x7452: "Yang " +0x7453: "Lian " +0x7454: "Quan " +0x7455: "Xia " +0x7456: "Duan " +0x7457: "Yuan " +0x7458: "Ye " +0x7459: "Nao " +0x745a: "Hu " +0x745b: "Ying " +0x745c: "Yu " +0x745d: "Huang " +0x745e: "Rui " +0x745f: "Se " +0x7460: "Liu " +0x7461: "Shi " +0x7462: "Rong " +0x7463: "Suo " +0x7464: "Yao " +0x7465: "Wen " +0x7466: "Wu " +0x7467: "Jin " +0x7468: "Jin " +0x7469: "Ying " +0x746a: "Ma " +0x746b: "Tao " +0x746c: "Liu " +0x746d: "Tang " +0x746e: "Li " +0x746f: "Lang " +0x7470: "Gui " +0x7471: "Zhen " +0x7472: "Qiang " +0x7473: "Cuo " +0x7474: "Jue " +0x7475: "Zhao " +0x7476: "Yao " +0x7477: "Ai " +0x7478: "Bin " +0x7479: "Tu " +0x747a: "Chang " +0x747b: "Kun " +0x747c: "Zhuan " +0x747d: "Cong " +0x747e: "Jin " +0x747f: "Yi " +0x7480: "Cui " +0x7481: "Cong " +0x7482: "Qi " +0x7483: "Li " +0x7484: "Ying " +0x7485: "Suo " +0x7486: "Qiu " +0x7487: "Xuan " +0x7488: "Ao " +0x7489: "Lian " +0x748a: "Man " +0x748b: "Zhang " +0x748c: "Yin " +0x748d: "[?] " +0x748e: "Ying " +0x748f: "Zhi " +0x7490: "Lu " +0x7491: "Wu " +0x7492: "Deng " +0x7493: "Xiou " +0x7494: "Zeng " +0x7495: "Xun " +0x7496: "Qu " +0x7497: "Dang " +0x7498: "Lin " +0x7499: "Liao " +0x749a: "Qiong " +0x749b: "Su " +0x749c: "Huang " +0x749d: "Gui " +0x749e: "Pu " +0x749f: "Jing " +0x74a0: "Fan " +0x74a1: "Jin " +0x74a2: "Liu " +0x74a3: "Ji " +0x74a4: "[?] " +0x74a5: "Jing " +0x74a6: "Ai " +0x74a7: "Bi " +0x74a8: "Can " +0x74a9: "Qu " +0x74aa: "Zao " +0x74ab: "Dang " +0x74ac: "Jiao " +0x74ad: "Gun " +0x74ae: "Tan " +0x74af: "Hui " +0x74b0: "Huan " +0x74b1: "Se " +0x74b2: "Sui " +0x74b3: "Tian " +0x74b4: "[?] " +0x74b5: "Yu " +0x74b6: "Jin " +0x74b7: "Lu " +0x74b8: "Bin " +0x74b9: "Shou " +0x74ba: "Wen " +0x74bb: "Zui " +0x74bc: "Lan " +0x74bd: "Xi " +0x74be: "Ji " +0x74bf: "Xuan " +0x74c0: "Ruan " +0x74c1: "Huo " +0x74c2: "Gai " +0x74c3: "Lei " +0x74c4: "Du " +0x74c5: "Li " +0x74c6: "Zhi " +0x74c7: "Rou " +0x74c8: "Li " +0x74c9: "Zan " +0x74ca: "Qiong " +0x74cb: "Zhe " +0x74cc: "Gui " +0x74cd: "Sui " +0x74ce: "La " +0x74cf: "Long " +0x74d0: "Lu " +0x74d1: "Li " +0x74d2: "Zan " +0x74d3: "Lan " +0x74d4: "Ying " +0x74d5: "Mi " +0x74d6: "Xiang " +0x74d7: "Xi " +0x74d8: "Guan " +0x74d9: "Dao " +0x74da: "Zan " +0x74db: "Huan " +0x74dc: "Gua " +0x74dd: "Bo " +0x74de: "Die " +0x74df: "Bao " +0x74e0: "Hu " +0x74e1: "Zhi " +0x74e2: "Piao " +0x74e3: "Ban " +0x74e4: "Rang " +0x74e5: "Li " +0x74e6: "Wa " +0x74e7: "Dekaguramu " +0x74e8: "Jiang " +0x74e9: "Qian " +0x74ea: "Fan " +0x74eb: "Pen " +0x74ec: "Fang " +0x74ed: "Dan " +0x74ee: "Weng " +0x74ef: "Ou " +0x74f0: "Deshiguramu " +0x74f1: "Miriguramu " +0x74f2: "Thon " +0x74f3: "Hu " +0x74f4: "Ling " +0x74f5: "Yi " +0x74f6: "Ping " +0x74f7: "Ci " +0x74f8: "Hekutogura " +0x74f9: "Juan " +0x74fa: "Chang " +0x74fb: "Chi " +0x74fc: "Sarake " +0x74fd: "Dang " +0x74fe: "Meng " +0x74ff: "Pou " +/* x075 */ +0x7500: "Zhui " +0x7501: "Ping " +0x7502: "Bian " +0x7503: "Zhou " +0x7504: "Zhen " +0x7505: "Senchigura " +0x7506: "Ci " +0x7507: "Ying " +0x7508: "Qi " +0x7509: "Xian " +0x750a: "Lou " +0x750b: "Di " +0x750c: "Ou " +0x750d: "Meng " +0x750e: "Zhuan " +0x750f: "Peng " +0x7510: "Lin " +0x7511: "Zeng " +0x7512: "Wu " +0x7513: "Pi " +0x7514: "Dan " +0x7515: "Weng " +0x7516: "Ying " +0x7517: "Yan " +0x7518: "Gan " +0x7519: "Dai " +0x751a: "Shen " +0x751b: "Tian " +0x751c: "Tian " +0x751d: "Han " +0x751e: "Chang " +0x751f: "Sheng " +0x7520: "Qing " +0x7521: "Sheng " +0x7522: "Chan " +0x7523: "Chan " +0x7524: "Rui " +0x7525: "Sheng " +0x7526: "Su " +0x7527: "Sen " +0x7528: "Yong " +0x7529: "Shuai " +0x752a: "Lu " +0x752b: "Fu " +0x752c: "Yong " +0x752d: "Beng " +0x752e: "Feng " +0x752f: "Ning " +0x7530: "Tian " +0x7531: "You " +0x7532: "Jia " +0x7533: "Shen " +0x7534: "Zha " +0x7535: "Dian " +0x7536: "Fu " +0x7537: "Nan " +0x7538: "Dian " +0x7539: "Ping " +0x753a: "Ting " +0x753b: "Hua " +0x753c: "Ting " +0x753d: "Quan " +0x753e: "Zi " +0x753f: "Meng " +0x7540: "Bi " +0x7541: "Qi " +0x7542: "Liu " +0x7543: "Xun " +0x7544: "Liu " +0x7545: "Chang " +0x7546: "Mu " +0x7547: "Yun " +0x7548: "Fan " +0x7549: "Fu " +0x754a: "Geng " +0x754b: "Tian " +0x754c: "Jie " +0x754d: "Jie " +0x754e: "Quan " +0x754f: "Wei " +0x7550: "Fu " +0x7551: "Tian " +0x7552: "Mu " +0x7553: "Tap " +0x7554: "Pan " +0x7555: "Jiang " +0x7556: "Wa " +0x7557: "Da " +0x7558: "Nan " +0x7559: "Liu " +0x755a: "Ben " +0x755b: "Zhen " +0x755c: "Chu " +0x755d: "Mu " +0x755e: "Mu " +0x755f: "Ce " +0x7560: "Cen " +0x7561: "Gai " +0x7562: "Bi " +0x7563: "Da " +0x7564: "Zhi " +0x7565: "Lue " +0x7566: "Qi " +0x7567: "Lue " +0x7568: "Pan " +0x7569: "Kesa " +0x756a: "Fan " +0x756b: "Hua " +0x756c: "Yu " +0x756d: "Yu " +0x756e: "Mu " +0x756f: "Jun " +0x7570: "Yi " +0x7571: "Liu " +0x7572: "Yu " +0x7573: "Die " +0x7574: "Chou " +0x7575: "Hua " +0x7576: "Dang " +0x7577: "Chuo " +0x7578: "Ji " +0x7579: "Wan " +0x757a: "Jiang " +0x757b: "Sheng " +0x757c: "Chang " +0x757d: "Tuan " +0x757e: "Lei " +0x757f: "Ji " +0x7580: "Cha " +0x7581: "Liu " +0x7582: "Tatamu " +0x7583: "Tuan " +0x7584: "Lin " +0x7585: "Jiang " +0x7586: "Jiang " +0x7587: "Chou " +0x7588: "Bo " +0x7589: "Die " +0x758a: "Die " +0x758b: "Pi " +0x758c: "Nie " +0x758d: "Dan " +0x758e: "Shu " +0x758f: "Shu " +0x7590: "Zhi " +0x7591: "Yi " +0x7592: "Chuang " +0x7593: "Nai " +0x7594: "Ding " +0x7595: "Bi " +0x7596: "Jie " +0x7597: "Liao " +0x7598: "Gong " +0x7599: "Ge " +0x759a: "Jiu " +0x759b: "Zhou " +0x759c: "Xia " +0x759d: "Shan " +0x759e: "Xu " +0x759f: "Nue " +0x75a0: "Li " +0x75a1: "Yang " +0x75a2: "Chen " +0x75a3: "You " +0x75a4: "Ba " +0x75a5: "Jie " +0x75a6: "Jue " +0x75a7: "Zhi " +0x75a8: "Xia " +0x75a9: "Cui " +0x75aa: "Bi " +0x75ab: "Yi " +0x75ac: "Li " +0x75ad: "Zong " +0x75ae: "Chuang " +0x75af: "Feng " +0x75b0: "Zhu " +0x75b1: "Pao " +0x75b2: "Pi " +0x75b3: "Gan " +0x75b4: "Ke " +0x75b5: "Ci " +0x75b6: "Xie " +0x75b7: "Qi " +0x75b8: "Dan " +0x75b9: "Zhen " +0x75ba: "Fa " +0x75bb: "Zhi " +0x75bc: "Teng " +0x75bd: "Ju " +0x75be: "Ji " +0x75bf: "Fei " +0x75c0: "Qu " +0x75c1: "Dian " +0x75c2: "Jia " +0x75c3: "Xian " +0x75c4: "Cha " +0x75c5: "Bing " +0x75c6: "Ni " +0x75c7: "Zheng " +0x75c8: "Yong " +0x75c9: "Jing " +0x75ca: "Quan " +0x75cb: "Chong " +0x75cc: "Tong " +0x75cd: "Yi " +0x75ce: "Kai " +0x75cf: "Wei " +0x75d0: "Hui " +0x75d1: "Duo " +0x75d2: "Yang " +0x75d3: "Chi " +0x75d4: "Zhi " +0x75d5: "Hen " +0x75d6: "Ya " +0x75d7: "Mei " +0x75d8: "Dou " +0x75d9: "Jing " +0x75da: "Xiao " +0x75db: "Tong " +0x75dc: "Tu " +0x75dd: "Mang " +0x75de: "Pi " +0x75df: "Xiao " +0x75e0: "Suan " +0x75e1: "Pu " +0x75e2: "Li " +0x75e3: "Zhi " +0x75e4: "Cuo " +0x75e5: "Duo " +0x75e6: "Wu " +0x75e7: "Sha " +0x75e8: "Lao " +0x75e9: "Shou " +0x75ea: "Huan " +0x75eb: "Xian " +0x75ec: "Yi " +0x75ed: "Peng " +0x75ee: "Zhang " +0x75ef: "Guan " +0x75f0: "Tan " +0x75f1: "Fei " +0x75f2: "Ma " +0x75f3: "Lin " +0x75f4: "Chi " +0x75f5: "Ji " +0x75f6: "Dian " +0x75f7: "An " +0x75f8: "Chi " +0x75f9: "Bi " +0x75fa: "Bei " +0x75fb: "Min " +0x75fc: "Gu " +0x75fd: "Dui " +0x75fe: "E " +0x75ff: "Wei " +/* x076 */ +0x7600: "Yu " +0x7601: "Cui " +0x7602: "Ya " +0x7603: "Zhu " +0x7604: "Cu " +0x7605: "Dan " +0x7606: "Shen " +0x7607: "Zhung " +0x7608: "Ji " +0x7609: "Yu " +0x760a: "Hou " +0x760b: "Feng " +0x760c: "La " +0x760d: "Yang " +0x760e: "Shen " +0x760f: "Tu " +0x7610: "Yu " +0x7611: "Gua " +0x7612: "Wen " +0x7613: "Huan " +0x7614: "Ku " +0x7615: "Jia " +0x7616: "Yin " +0x7617: "Yi " +0x7618: "Lu " +0x7619: "Sao " +0x761a: "Jue " +0x761b: "Chi " +0x761c: "Xi " +0x761d: "Guan " +0x761e: "Yi " +0x761f: "Wen " +0x7620: "Ji " +0x7621: "Chuang " +0x7622: "Ban " +0x7623: "Lei " +0x7624: "Liu " +0x7625: "Chai " +0x7626: "Shou " +0x7627: "Nue " +0x7628: "Dian " +0x7629: "Da " +0x762a: "Pie " +0x762b: "Tan " +0x762c: "Zhang " +0x762d: "Biao " +0x762e: "Shen " +0x762f: "Cu " +0x7630: "Luo " +0x7631: "Yi " +0x7632: "Zong " +0x7633: "Chou " +0x7634: "Zhang " +0x7635: "Zhai " +0x7636: "Sou " +0x7637: "Suo " +0x7638: "Que " +0x7639: "Diao " +0x763a: "Lou " +0x763b: "Lu " +0x763c: "Mo " +0x763d: "Jin " +0x763e: "Yin " +0x763f: "Ying " +0x7640: "Huang " +0x7641: "Fu " +0x7642: "Liao " +0x7643: "Long " +0x7644: "Qiao " +0x7645: "Liu " +0x7646: "Lao " +0x7647: "Xian " +0x7648: "Fei " +0x7649: "Dan " +0x764a: "Yin " +0x764b: "He " +0x764c: "Yan " +0x764d: "Ban " +0x764e: "Xian " +0x764f: "Guan " +0x7650: "Guai " +0x7651: "Nong " +0x7652: "Yu " +0x7653: "Wei " +0x7654: "Yi " +0x7655: "Yong " +0x7656: "Pi " +0x7657: "Lei " +0x7658: "Li " +0x7659: "Shu " +0x765a: "Dan " +0x765b: "Lin " +0x765c: "Dian " +0x765d: "Lin " +0x765e: "Lai " +0x765f: "Pie " +0x7660: "Ji " +0x7661: "Chi " +0x7662: "Yang " +0x7663: "Xian " +0x7664: "Jie " +0x7665: "Zheng " +0x7666: "[?] " +0x7667: "Li " +0x7668: "Huo " +0x7669: "Lai " +0x766a: "Shaku " +0x766b: "Dian " +0x766c: "Xian " +0x766d: "Ying " +0x766e: "Yin " +0x766f: "Qu " +0x7670: "Yong " +0x7671: "Tan " +0x7672: "Dian " +0x7673: "Luo " +0x7674: "Luan " +0x7675: "Luan " +0x7676: "Bo " +0x7677: "[?] " +0x7678: "Gui " +0x7679: "Po " +0x767a: "Fa " +0x767b: "Deng " +0x767c: "Fa " +0x767d: "Bai " +0x767e: "Bai " +0x767f: "Qie " +0x7680: "Bi " +0x7681: "Zao " +0x7682: "Zao " +0x7683: "Mao " +0x7684: "De " +0x7685: "Pa " +0x7686: "Jie " +0x7687: "Huang " +0x7688: "Gui " +0x7689: "Ci " +0x768a: "Ling " +0x768b: "Gao " +0x768c: "Mo " +0x768d: "Ji " +0x768e: "Jiao " +0x768f: "Peng " +0x7690: "Gao " +0x7691: "Ai " +0x7692: "E " +0x7693: "Hao " +0x7694: "Han " +0x7695: "Bi " +0x7696: "Wan " +0x7697: "Chou " +0x7698: "Qian " +0x7699: "Xi " +0x769a: "Ai " +0x769b: "Jiong " +0x769c: "Hao " +0x769d: "Huang " +0x769e: "Hao " +0x769f: "Ze " +0x76a0: "Cui " +0x76a1: "Hao " +0x76a2: "Xiao " +0x76a3: "Ye " +0x76a4: "Po " +0x76a5: "Hao " +0x76a6: "Jiao " +0x76a7: "Ai " +0x76a8: "Xing " +0x76a9: "Huang " +0x76aa: "Li " +0x76ab: "Piao " +0x76ac: "He " +0x76ad: "Jiao " +0x76ae: "Pi " +0x76af: "Gan " +0x76b0: "Pao " +0x76b1: "Zhou " +0x76b2: "Jun " +0x76b3: "Qiu " +0x76b4: "Cun " +0x76b5: "Que " +0x76b6: "Zha " +0x76b7: "Gu " +0x76b8: "Jun " +0x76b9: "Jun " +0x76ba: "Zhou " +0x76bb: "Zha " +0x76bc: "Gu " +0x76bd: "Zhan " +0x76be: "Du " +0x76bf: "Min " +0x76c0: "Qi " +0x76c1: "Ying " +0x76c2: "Yu " +0x76c3: "Bei " +0x76c4: "Zhao " +0x76c5: "Zhong " +0x76c6: "Pen " +0x76c7: "He " +0x76c8: "Ying " +0x76c9: "He " +0x76ca: "Yi " +0x76cb: "Bo " +0x76cc: "Wan " +0x76cd: "He " +0x76ce: "Ang " +0x76cf: "Zhan " +0x76d0: "Yan " +0x76d1: "Jian " +0x76d2: "He " +0x76d3: "Yu " +0x76d4: "Kui " +0x76d5: "Fan " +0x76d6: "Gai " +0x76d7: "Dao " +0x76d8: "Pan " +0x76d9: "Fu " +0x76da: "Qiu " +0x76db: "Sheng " +0x76dc: "Dao " +0x76dd: "Lu " +0x76de: "Zhan " +0x76df: "Meng " +0x76e0: "Li " +0x76e1: "Jin " +0x76e2: "Xu " +0x76e3: "Jian " +0x76e4: "Pan " +0x76e5: "Guan " +0x76e6: "An " +0x76e7: "Lu " +0x76e8: "Shu " +0x76e9: "Zhou " +0x76ea: "Dang " +0x76eb: "An " +0x76ec: "Gu " +0x76ed: "Li " +0x76ee: "Mu " +0x76ef: "Cheng " +0x76f0: "Gan " +0x76f1: "Xu " +0x76f2: "Mang " +0x76f3: "Mang " +0x76f4: "Zhi " +0x76f5: "Qi " +0x76f6: "Ruan " +0x76f7: "Tian " +0x76f8: "Xiang " +0x76f9: "Dun " +0x76fa: "Xin " +0x76fb: "Xi " +0x76fc: "Pan " +0x76fd: "Feng " +0x76fe: "Dun " +0x76ff: "Min " +/* x077 */ +0x7700: "Ming " +0x7701: "Sheng " +0x7702: "Shi " +0x7703: "Yun " +0x7704: "Mian " +0x7705: "Pan " +0x7706: "Fang " +0x7707: "Miao " +0x7708: "Dan " +0x7709: "Mei " +0x770a: "Mao " +0x770b: "Kan " +0x770c: "Xian " +0x770d: "Ou " +0x770e: "Shi " +0x770f: "Yang " +0x7710: "Zheng " +0x7711: "Yao " +0x7712: "Shen " +0x7713: "Huo " +0x7714: "Da " +0x7715: "Zhen " +0x7716: "Kuang " +0x7717: "Ju " +0x7718: "Shen " +0x7719: "Chi " +0x771a: "Sheng " +0x771b: "Mei " +0x771c: "Mo " +0x771d: "Zhu " +0x771e: "Zhen " +0x771f: "Zhen " +0x7720: "Mian " +0x7721: "Di " +0x7722: "Yuan " +0x7723: "Die " +0x7724: "Yi " +0x7725: "Zi " +0x7726: "Zi " +0x7727: "Chao " +0x7728: "Zha " +0x7729: "Xuan " +0x772a: "Bing " +0x772b: "Mi " +0x772c: "Long " +0x772d: "Sui " +0x772e: "Dong " +0x772f: "Mi " +0x7730: "Die " +0x7731: "Yi " +0x7732: "Er " +0x7733: "Ming " +0x7734: "Xuan " +0x7735: "Chi " +0x7736: "Kuang " +0x7737: "Juan " +0x7738: "Mou " +0x7739: "Zhen " +0x773a: "Tiao " +0x773b: "Yang " +0x773c: "Yan " +0x773d: "Mo " +0x773e: "Zhong " +0x773f: "Mai " +0x7740: "Zhao " +0x7741: "Zheng " +0x7742: "Mei " +0x7743: "Jun " +0x7744: "Shao " +0x7745: "Han " +0x7746: "Huan " +0x7747: "Di " +0x7748: "Cheng " +0x7749: "Cuo " +0x774a: "Juan " +0x774b: "E " +0x774c: "Wan " +0x774d: "Xian " +0x774e: "Xi " +0x774f: "Kun " +0x7750: "Lai " +0x7751: "Jian " +0x7752: "Shan " +0x7753: "Tian " +0x7754: "Hun " +0x7755: "Wan " +0x7756: "Ling " +0x7757: "Shi " +0x7758: "Qiong " +0x7759: "Lie " +0x775a: "Yai " +0x775b: "Jing " +0x775c: "Zheng " +0x775d: "Li " +0x775e: "Lai " +0x775f: "Sui " +0x7760: "Juan " +0x7761: "Shui " +0x7762: "Sui " +0x7763: "Du " +0x7764: "Bi " +0x7765: "Bi " +0x7766: "Mu " +0x7767: "Hun " +0x7768: "Ni " +0x7769: "Lu " +0x776a: "Yi " +0x776b: "Jie " +0x776c: "Cai " +0x776d: "Zhou " +0x776e: "Yu " +0x776f: "Hun " +0x7770: "Ma " +0x7771: "Xia " +0x7772: "Xing " +0x7773: "Xi " +0x7774: "Gun " +0x7775: "Cai " +0x7776: "Chun " +0x7777: "Jian " +0x7778: "Mei " +0x7779: "Du " +0x777a: "Hou " +0x777b: "Xuan " +0x777c: "Ti " +0x777d: "Kui " +0x777e: "Gao " +0x777f: "Rui " +0x7780: "Mou " +0x7781: "Xu " +0x7782: "Fa " +0x7783: "Wen " +0x7784: "Miao " +0x7785: "Chou " +0x7786: "Kui " +0x7787: "Mi " +0x7788: "Weng " +0x7789: "Kou " +0x778a: "Dang " +0x778b: "Chen " +0x778c: "Ke " +0x778d: "Sou " +0x778e: "Xia " +0x778f: "Qiong " +0x7790: "Mao " +0x7791: "Ming " +0x7792: "Man " +0x7793: "Shui " +0x7794: "Ze " +0x7795: "Zhang " +0x7796: "Yi " +0x7797: "Diao " +0x7798: "Ou " +0x7799: "Mo " +0x779a: "Shun " +0x779b: "Cong " +0x779c: "Lou " +0x779d: "Chi " +0x779e: "Man " +0x779f: "Piao " +0x77a0: "Cheng " +0x77a1: "Ji " +0x77a2: "Meng " +0x77a3: "[?] " +0x77a4: "Run " +0x77a5: "Pie " +0x77a6: "Xi " +0x77a7: "Qiao " +0x77a8: "Pu " +0x77a9: "Zhu " +0x77aa: "Deng " +0x77ab: "Shen " +0x77ac: "Shun " +0x77ad: "Liao " +0x77ae: "Che " +0x77af: "Xian " +0x77b0: "Kan " +0x77b1: "Ye " +0x77b2: "Xu " +0x77b3: "Tong " +0x77b4: "Mou " +0x77b5: "Lin " +0x77b6: "Kui " +0x77b7: "Xian " +0x77b8: "Ye " +0x77b9: "Ai " +0x77ba: "Hui " +0x77bb: "Zhan " +0x77bc: "Jian " +0x77bd: "Gu " +0x77be: "Zhao " +0x77bf: "Qu " +0x77c0: "Wei " +0x77c1: "Chou " +0x77c2: "Sao " +0x77c3: "Ning " +0x77c4: "Xun " +0x77c5: "Yao " +0x77c6: "Huo " +0x77c7: "Meng " +0x77c8: "Mian " +0x77c9: "Bin " +0x77ca: "Mian " +0x77cb: "Li " +0x77cc: "Kuang " +0x77cd: "Jue " +0x77ce: "Xuan " +0x77cf: "Mian " +0x77d0: "Huo " +0x77d1: "Lu " +0x77d2: "Meng " +0x77d3: "Long " +0x77d4: "Guan " +0x77d5: "Man " +0x77d6: "Xi " +0x77d7: "Chu " +0x77d8: "Tang " +0x77d9: "Kan " +0x77da: "Zhu " +0x77db: "Mao " +0x77dc: "Jin " +0x77dd: "Lin " +0x77de: "Yu " +0x77df: "Shuo " +0x77e0: "Ce " +0x77e1: "Jue " +0x77e2: "Shi " +0x77e3: "Yi " +0x77e4: "Shen " +0x77e5: "Zhi " +0x77e6: "Hou " +0x77e7: "Shen " +0x77e8: "Ying " +0x77e9: "Ju " +0x77ea: "Zhou " +0x77eb: "Jiao " +0x77ec: "Cuo " +0x77ed: "Duan " +0x77ee: "Ai " +0x77ef: "Jiao " +0x77f0: "Zeng " +0x77f1: "Huo " +0x77f2: "Bai " +0x77f3: "Shi " +0x77f4: "Ding " +0x77f5: "Qi " +0x77f6: "Ji " +0x77f7: "Zi " +0x77f8: "Gan " +0x77f9: "Wu " +0x77fa: "Tuo " +0x77fb: "Ku " +0x77fc: "Qiang " +0x77fd: "Xi " +0x77fe: "Fan " +0x77ff: "Kuang " +/* x078 */ +0x7800: "Dang " +0x7801: "Ma " +0x7802: "Sha " +0x7803: "Dan " +0x7804: "Jue " +0x7805: "Li " +0x7806: "Fu " +0x7807: "Min " +0x7808: "Nuo " +0x7809: "Huo " +0x780a: "Kang " +0x780b: "Zhi " +0x780c: "Qi " +0x780d: "Kan " +0x780e: "Jie " +0x780f: "Fen " +0x7810: "E " +0x7811: "Ya " +0x7812: "Pi " +0x7813: "Zhe " +0x7814: "Yan " +0x7815: "Sui " +0x7816: "Zhuan " +0x7817: "Che " +0x7818: "Dun " +0x7819: "Pan " +0x781a: "Yan " +0x781b: "[?] " +0x781c: "Feng " +0x781d: "Fa " +0x781e: "Mo " +0x781f: "Zha " +0x7820: "Qu " +0x7821: "Yu " +0x7822: "Luo " +0x7823: "Tuo " +0x7824: "Tuo " +0x7825: "Di " +0x7826: "Zhai " +0x7827: "Zhen " +0x7828: "Ai " +0x7829: "Fei " +0x782a: "Mu " +0x782b: "Zhu " +0x782c: "Li " +0x782d: "Bian " +0x782e: "Nu " +0x782f: "Ping " +0x7830: "Peng " +0x7831: "Ling " +0x7832: "Pao " +0x7833: "Le " +0x7834: "Po " +0x7835: "Bo " +0x7836: "Po " +0x7837: "Shen " +0x7838: "Za " +0x7839: "Nuo " +0x783a: "Li " +0x783b: "Long " +0x783c: "Tong " +0x783d: "[?] " +0x783e: "Li " +0x783f: "Aragane " +0x7840: "Chu " +0x7841: "Keng " +0x7842: "Quan " +0x7843: "Zhu " +0x7844: "Kuang " +0x7845: "Huo " +0x7846: "E " +0x7847: "Nao " +0x7848: "Jia " +0x7849: "Lu " +0x784a: "Wei " +0x784b: "Ai " +0x784c: "Luo " +0x784d: "Ken " +0x784e: "Xing " +0x784f: "Yan " +0x7850: "Tong " +0x7851: "Peng " +0x7852: "Xi " +0x7853: "[?] " +0x7854: "Hong " +0x7855: "Shuo " +0x7856: "Xia " +0x7857: "Qiao " +0x7858: "[?] " +0x7859: "Wei " +0x785a: "Qiao " +0x785b: "[?] " +0x785c: "Keng " +0x785d: "Xiao " +0x785e: "Que " +0x785f: "Chan " +0x7860: "Lang " +0x7861: "Hong " +0x7862: "Yu " +0x7863: "Xiao " +0x7864: "Xia " +0x7865: "Mang " +0x7866: "Long " +0x7867: "Iong " +0x7868: "Che " +0x7869: "Che " +0x786a: "E " +0x786b: "Liu " +0x786c: "Ying " +0x786d: "Mang " +0x786e: "Que " +0x786f: "Yan " +0x7870: "Sha " +0x7871: "Kun " +0x7872: "Yu " +0x7873: "[?] " +0x7874: "Kaki " +0x7875: "Lu " +0x7876: "Chen " +0x7877: "Jian " +0x7878: "Nue " +0x7879: "Song " +0x787a: "Zhuo " +0x787b: "Keng " +0x787c: "Peng " +0x787d: "Yan " +0x787e: "Zhui " +0x787f: "Kong " +0x7880: "Ceng " +0x7881: "Qi " +0x7882: "Zong " +0x7883: "Qing " +0x7884: "Lin " +0x7885: "Jun " +0x7886: "Bo " +0x7887: "Ding " +0x7888: "Min " +0x7889: "Diao " +0x788a: "Jian " +0x788b: "He " +0x788c: "Lu " +0x788d: "Ai " +0x788e: "Sui " +0x788f: "Que " +0x7890: "Ling " +0x7891: "Bei " +0x7892: "Yin " +0x7893: "Dui " +0x7894: "Wu " +0x7895: "Qi " +0x7896: "Lun " +0x7897: "Wan " +0x7898: "Dian " +0x7899: "Gang " +0x789a: "Pei " +0x789b: "Qi " +0x789c: "Chen " +0x789d: "Ruan " +0x789e: "Yan " +0x789f: "Die " +0x78a0: "Ding " +0x78a1: "Du " +0x78a2: "Tuo " +0x78a3: "Jie " +0x78a4: "Ying " +0x78a5: "Bian " +0x78a6: "Ke " +0x78a7: "Bi " +0x78a8: "Wei " +0x78a9: "Shuo " +0x78aa: "Zhen " +0x78ab: "Duan " +0x78ac: "Xia " +0x78ad: "Dang " +0x78ae: "Ti " +0x78af: "Nao " +0x78b0: "Peng " +0x78b1: "Jian " +0x78b2: "Di " +0x78b3: "Tan " +0x78b4: "Cha " +0x78b5: "Seki " +0x78b6: "Qi " +0x78b7: "[?] " +0x78b8: "Feng " +0x78b9: "Xuan " +0x78ba: "Que " +0x78bb: "Que " +0x78bc: "Ma " +0x78bd: "Gong " +0x78be: "Nian " +0x78bf: "Su " +0x78c0: "E " +0x78c1: "Ci " +0x78c2: "Liu " +0x78c3: "Si " +0x78c4: "Tang " +0x78c5: "Bang " +0x78c6: "Hua " +0x78c7: "Pi " +0x78c8: "Wei " +0x78c9: "Sang " +0x78ca: "Lei " +0x78cb: "Cuo " +0x78cc: "Zhen " +0x78cd: "Xia " +0x78ce: "Qi " +0x78cf: "Lian " +0x78d0: "Pan " +0x78d1: "Wei " +0x78d2: "Yun " +0x78d3: "Dui " +0x78d4: "Zhe " +0x78d5: "Ke " +0x78d6: "La " +0x78d7: "[?] " +0x78d8: "Qing " +0x78d9: "Gun " +0x78da: "Zhuan " +0x78db: "Chan " +0x78dc: "Qi " +0x78dd: "Ao " +0x78de: "Peng " +0x78df: "Lu " +0x78e0: "Lu " +0x78e1: "Kan " +0x78e2: "Qiang " +0x78e3: "Chen " +0x78e4: "Yin " +0x78e5: "Lei " +0x78e6: "Biao " +0x78e7: "Qi " +0x78e8: "Mo " +0x78e9: "Qi " +0x78ea: "Cui " +0x78eb: "Zong " +0x78ec: "Qing " +0x78ed: "Chuo " +0x78ee: "[?] " +0x78ef: "Ji " +0x78f0: "Shan " +0x78f1: "Lao " +0x78f2: "Qu " +0x78f3: "Zeng " +0x78f4: "Deng " +0x78f5: "Jian " +0x78f6: "Xi " +0x78f7: "Lin " +0x78f8: "Ding " +0x78f9: "Dian " +0x78fa: "Huang " +0x78fb: "Pan " +0x78fc: "Za " +0x78fd: "Qiao " +0x78fe: "Di " +0x78ff: "Li " +/* x079 */ +0x7900: "Tani " +0x7901: "Jiao " +0x7902: "[?] " +0x7903: "Zhang " +0x7904: "Qiao " +0x7905: "Dun " +0x7906: "Xian " +0x7907: "Yu " +0x7908: "Zhui " +0x7909: "He " +0x790a: "Huo " +0x790b: "Zhai " +0x790c: "Lei " +0x790d: "Ke " +0x790e: "Chu " +0x790f: "Ji " +0x7910: "Que " +0x7911: "Dang " +0x7912: "Yi " +0x7913: "Jiang " +0x7914: "Pi " +0x7915: "Pi " +0x7916: "Yu " +0x7917: "Pin " +0x7918: "Qi " +0x7919: "Ai " +0x791a: "Kai " +0x791b: "Jian " +0x791c: "Yu " +0x791d: "Ruan " +0x791e: "Meng " +0x791f: "Pao " +0x7920: "Ci " +0x7921: "[?] " +0x7922: "[?] " +0x7923: "Mie " +0x7924: "Ca " +0x7925: "Xian " +0x7926: "Kuang " +0x7927: "Lei " +0x7928: "Lei " +0x7929: "Zhi " +0x792a: "Li " +0x792b: "Li " +0x792c: "Fan " +0x792d: "Que " +0x792e: "Pao " +0x792f: "Ying " +0x7930: "Li " +0x7931: "Long " +0x7932: "Long " +0x7933: "Mo " +0x7934: "Bo " +0x7935: "Shuang " +0x7936: "Guan " +0x7937: "Lan " +0x7938: "Zan " +0x7939: "Yan " +0x793a: "Shi " +0x793b: "Shi " +0x793c: "Li " +0x793d: "Reng " +0x793e: "She " +0x793f: "Yue " +0x7940: "Si " +0x7941: "Qi " +0x7942: "Ta " +0x7943: "Ma " +0x7944: "Xie " +0x7945: "Xian " +0x7946: "Xian " +0x7947: "Zhi " +0x7948: "Qi " +0x7949: "Zhi " +0x794a: "Beng " +0x794b: "Dui " +0x794c: "Zhong " +0x794d: "[?] " +0x794e: "Yi " +0x794f: "Shi " +0x7950: "You " +0x7951: "Zhi " +0x7952: "Tiao " +0x7953: "Fu " +0x7954: "Fu " +0x7955: "Mi " +0x7956: "Zu " +0x7957: "Zhi " +0x7958: "Suan " +0x7959: "Mei " +0x795a: "Zuo " +0x795b: "Qu " +0x795c: "Hu " +0x795d: "Zhu " +0x795e: "Shen " +0x795f: "Sui " +0x7960: "Ci " +0x7961: "Chai " +0x7962: "Mi " +0x7963: "Lu " +0x7964: "Yu " +0x7965: "Xiang " +0x7966: "Wu " +0x7967: "Tiao " +0x7968: "Piao " +0x7969: "Zhu " +0x796a: "Gui " +0x796b: "Xia " +0x796c: "Zhi " +0x796d: "Ji " +0x796e: "Gao " +0x796f: "Zhen " +0x7970: "Gao " +0x7971: "Shui " +0x7972: "Jin " +0x7973: "Chen " +0x7974: "Gai " +0x7975: "Kun " +0x7976: "Di " +0x7977: "Dao " +0x7978: "Huo " +0x7979: "Tao " +0x797a: "Qi " +0x797b: "Gu " +0x797c: "Guan " +0x797d: "Zui " +0x797e: "Ling " +0x797f: "Lu " +0x7980: "Bing " +0x7981: "Jin " +0x7982: "Dao " +0x7983: "Zhi " +0x7984: "Lu " +0x7985: "Shan " +0x7986: "Bei " +0x7987: "Zhe " +0x7988: "Hui " +0x7989: "You " +0x798a: "Xi " +0x798b: "Yin " +0x798c: "Zi " +0x798d: "Huo " +0x798e: "Zhen " +0x798f: "Fu " +0x7990: "Yuan " +0x7991: "Wu " +0x7992: "Xian " +0x7993: "Yang " +0x7994: "Ti " +0x7995: "Yi " +0x7996: "Mei " +0x7997: "Si " +0x7998: "Di " +0x7999: "[?] " +0x799a: "Zhuo " +0x799b: "Zhen " +0x799c: "Yong " +0x799d: "Ji " +0x799e: "Gao " +0x799f: "Tang " +0x79a0: "Si " +0x79a1: "Ma " +0x79a2: "Ta " +0x79a3: "[?] " +0x79a4: "Xuan " +0x79a5: "Qi " +0x79a6: "Yu " +0x79a7: "Xi " +0x79a8: "Ji " +0x79a9: "Si " +0x79aa: "Chan " +0x79ab: "Tan " +0x79ac: "Kuai " +0x79ad: "Sui " +0x79ae: "Li " +0x79af: "Nong " +0x79b0: "Ni " +0x79b1: "Dao " +0x79b2: "Li " +0x79b3: "Rang " +0x79b4: "Yue " +0x79b5: "Ti " +0x79b6: "Zan " +0x79b7: "Lei " +0x79b8: "Rou " +0x79b9: "Yu " +0x79ba: "Yu " +0x79bb: "Chi " +0x79bc: "Xie " +0x79bd: "Qin " +0x79be: "He " +0x79bf: "Tu " +0x79c0: "Xiu " +0x79c1: "Si " +0x79c2: "Ren " +0x79c3: "Tu " +0x79c4: "Zi " +0x79c5: "Cha " +0x79c6: "Gan " +0x79c7: "Yi " +0x79c8: "Xian " +0x79c9: "Bing " +0x79ca: "Nian " +0x79cb: "Qiu " +0x79cc: "Qiu " +0x79cd: "Chong " +0x79ce: "Fen " +0x79cf: "Hao " +0x79d0: "Yun " +0x79d1: "Ke " +0x79d2: "Miao " +0x79d3: "Zhi " +0x79d4: "Geng " +0x79d5: "Bi " +0x79d6: "Zhi " +0x79d7: "Yu " +0x79d8: "Mi " +0x79d9: "Ku " +0x79da: "Ban " +0x79db: "Pi " +0x79dc: "Ni " +0x79dd: "Li " +0x79de: "You " +0x79df: "Zu " +0x79e0: "Pi " +0x79e1: "Ba " +0x79e2: "Ling " +0x79e3: "Mo " +0x79e4: "Cheng " +0x79e5: "Nian " +0x79e6: "Qin " +0x79e7: "Yang " +0x79e8: "Zuo " +0x79e9: "Zhi " +0x79ea: "Zhi " +0x79eb: "Shu " +0x79ec: "Ju " +0x79ed: "Zi " +0x79ee: "Huo " +0x79ef: "Ji " +0x79f0: "Cheng " +0x79f1: "Tong " +0x79f2: "Zhi " +0x79f3: "Huo " +0x79f4: "He " +0x79f5: "Yin " +0x79f6: "Zi " +0x79f7: "Zhi " +0x79f8: "Jie " +0x79f9: "Ren " +0x79fa: "Du " +0x79fb: "Yi " +0x79fc: "Zhu " +0x79fd: "Hui " +0x79fe: "Nong " +0x79ff: "Fu " +/* x07a */ +0x7a00: "Xi " +0x7a01: "Kao " +0x7a02: "Lang " +0x7a03: "Fu " +0x7a04: "Ze " +0x7a05: "Shui " +0x7a06: "Lu " +0x7a07: "Kun " +0x7a08: "Gan " +0x7a09: "Geng " +0x7a0a: "Ti " +0x7a0b: "Cheng " +0x7a0c: "Tu " +0x7a0d: "Shao " +0x7a0e: "Shui " +0x7a0f: "Ya " +0x7a10: "Lun " +0x7a11: "Lu " +0x7a12: "Gu " +0x7a13: "Zuo " +0x7a14: "Ren " +0x7a15: "Zhun " +0x7a16: "Bang " +0x7a17: "Bai " +0x7a18: "Ji " +0x7a19: "Zhi " +0x7a1a: "Zhi " +0x7a1b: "Kun " +0x7a1c: "Leng " +0x7a1d: "Peng " +0x7a1e: "Ke " +0x7a1f: "Bing " +0x7a20: "Chou " +0x7a21: "Zu " +0x7a22: "Yu " +0x7a23: "Su " +0x7a24: "Lue " +0x7a25: "[?] " +0x7a26: "Yi " +0x7a27: "Xi " +0x7a28: "Bian " +0x7a29: "Ji " +0x7a2a: "Fu " +0x7a2b: "Bi " +0x7a2c: "Nuo " +0x7a2d: "Jie " +0x7a2e: "Zhong " +0x7a2f: "Zong " +0x7a30: "Xu " +0x7a31: "Cheng " +0x7a32: "Dao " +0x7a33: "Wen " +0x7a34: "Lian " +0x7a35: "Zi " +0x7a36: "Yu " +0x7a37: "Ji " +0x7a38: "Xu " +0x7a39: "Zhen " +0x7a3a: "Zhi " +0x7a3b: "Dao " +0x7a3c: "Jia " +0x7a3d: "Ji " +0x7a3e: "Gao " +0x7a3f: "Gao " +0x7a40: "Gu " +0x7a41: "Rong " +0x7a42: "Sui " +0x7a43: "You " +0x7a44: "Ji " +0x7a45: "Kang " +0x7a46: "Mu " +0x7a47: "Shan " +0x7a48: "Men " +0x7a49: "Zhi " +0x7a4a: "Ji " +0x7a4b: "Lu " +0x7a4c: "Su " +0x7a4d: "Ji " +0x7a4e: "Ying " +0x7a4f: "Wen " +0x7a50: "Qiu " +0x7a51: "Se " +0x7a52: "[?] " +0x7a53: "Yi " +0x7a54: "Huang " +0x7a55: "Qie " +0x7a56: "Ji " +0x7a57: "Sui " +0x7a58: "Xiao " +0x7a59: "Pu " +0x7a5a: "Jiao " +0x7a5b: "Zhuo " +0x7a5c: "Tong " +0x7a5d: "Sai " +0x7a5e: "Lu " +0x7a5f: "Sui " +0x7a60: "Nong " +0x7a61: "Se " +0x7a62: "Hui " +0x7a63: "Rang " +0x7a64: "Nuo " +0x7a65: "Yu " +0x7a66: "Bin " +0x7a67: "Ji " +0x7a68: "Tui " +0x7a69: "Wen " +0x7a6a: "Cheng " +0x7a6b: "Huo " +0x7a6c: "Gong " +0x7a6d: "Lu " +0x7a6e: "Biao " +0x7a6f: "[?] " +0x7a70: "Rang " +0x7a71: "Zhuo " +0x7a72: "Li " +0x7a73: "Zan " +0x7a74: "Xue " +0x7a75: "Wa " +0x7a76: "Jiu " +0x7a77: "Qiong " +0x7a78: "Xi " +0x7a79: "Qiong " +0x7a7a: "Kong " +0x7a7b: "Yu " +0x7a7c: "Sen " +0x7a7d: "Jing " +0x7a7e: "Yao " +0x7a7f: "Chuan " +0x7a80: "Zhun " +0x7a81: "Tu " +0x7a82: "Lao " +0x7a83: "Qie " +0x7a84: "Zhai " +0x7a85: "Yao " +0x7a86: "Bian " +0x7a87: "Bao " +0x7a88: "Yao " +0x7a89: "Bing " +0x7a8a: "Wa " +0x7a8b: "Zhu " +0x7a8c: "Jiao " +0x7a8d: "Qiao " +0x7a8e: "Diao " +0x7a8f: "Wu " +0x7a90: "Gui " +0x7a91: "Yao " +0x7a92: "Zhi " +0x7a93: "Chuang " +0x7a94: "Yao " +0x7a95: "Tiao " +0x7a96: "Jiao " +0x7a97: "Chuang " +0x7a98: "Jiong " +0x7a99: "Xiao " +0x7a9a: "Cheng " +0x7a9b: "Kou " +0x7a9c: "Cuan " +0x7a9d: "Wo " +0x7a9e: "Dan " +0x7a9f: "Ku " +0x7aa0: "Ke " +0x7aa1: "Zhui " +0x7aa2: "Xu " +0x7aa3: "Su " +0x7aa4: "Guan " +0x7aa5: "Kui " +0x7aa6: "Dou " +0x7aa7: "[?] " +0x7aa8: "Yin " +0x7aa9: "Wo " +0x7aaa: "Wa " +0x7aab: "Ya " +0x7aac: "Yu " +0x7aad: "Ju " +0x7aae: "Qiong " +0x7aaf: "Yao " +0x7ab0: "Yao " +0x7ab1: "Tiao " +0x7ab2: "Chao " +0x7ab3: "Yu " +0x7ab4: "Tian " +0x7ab5: "Diao " +0x7ab6: "Ju " +0x7ab7: "Liao " +0x7ab8: "Xi " +0x7ab9: "Wu " +0x7aba: "Kui " +0x7abb: "Chuang " +0x7abc: "Zhao " +0x7abd: "[?] " +0x7abe: "Kuan " +0x7abf: "Long " +0x7ac0: "Cheng " +0x7ac1: "Cui " +0x7ac2: "Piao " +0x7ac3: "Zao " +0x7ac4: "Cuan " +0x7ac5: "Qiao " +0x7ac6: "Qiong " +0x7ac7: "Dou " +0x7ac8: "Zao " +0x7ac9: "Long " +0x7aca: "Qie " +0x7acb: "Li " +0x7acc: "Chu " +0x7acd: "Shi " +0x7ace: "Fou " +0x7acf: "Qian " +0x7ad0: "Chu " +0x7ad1: "Hong " +0x7ad2: "Qi " +0x7ad3: "Qian " +0x7ad4: "Gong " +0x7ad5: "Shi " +0x7ad6: "Shu " +0x7ad7: "Miao " +0x7ad8: "Ju " +0x7ad9: "Zhan " +0x7ada: "Zhu " +0x7adb: "Ling " +0x7adc: "Long " +0x7add: "Bing " +0x7ade: "Jing " +0x7adf: "Jing " +0x7ae0: "Zhang " +0x7ae1: "Yi " +0x7ae2: "Si " +0x7ae3: "Jun " +0x7ae4: "Hong " +0x7ae5: "Tong " +0x7ae6: "Song " +0x7ae7: "Jing " +0x7ae8: "Diao " +0x7ae9: "Yi " +0x7aea: "Shu " +0x7aeb: "Jing " +0x7aec: "Qu " +0x7aed: "Jie " +0x7aee: "Ping " +0x7aef: "Duan " +0x7af0: "Shao " +0x7af1: "Zhuan " +0x7af2: "Ceng " +0x7af3: "Deng " +0x7af4: "Cui " +0x7af5: "Huai " +0x7af6: "Jing " +0x7af7: "Kan " +0x7af8: "Jing " +0x7af9: "Zhu " +0x7afa: "Zhu " +0x7afb: "Le " +0x7afc: "Peng " +0x7afd: "Yu " +0x7afe: "Chi " +0x7aff: "Gan " +/* x07b */ +0x7b00: "Mang " +0x7b01: "Zhu " +0x7b02: "Utsubo " +0x7b03: "Du " +0x7b04: "Ji " +0x7b05: "Xiao " +0x7b06: "Ba " +0x7b07: "Suan " +0x7b08: "Ji " +0x7b09: "Zhen " +0x7b0a: "Zhao " +0x7b0b: "Sun " +0x7b0c: "Ya " +0x7b0d: "Zhui " +0x7b0e: "Yuan " +0x7b0f: "Hu " +0x7b10: "Gang " +0x7b11: "Xiao " +0x7b12: "Cen " +0x7b13: "Pi " +0x7b14: "Bi " +0x7b15: "Jian " +0x7b16: "Yi " +0x7b17: "Dong " +0x7b18: "Shan " +0x7b19: "Sheng " +0x7b1a: "Xia " +0x7b1b: "Di " +0x7b1c: "Zhu " +0x7b1d: "Na " +0x7b1e: "Chi " +0x7b1f: "Gu " +0x7b20: "Li " +0x7b21: "Qie " +0x7b22: "Min " +0x7b23: "Bao " +0x7b24: "Tiao " +0x7b25: "Si " +0x7b26: "Fu " +0x7b27: "Ce " +0x7b28: "Ben " +0x7b29: "Pei " +0x7b2a: "Da " +0x7b2b: "Zi " +0x7b2c: "Di " +0x7b2d: "Ling " +0x7b2e: "Ze " +0x7b2f: "Nu " +0x7b30: "Fu " +0x7b31: "Gou " +0x7b32: "Fan " +0x7b33: "Jia " +0x7b34: "Ge " +0x7b35: "Fan " +0x7b36: "Shi " +0x7b37: "Mao " +0x7b38: "Po " +0x7b39: "Sey " +0x7b3a: "Jian " +0x7b3b: "Qiong " +0x7b3c: "Long " +0x7b3d: "Souke " +0x7b3e: "Bian " +0x7b3f: "Luo " +0x7b40: "Gui " +0x7b41: "Qu " +0x7b42: "Chi " +0x7b43: "Yin " +0x7b44: "Yao " +0x7b45: "Xian " +0x7b46: "Bi " +0x7b47: "Qiong " +0x7b48: "Gua " +0x7b49: "Deng " +0x7b4a: "Jiao " +0x7b4b: "Jin " +0x7b4c: "Quan " +0x7b4d: "Sun " +0x7b4e: "Ru " +0x7b4f: "Fa " +0x7b50: "Kuang " +0x7b51: "Zhu " +0x7b52: "Tong " +0x7b53: "Ji " +0x7b54: "Da " +0x7b55: "Xing " +0x7b56: "Ce " +0x7b57: "Zhong " +0x7b58: "Kou " +0x7b59: "Lai " +0x7b5a: "Bi " +0x7b5b: "Shai " +0x7b5c: "Dang " +0x7b5d: "Zheng " +0x7b5e: "Ce " +0x7b5f: "Fu " +0x7b60: "Yun " +0x7b61: "Tu " +0x7b62: "Pa " +0x7b63: "Li " +0x7b64: "Lang " +0x7b65: "Ju " +0x7b66: "Guan " +0x7b67: "Jian " +0x7b68: "Han " +0x7b69: "Tong " +0x7b6a: "Xia " +0x7b6b: "Zhi " +0x7b6c: "Cheng " +0x7b6d: "Suan " +0x7b6e: "Shi " +0x7b6f: "Zhu " +0x7b70: "Zuo " +0x7b71: "Xiao " +0x7b72: "Shao " +0x7b73: "Ting " +0x7b74: "Ce " +0x7b75: "Yan " +0x7b76: "Gao " +0x7b77: "Kuai " +0x7b78: "Gan " +0x7b79: "Chou " +0x7b7a: "Kago " +0x7b7b: "Gang " +0x7b7c: "Yun " +0x7b7d: "O " +0x7b7e: "Qian " +0x7b7f: "Xiao " +0x7b80: "Jian " +0x7b81: "Pu " +0x7b82: "Lai " +0x7b83: "Zou " +0x7b84: "Bi " +0x7b85: "Bi " +0x7b86: "Bi " +0x7b87: "Ge " +0x7b88: "Chi " +0x7b89: "Guai " +0x7b8a: "Yu " +0x7b8b: "Jian " +0x7b8c: "Zhao " +0x7b8d: "Gu " +0x7b8e: "Chi " +0x7b8f: "Zheng " +0x7b90: "Jing " +0x7b91: "Sha " +0x7b92: "Zhou " +0x7b93: "Lu " +0x7b94: "Bo " +0x7b95: "Ji " +0x7b96: "Lin " +0x7b97: "Suan " +0x7b98: "Jun " +0x7b99: "Fu " +0x7b9a: "Zha " +0x7b9b: "Gu " +0x7b9c: "Kong " +0x7b9d: "Qian " +0x7b9e: "Quan " +0x7b9f: "Jun " +0x7ba0: "Chui " +0x7ba1: "Guan " +0x7ba2: "Yuan " +0x7ba3: "Ce " +0x7ba4: "Ju " +0x7ba5: "Bo " +0x7ba6: "Ze " +0x7ba7: "Qie " +0x7ba8: "Tuo " +0x7ba9: "Luo " +0x7baa: "Dan " +0x7bab: "Xiao " +0x7bac: "Ruo " +0x7bad: "Jian " +0x7bae: "Xuan " +0x7baf: "Bian " +0x7bb0: "Sun " +0x7bb1: "Xiang " +0x7bb2: "Xian " +0x7bb3: "Ping " +0x7bb4: "Zhen " +0x7bb5: "Sheng " +0x7bb6: "Hu " +0x7bb7: "Shi " +0x7bb8: "Zhu " +0x7bb9: "Yue " +0x7bba: "Chun " +0x7bbb: "Lu " +0x7bbc: "Wu " +0x7bbd: "Dong " +0x7bbe: "Xiao " +0x7bbf: "Ji " +0x7bc0: "Jie " +0x7bc1: "Huang " +0x7bc2: "Xing " +0x7bc3: "Mei " +0x7bc4: "Fan " +0x7bc5: "Chui " +0x7bc6: "Zhuan " +0x7bc7: "Pian " +0x7bc8: "Feng " +0x7bc9: "Zhu " +0x7bca: "Hong " +0x7bcb: "Qie " +0x7bcc: "Hou " +0x7bcd: "Qiu " +0x7bce: "Miao " +0x7bcf: "Qian " +0x7bd0: "[?] " +0x7bd1: "Kui " +0x7bd2: "Sik " +0x7bd3: "Lou " +0x7bd4: "Yun " +0x7bd5: "He " +0x7bd6: "Tang " +0x7bd7: "Yue " +0x7bd8: "Chou " +0x7bd9: "Gao " +0x7bda: "Fei " +0x7bdb: "Ruo " +0x7bdc: "Zheng " +0x7bdd: "Gou " +0x7bde: "Nie " +0x7bdf: "Qian " +0x7be0: "Xiao " +0x7be1: "Cuan " +0x7be2: "Gong " +0x7be3: "Pang " +0x7be4: "Du " +0x7be5: "Li " +0x7be6: "Bi " +0x7be7: "Zhuo " +0x7be8: "Chu " +0x7be9: "Shai " +0x7bea: "Chi " +0x7beb: "Zhu " +0x7bec: "Qiang " +0x7bed: "Long " +0x7bee: "Lan " +0x7bef: "Jian " +0x7bf0: "Bu " +0x7bf1: "Li " +0x7bf2: "Hui " +0x7bf3: "Bi " +0x7bf4: "Di " +0x7bf5: "Cong " +0x7bf6: "Yan " +0x7bf7: "Peng " +0x7bf8: "Sen " +0x7bf9: "Zhuan " +0x7bfa: "Pai " +0x7bfb: "Piao " +0x7bfc: "Dou " +0x7bfd: "Yu " +0x7bfe: "Mie " +0x7bff: "Zhuan " +/* x07c */ +0x7c00: "Ze " +0x7c01: "Xi " +0x7c02: "Guo " +0x7c03: "Yi " +0x7c04: "Hu " +0x7c05: "Chan " +0x7c06: "Kou " +0x7c07: "Cu " +0x7c08: "Ping " +0x7c09: "Chou " +0x7c0a: "Ji " +0x7c0b: "Gui " +0x7c0c: "Su " +0x7c0d: "Lou " +0x7c0e: "Zha " +0x7c0f: "Lu " +0x7c10: "Nian " +0x7c11: "Suo " +0x7c12: "Cuan " +0x7c13: "Sasara " +0x7c14: "Suo " +0x7c15: "Le " +0x7c16: "Duan " +0x7c17: "Yana " +0x7c18: "Xiao " +0x7c19: "Bo " +0x7c1a: "Mi " +0x7c1b: "Si " +0x7c1c: "Dang " +0x7c1d: "Liao " +0x7c1e: "Dan " +0x7c1f: "Dian " +0x7c20: "Fu " +0x7c21: "Jian " +0x7c22: "Min " +0x7c23: "Kui " +0x7c24: "Dai " +0x7c25: "Qiao " +0x7c26: "Deng " +0x7c27: "Huang " +0x7c28: "Sun " +0x7c29: "Lao " +0x7c2a: "Zan " +0x7c2b: "Xiao " +0x7c2c: "Du " +0x7c2d: "Shi " +0x7c2e: "Zan " +0x7c2f: "[?] " +0x7c30: "Pai " +0x7c31: "Hata " +0x7c32: "Pai " +0x7c33: "Gan " +0x7c34: "Ju " +0x7c35: "Du " +0x7c36: "Lu " +0x7c37: "Yan " +0x7c38: "Bo " +0x7c39: "Dang " +0x7c3a: "Sai " +0x7c3b: "Ke " +0x7c3c: "Long " +0x7c3d: "Qian " +0x7c3e: "Lian " +0x7c3f: "Bo " +0x7c40: "Zhou " +0x7c41: "Lai " +0x7c42: "[?] " +0x7c43: "Lan " +0x7c44: "Kui " +0x7c45: "Yu " +0x7c46: "Yue " +0x7c47: "Hao " +0x7c48: "Zhen " +0x7c49: "Tai " +0x7c4a: "Ti " +0x7c4b: "Mi " +0x7c4c: "Chou " +0x7c4d: "Ji " +0x7c4e: "[?] " +0x7c4f: "Hata " +0x7c50: "Teng " +0x7c51: "Zhuan " +0x7c52: "Zhou " +0x7c53: "Fan " +0x7c54: "Sou " +0x7c55: "Zhou " +0x7c56: "Kuji " +0x7c57: "Zhuo " +0x7c58: "Teng " +0x7c59: "Lu " +0x7c5a: "Lu " +0x7c5b: "Jian " +0x7c5c: "Tuo " +0x7c5d: "Ying " +0x7c5e: "Yu " +0x7c5f: "Lai " +0x7c60: "Long " +0x7c61: "Shinshi " +0x7c62: "Lian " +0x7c63: "Lan " +0x7c64: "Qian " +0x7c65: "Yue " +0x7c66: "Zhong " +0x7c67: "Qu " +0x7c68: "Lian " +0x7c69: "Bian " +0x7c6a: "Duan " +0x7c6b: "Zuan " +0x7c6c: "Li " +0x7c6d: "Si " +0x7c6e: "Luo " +0x7c6f: "Ying " +0x7c70: "Yue " +0x7c71: "Zhuo " +0x7c72: "Xu " +0x7c73: "Mi " +0x7c74: "Di " +0x7c75: "Fan " +0x7c76: "Shen " +0x7c77: "Zhe " +0x7c78: "Shen " +0x7c79: "Nu " +0x7c7a: "Xie " +0x7c7b: "Lei " +0x7c7c: "Xian " +0x7c7d: "Zi " +0x7c7e: "Ni " +0x7c7f: "Cun " +0x7c80: "[?] " +0x7c81: "Qian " +0x7c82: "Kume " +0x7c83: "Bi " +0x7c84: "Ban " +0x7c85: "Wu " +0x7c86: "Sha " +0x7c87: "Kang " +0x7c88: "Rou " +0x7c89: "Fen " +0x7c8a: "Bi " +0x7c8b: "Cui " +0x7c8c: "[?] " +0x7c8d: "Li " +0x7c8e: "Chi " +0x7c8f: "Nukamiso " +0x7c90: "Ro " +0x7c91: "Ba " +0x7c92: "Li " +0x7c93: "Gan " +0x7c94: "Ju " +0x7c95: "Po " +0x7c96: "Mo " +0x7c97: "Cu " +0x7c98: "Nian " +0x7c99: "Zhou " +0x7c9a: "Li " +0x7c9b: "Su " +0x7c9c: "Tiao " +0x7c9d: "Li " +0x7c9e: "Qi " +0x7c9f: "Su " +0x7ca0: "Hong " +0x7ca1: "Tong " +0x7ca2: "Zi " +0x7ca3: "Ce " +0x7ca4: "Yue " +0x7ca5: "Zhou " +0x7ca6: "Lin " +0x7ca7: "Zhuang " +0x7ca8: "Bai " +0x7ca9: "[?] " +0x7caa: "Fen " +0x7cab: "Ji " +0x7cac: "[?] " +0x7cad: "Sukumo " +0x7cae: "Liang " +0x7caf: "Xian " +0x7cb0: "Fu " +0x7cb1: "Liang " +0x7cb2: "Can " +0x7cb3: "Geng " +0x7cb4: "Li " +0x7cb5: "Yue " +0x7cb6: "Lu " +0x7cb7: "Ju " +0x7cb8: "Qi " +0x7cb9: "Cui " +0x7cba: "Bai " +0x7cbb: "Zhang " +0x7cbc: "Lin " +0x7cbd: "Zong " +0x7cbe: "Jing " +0x7cbf: "Guo " +0x7cc0: "Kouji " +0x7cc1: "San " +0x7cc2: "San " +0x7cc3: "Tang " +0x7cc4: "Bian " +0x7cc5: "Rou " +0x7cc6: "Mian " +0x7cc7: "Hou " +0x7cc8: "Xu " +0x7cc9: "Zong " +0x7cca: "Hu " +0x7ccb: "Jian " +0x7ccc: "Zan " +0x7ccd: "Ci " +0x7cce: "Li " +0x7ccf: "Xie " +0x7cd0: "Fu " +0x7cd1: "Ni " +0x7cd2: "Bei " +0x7cd3: "Gu " +0x7cd4: "Xiu " +0x7cd5: "Gao " +0x7cd6: "Tang " +0x7cd7: "Qiu " +0x7cd8: "Sukumo " +0x7cd9: "Cao " +0x7cda: "Zhuang " +0x7cdb: "Tang " +0x7cdc: "Mi " +0x7cdd: "San " +0x7cde: "Fen " +0x7cdf: "Zao " +0x7ce0: "Kang " +0x7ce1: "Jiang " +0x7ce2: "Mo " +0x7ce3: "San " +0x7ce4: "San " +0x7ce5: "Nuo " +0x7ce6: "Xi " +0x7ce7: "Liang " +0x7ce8: "Jiang " +0x7ce9: "Kuai " +0x7cea: "Bo " +0x7ceb: "Huan " +0x7cec: "[?] " +0x7ced: "Zong " +0x7cee: "Xian " +0x7cef: "Nuo " +0x7cf0: "Tuan " +0x7cf1: "Nie " +0x7cf2: "Li " +0x7cf3: "Zuo " +0x7cf4: "Di " +0x7cf5: "Nie " +0x7cf6: "Tiao " +0x7cf7: "Lan " +0x7cf8: "Mi " +0x7cf9: "Jiao " +0x7cfa: "Jiu " +0x7cfb: "Xi " +0x7cfc: "Gong " +0x7cfd: "Zheng " +0x7cfe: "Jiu " +0x7cff: "You " +/* x07d */ +0x7d00: "Ji " +0x7d01: "Cha " +0x7d02: "Zhou " +0x7d03: "Xun " +0x7d04: "Yue " +0x7d05: "Hong " +0x7d06: "Yu " +0x7d07: "He " +0x7d08: "Wan " +0x7d09: "Ren " +0x7d0a: "Wen " +0x7d0b: "Wen " +0x7d0c: "Qiu " +0x7d0d: "Na " +0x7d0e: "Zi " +0x7d0f: "Tou " +0x7d10: "Niu " +0x7d11: "Fou " +0x7d12: "Jie " +0x7d13: "Shu " +0x7d14: "Chun " +0x7d15: "Pi " +0x7d16: "Yin " +0x7d17: "Sha " +0x7d18: "Hong " +0x7d19: "Zhi " +0x7d1a: "Ji " +0x7d1b: "Fen " +0x7d1c: "Yun " +0x7d1d: "Ren " +0x7d1e: "Dan " +0x7d1f: "Jin " +0x7d20: "Su " +0x7d21: "Fang " +0x7d22: "Suo " +0x7d23: "Cui " +0x7d24: "Jiu " +0x7d25: "Zha " +0x7d26: "Kinu " +0x7d27: "Jin " +0x7d28: "Fu " +0x7d29: "Zhi " +0x7d2a: "Ci " +0x7d2b: "Zi " +0x7d2c: "Chou " +0x7d2d: "Hong " +0x7d2e: "Zha " +0x7d2f: "Lei " +0x7d30: "Xi " +0x7d31: "Fu " +0x7d32: "Xie " +0x7d33: "Shen " +0x7d34: "Bei " +0x7d35: "Zhu " +0x7d36: "Qu " +0x7d37: "Ling " +0x7d38: "Zhu " +0x7d39: "Shao " +0x7d3a: "Gan " +0x7d3b: "Yang " +0x7d3c: "Fu " +0x7d3d: "Tuo " +0x7d3e: "Zhen " +0x7d3f: "Dai " +0x7d40: "Zhuo " +0x7d41: "Shi " +0x7d42: "Zhong " +0x7d43: "Xian " +0x7d44: "Zu " +0x7d45: "Jiong " +0x7d46: "Ban " +0x7d47: "Ju " +0x7d48: "Mo " +0x7d49: "Shu " +0x7d4a: "Zui " +0x7d4b: "Wata " +0x7d4c: "Jing " +0x7d4d: "Ren " +0x7d4e: "Heng " +0x7d4f: "Xie " +0x7d50: "Jie " +0x7d51: "Zhu " +0x7d52: "Chou " +0x7d53: "Gua " +0x7d54: "Bai " +0x7d55: "Jue " +0x7d56: "Kuang " +0x7d57: "Hu " +0x7d58: "Ci " +0x7d59: "Geng " +0x7d5a: "Geng " +0x7d5b: "Tao " +0x7d5c: "Xie " +0x7d5d: "Ku " +0x7d5e: "Jiao " +0x7d5f: "Quan " +0x7d60: "Gai " +0x7d61: "Luo " +0x7d62: "Xuan " +0x7d63: "Bing " +0x7d64: "Xian " +0x7d65: "Fu " +0x7d66: "Gei " +0x7d67: "Tong " +0x7d68: "Rong " +0x7d69: "Tiao " +0x7d6a: "Yin " +0x7d6b: "Lei " +0x7d6c: "Xie " +0x7d6d: "Quan " +0x7d6e: "Xu " +0x7d6f: "Lun " +0x7d70: "Die " +0x7d71: "Tong " +0x7d72: "Si " +0x7d73: "Jiang " +0x7d74: "Xiang " +0x7d75: "Hui " +0x7d76: "Jue " +0x7d77: "Zhi " +0x7d78: "Jian " +0x7d79: "Juan " +0x7d7a: "Chi " +0x7d7b: "Mian " +0x7d7c: "Zhen " +0x7d7d: "Lu " +0x7d7e: "Cheng " +0x7d7f: "Qiu " +0x7d80: "Shu " +0x7d81: "Bang " +0x7d82: "Tong " +0x7d83: "Xiao " +0x7d84: "Wan " +0x7d85: "Qin " +0x7d86: "Geng " +0x7d87: "Xiu " +0x7d88: "Ti " +0x7d89: "Xiu " +0x7d8a: "Xie " +0x7d8b: "Hong " +0x7d8c: "Xi " +0x7d8d: "Fu " +0x7d8e: "Ting " +0x7d8f: "Sui " +0x7d90: "Dui " +0x7d91: "Kun " +0x7d92: "Fu " +0x7d93: "Jing " +0x7d94: "Hu " +0x7d95: "Zhi " +0x7d96: "Yan " +0x7d97: "Jiong " +0x7d98: "Feng " +0x7d99: "Ji " +0x7d9a: "Sok " +0x7d9b: "Kase " +0x7d9c: "Zong " +0x7d9d: "Lin " +0x7d9e: "Duo " +0x7d9f: "Li " +0x7da0: "Lu " +0x7da1: "Liang " +0x7da2: "Chou " +0x7da3: "Quan " +0x7da4: "Shao " +0x7da5: "Qi " +0x7da6: "Qi " +0x7da7: "Zhun " +0x7da8: "Qi " +0x7da9: "Wan " +0x7daa: "Qian " +0x7dab: "Xian " +0x7dac: "Shou " +0x7dad: "Wei " +0x7dae: "Qi " +0x7daf: "Tao " +0x7db0: "Wan " +0x7db1: "Gang " +0x7db2: "Wang " +0x7db3: "Beng " +0x7db4: "Zhui " +0x7db5: "Cai " +0x7db6: "Guo " +0x7db7: "Cui " +0x7db8: "Lun " +0x7db9: "Liu " +0x7dba: "Qi " +0x7dbb: "Zhan " +0x7dbc: "Bei " +0x7dbd: "Chuo " +0x7dbe: "Ling " +0x7dbf: "Mian " +0x7dc0: "Qi " +0x7dc1: "Qie " +0x7dc2: "Tan " +0x7dc3: "Zong " +0x7dc4: "Gun " +0x7dc5: "Zou " +0x7dc6: "Yi " +0x7dc7: "Zi " +0x7dc8: "Xing " +0x7dc9: "Liang " +0x7dca: "Jin " +0x7dcb: "Fei " +0x7dcc: "Rui " +0x7dcd: "Min " +0x7dce: "Yu " +0x7dcf: "Zong " +0x7dd0: "Fan " +0x7dd1: "Lu " +0x7dd2: "Xu " +0x7dd3: "Yingl " +0x7dd4: "Zhang " +0x7dd5: "Kasuri " +0x7dd6: "Xu " +0x7dd7: "Xiang " +0x7dd8: "Jian " +0x7dd9: "Ke " +0x7dda: "Xian " +0x7ddb: "Ruan " +0x7ddc: "Mian " +0x7ddd: "Qi " +0x7dde: "Duan " +0x7ddf: "Zhong " +0x7de0: "Di " +0x7de1: "Min " +0x7de2: "Miao " +0x7de3: "Yuan " +0x7de4: "Xie " +0x7de5: "Bao " +0x7de6: "Si " +0x7de7: "Qiu " +0x7de8: "Bian " +0x7de9: "Huan " +0x7dea: "Geng " +0x7deb: "Cong " +0x7dec: "Mian " +0x7ded: "Wei " +0x7dee: "Fu " +0x7def: "Wei " +0x7df0: "Yu " +0x7df1: "Gou " +0x7df2: "Miao " +0x7df3: "Xie " +0x7df4: "Lian " +0x7df5: "Zong " +0x7df6: "Bian " +0x7df7: "Yun " +0x7df8: "Yin " +0x7df9: "Ti " +0x7dfa: "Gua " +0x7dfb: "Zhi " +0x7dfc: "Yun " +0x7dfd: "Cheng " +0x7dfe: "Chan " +0x7dff: "Dai " +/* x07e */ +0x7e00: "Xia " +0x7e01: "Yuan " +0x7e02: "Zong " +0x7e03: "Xu " +0x7e04: "Nawa " +0x7e05: "Odoshi " +0x7e06: "Geng " +0x7e07: "Sen " +0x7e08: "Ying " +0x7e09: "Jin " +0x7e0a: "Yi " +0x7e0b: "Zhui " +0x7e0c: "Ni " +0x7e0d: "Bang " +0x7e0e: "Gu " +0x7e0f: "Pan " +0x7e10: "Zhou " +0x7e11: "Jian " +0x7e12: "Cuo " +0x7e13: "Quan " +0x7e14: "Shuang " +0x7e15: "Yun " +0x7e16: "Xia " +0x7e17: "Shuai " +0x7e18: "Xi " +0x7e19: "Rong " +0x7e1a: "Tao " +0x7e1b: "Fu " +0x7e1c: "Yun " +0x7e1d: "Zhen " +0x7e1e: "Gao " +0x7e1f: "Ru " +0x7e20: "Hu " +0x7e21: "Zai " +0x7e22: "Teng " +0x7e23: "Xian " +0x7e24: "Su " +0x7e25: "Zhen " +0x7e26: "Zong " +0x7e27: "Tao " +0x7e28: "Horo " +0x7e29: "Cai " +0x7e2a: "Bi " +0x7e2b: "Feng " +0x7e2c: "Cu " +0x7e2d: "Li " +0x7e2e: "Suo " +0x7e2f: "Yin " +0x7e30: "Xi " +0x7e31: "Zong " +0x7e32: "Lei " +0x7e33: "Zhuan " +0x7e34: "Qian " +0x7e35: "Man " +0x7e36: "Zhi " +0x7e37: "Lu " +0x7e38: "Mo " +0x7e39: "Piao " +0x7e3a: "Lian " +0x7e3b: "Mi " +0x7e3c: "Xuan " +0x7e3d: "Zong " +0x7e3e: "Ji " +0x7e3f: "Shan " +0x7e40: "Sui " +0x7e41: "Fan " +0x7e42: "Shuai " +0x7e43: "Beng " +0x7e44: "Yi " +0x7e45: "Sao " +0x7e46: "Mou " +0x7e47: "Zhou " +0x7e48: "Qiang " +0x7e49: "Hun " +0x7e4a: "Sem " +0x7e4b: "Xi " +0x7e4c: "Jung " +0x7e4d: "Xiu " +0x7e4e: "Ran " +0x7e4f: "Xuan " +0x7e50: "Hui " +0x7e51: "Qiao " +0x7e52: "Zeng " +0x7e53: "Zuo " +0x7e54: "Zhi " +0x7e55: "Shan " +0x7e56: "San " +0x7e57: "Lin " +0x7e58: "Yu " +0x7e59: "Fan " +0x7e5a: "Liao " +0x7e5b: "Chuo " +0x7e5c: "Zun " +0x7e5d: "Jian " +0x7e5e: "Rao " +0x7e5f: "Chan " +0x7e60: "Rui " +0x7e61: "Xiu " +0x7e62: "Hui " +0x7e63: "Hua " +0x7e64: "Zuan " +0x7e65: "Xi " +0x7e66: "Qiang " +0x7e67: "Un " +0x7e68: "Da " +0x7e69: "Sheng " +0x7e6a: "Hui " +0x7e6b: "Xi " +0x7e6c: "Se " +0x7e6d: "Jian " +0x7e6e: "Jiang " +0x7e6f: "Huan " +0x7e70: "Zao " +0x7e71: "Cong " +0x7e72: "Jie " +0x7e73: "Jiao " +0x7e74: "Bo " +0x7e75: "Chan " +0x7e76: "Yi " +0x7e77: "Nao " +0x7e78: "Sui " +0x7e79: "Yi " +0x7e7a: "Shai " +0x7e7b: "Xu " +0x7e7c: "Ji " +0x7e7d: "Bin " +0x7e7e: "Qian " +0x7e7f: "Lan " +0x7e80: "Pu " +0x7e81: "Xun " +0x7e82: "Zuan " +0x7e83: "Qi " +0x7e84: "Peng " +0x7e85: "Li " +0x7e86: "Mo " +0x7e87: "Lei " +0x7e88: "Xie " +0x7e89: "Zuan " +0x7e8a: "Kuang " +0x7e8b: "You " +0x7e8c: "Xu " +0x7e8d: "Lei " +0x7e8e: "Xian " +0x7e8f: "Chan " +0x7e90: "Kou " +0x7e91: "Lu " +0x7e92: "Chan " +0x7e93: "Ying " +0x7e94: "Cai " +0x7e95: "Xiang " +0x7e96: "Xian " +0x7e97: "Zui " +0x7e98: "Zuan " +0x7e99: "Luo " +0x7e9a: "Xi " +0x7e9b: "Dao " +0x7e9c: "Lan " +0x7e9d: "Lei " +0x7e9e: "Lian " +0x7e9f: "Si " +0x7ea0: "Jiu " +0x7ea1: "Yu " +0x7ea2: "Hong " +0x7ea3: "Zhou " +0x7ea4: "Xian " +0x7ea5: "He " +0x7ea6: "Yue " +0x7ea7: "Ji " +0x7ea8: "Wan " +0x7ea9: "Kuang " +0x7eaa: "Ji " +0x7eab: "Ren " +0x7eac: "Wei " +0x7ead: "Yun " +0x7eae: "Hong " +0x7eaf: "Chun " +0x7eb0: "Pi " +0x7eb1: "Sha " +0x7eb2: "Gang " +0x7eb3: "Na " +0x7eb4: "Ren " +0x7eb5: "Zong " +0x7eb6: "Lun " +0x7eb7: "Fen " +0x7eb8: "Zhi " +0x7eb9: "Wen " +0x7eba: "Fang " +0x7ebb: "Zhu " +0x7ebc: "Yin " +0x7ebd: "Niu " +0x7ebe: "Shu " +0x7ebf: "Xian " +0x7ec0: "Gan " +0x7ec1: "Xie " +0x7ec2: "Fu " +0x7ec3: "Lian " +0x7ec4: "Zu " +0x7ec5: "Shen " +0x7ec6: "Xi " +0x7ec7: "Zhi " +0x7ec8: "Zhong " +0x7ec9: "Zhou " +0x7eca: "Ban " +0x7ecb: "Fu " +0x7ecc: "Zhuo " +0x7ecd: "Shao " +0x7ece: "Yi " +0x7ecf: "Jing " +0x7ed0: "Dai " +0x7ed1: "Bang " +0x7ed2: "Rong " +0x7ed3: "Jie " +0x7ed4: "Ku " +0x7ed5: "Rao " +0x7ed6: "Die " +0x7ed7: "Heng " +0x7ed8: "Hui " +0x7ed9: "Gei " +0x7eda: "Xuan " +0x7edb: "Jiang " +0x7edc: "Luo " +0x7edd: "Jue " +0x7ede: "Jiao " +0x7edf: "Tong " +0x7ee0: "Geng " +0x7ee1: "Xiao " +0x7ee2: "Juan " +0x7ee3: "Xiu " +0x7ee4: "Xi " +0x7ee5: "Sui " +0x7ee6: "Tao " +0x7ee7: "Ji " +0x7ee8: "Ti " +0x7ee9: "Ji " +0x7eea: "Xu " +0x7eeb: "Ling " +0x7eec: "[?] " +0x7eed: "Xu " +0x7eee: "Qi " +0x7eef: "Fei " +0x7ef0: "Chuo " +0x7ef1: "Zhang " +0x7ef2: "Gun " +0x7ef3: "Sheng " +0x7ef4: "Wei " +0x7ef5: "Mian " +0x7ef6: "Shou " +0x7ef7: "Beng " +0x7ef8: "Chou " +0x7ef9: "Tao " +0x7efa: "Liu " +0x7efb: "Quan " +0x7efc: "Zong " +0x7efd: "Zhan " +0x7efe: "Wan " +0x7eff: "Lu " +/* x07f */ +0x7f00: "Zhui " +0x7f01: "Zi " +0x7f02: "Ke " +0x7f03: "Xiang " +0x7f04: "Jian " +0x7f05: "Mian " +0x7f06: "Lan " +0x7f07: "Ti " +0x7f08: "Miao " +0x7f09: "Qi " +0x7f0a: "Yun " +0x7f0b: "Hui " +0x7f0c: "Si " +0x7f0d: "Duo " +0x7f0e: "Duan " +0x7f0f: "Bian " +0x7f10: "Xian " +0x7f11: "Gou " +0x7f12: "Zhui " +0x7f13: "Huan " +0x7f14: "Di " +0x7f15: "Lu " +0x7f16: "Bian " +0x7f17: "Min " +0x7f18: "Yuan " +0x7f19: "Jin " +0x7f1a: "Fu " +0x7f1b: "Ru " +0x7f1c: "Zhen " +0x7f1d: "Feng " +0x7f1e: "Shuai " +0x7f1f: "Gao " +0x7f20: "Chan " +0x7f21: "Li " +0x7f22: "Yi " +0x7f23: "Jian " +0x7f24: "Bin " +0x7f25: "Piao " +0x7f26: "Man " +0x7f27: "Lei " +0x7f28: "Ying " +0x7f29: "Suo " +0x7f2a: "Mou " +0x7f2b: "Sao " +0x7f2c: "Xie " +0x7f2d: "Liao " +0x7f2e: "Shan " +0x7f2f: "Zeng " +0x7f30: "Jiang " +0x7f31: "Qian " +0x7f32: "Zao " +0x7f33: "Huan " +0x7f34: "Jiao " +0x7f35: "Zuan " +0x7f36: "Fou " +0x7f37: "Xie " +0x7f38: "Gang " +0x7f39: "Fou " +0x7f3a: "Que " +0x7f3b: "Fou " +0x7f3c: "Kaakeru " +0x7f3d: "Bo " +0x7f3e: "Ping " +0x7f3f: "Hou " +0x7f40: "[?] " +0x7f41: "Gang " +0x7f42: "Ying " +0x7f43: "Ying " +0x7f44: "Qing " +0x7f45: "Xia " +0x7f46: "Guan " +0x7f47: "Zun " +0x7f48: "Tan " +0x7f49: "Chang " +0x7f4a: "Qi " +0x7f4b: "Weng " +0x7f4c: "Ying " +0x7f4d: "Lei " +0x7f4e: "Tan " +0x7f4f: "Lu " +0x7f50: "Guan " +0x7f51: "Wang " +0x7f52: "Wang " +0x7f53: "Gang " +0x7f54: "Wang " +0x7f55: "Han " +0x7f56: "[?] " +0x7f57: "Luo " +0x7f58: "Fu " +0x7f59: "Mi " +0x7f5a: "Fa " +0x7f5b: "Gu " +0x7f5c: "Zhu " +0x7f5d: "Ju " +0x7f5e: "Mao " +0x7f5f: "Gu " +0x7f60: "Min " +0x7f61: "Gang " +0x7f62: "Ba " +0x7f63: "Gua " +0x7f64: "Ti " +0x7f65: "Juan " +0x7f66: "Fu " +0x7f67: "Lin " +0x7f68: "Yan " +0x7f69: "Zhao " +0x7f6a: "Zui " +0x7f6b: "Gua " +0x7f6c: "Zhuo " +0x7f6d: "Yu " +0x7f6e: "Zhi " +0x7f6f: "An " +0x7f70: "Fa " +0x7f71: "Nan " +0x7f72: "Shu " +0x7f73: "Si " +0x7f74: "Pi " +0x7f75: "Ma " +0x7f76: "Liu " +0x7f77: "Ba " +0x7f78: "Fa " +0x7f79: "Li " +0x7f7a: "Chao " +0x7f7b: "Wei " +0x7f7c: "Bi " +0x7f7d: "Ji " +0x7f7e: "Zeng " +0x7f7f: "Tong " +0x7f80: "Liu " +0x7f81: "Ji " +0x7f82: "Juan " +0x7f83: "Mi " +0x7f84: "Zhao " +0x7f85: "Luo " +0x7f86: "Pi " +0x7f87: "Ji " +0x7f88: "Ji " +0x7f89: "Luan " +0x7f8a: "Yang " +0x7f8b: "Mie " +0x7f8c: "Qiang " +0x7f8d: "Ta " +0x7f8e: "Mei " +0x7f8f: "Yang " +0x7f90: "You " +0x7f91: "You " +0x7f92: "Fen " +0x7f93: "Ba " +0x7f94: "Gao " +0x7f95: "Yang " +0x7f96: "Gu " +0x7f97: "Qiang " +0x7f98: "Zang " +0x7f99: "Gao " +0x7f9a: "Ling " +0x7f9b: "Yi " +0x7f9c: "Zhu " +0x7f9d: "Di " +0x7f9e: "Xiu " +0x7f9f: "Qian " +0x7fa0: "Yi " +0x7fa1: "Xian " +0x7fa2: "Rong " +0x7fa3: "Qun " +0x7fa4: "Qun " +0x7fa5: "Qian " +0x7fa6: "Huan " +0x7fa7: "Zui " +0x7fa8: "Xian " +0x7fa9: "Yi " +0x7faa: "Yashinau " +0x7fab: "Qiang " +0x7fac: "Xian " +0x7fad: "Yu " +0x7fae: "Geng " +0x7faf: "Jie " +0x7fb0: "Tang " +0x7fb1: "Yuan " +0x7fb2: "Xi " +0x7fb3: "Fan " +0x7fb4: "Shan " +0x7fb5: "Fen " +0x7fb6: "Shan " +0x7fb7: "Lian " +0x7fb8: "Lei " +0x7fb9: "Geng " +0x7fba: "Nou " +0x7fbb: "Qiang " +0x7fbc: "Chan " +0x7fbd: "Yu " +0x7fbe: "Gong " +0x7fbf: "Yi " +0x7fc0: "Chong " +0x7fc1: "Weng " +0x7fc2: "Fen " +0x7fc3: "Hong " +0x7fc4: "Chi " +0x7fc5: "Chi " +0x7fc6: "Cui " +0x7fc7: "Fu " +0x7fc8: "Xia " +0x7fc9: "Pen " +0x7fca: "Yi " +0x7fcb: "La " +0x7fcc: "Yi " +0x7fcd: "Pi " +0x7fce: "Ling " +0x7fcf: "Liu " +0x7fd0: "Zhi " +0x7fd1: "Qu " +0x7fd2: "Xi " +0x7fd3: "Xie " +0x7fd4: "Xiang " +0x7fd5: "Xi " +0x7fd6: "Xi " +0x7fd7: "Qi " +0x7fd8: "Qiao " +0x7fd9: "Hui " +0x7fda: "Hui " +0x7fdb: "Xiao " +0x7fdc: "Se " +0x7fdd: "Hong " +0x7fde: "Jiang " +0x7fdf: "Di " +0x7fe0: "Cui " +0x7fe1: "Fei " +0x7fe2: "Tao " +0x7fe3: "Sha " +0x7fe4: "Chi " +0x7fe5: "Zhu " +0x7fe6: "Jian " +0x7fe7: "Xuan " +0x7fe8: "Shi " +0x7fe9: "Pian " +0x7fea: "Zong " +0x7feb: "Wan " +0x7fec: "Hui " +0x7fed: "Hou " +0x7fee: "He " +0x7fef: "He " +0x7ff0: "Han " +0x7ff1: "Ao " +0x7ff2: "Piao " +0x7ff3: "Yi " +0x7ff4: "Lian " +0x7ff5: "Qu " +0x7ff6: "[?] " +0x7ff7: "Lin " +0x7ff8: "Pen " +0x7ff9: "Qiao " +0x7ffa: "Ao " +0x7ffb: "Fan " +0x7ffc: "Yi " +0x7ffd: "Hui " +0x7ffe: "Xuan " +0x7fff: "Dao " +/* x080 */ +0x8000: "Yao " +0x8001: "Lao " +0x8002: "[?] " +0x8003: "Kao " +0x8004: "Mao " +0x8005: "Zhe " +0x8006: "Qi " +0x8007: "Gou " +0x8008: "Gou " +0x8009: "Gou " +0x800a: "Die " +0x800b: "Die " +0x800c: "Er " +0x800d: "Shua " +0x800e: "Ruan " +0x800f: "Er " +0x8010: "Nai " +0x8011: "Zhuan " +0x8012: "Lei " +0x8013: "Ting " +0x8014: "Zi " +0x8015: "Geng " +0x8016: "Chao " +0x8017: "Hao " +0x8018: "Yun " +0x8019: "Pa " +0x801a: "Pi " +0x801b: "Chi " +0x801c: "Si " +0x801d: "Chu " +0x801e: "Jia " +0x801f: "Ju " +0x8020: "He " +0x8021: "Chu " +0x8022: "Lao " +0x8023: "Lun " +0x8024: "Ji " +0x8025: "Tang " +0x8026: "Ou " +0x8027: "Lou " +0x8028: "Nou " +0x8029: "Gou " +0x802a: "Pang " +0x802b: "Ze " +0x802c: "Lou " +0x802d: "Ji " +0x802e: "Lao " +0x802f: "Huo " +0x8030: "You " +0x8031: "Mo " +0x8032: "Huai " +0x8033: "Er " +0x8034: "Zhe " +0x8035: "Ting " +0x8036: "Ye " +0x8037: "Da " +0x8038: "Song " +0x8039: "Qin " +0x803a: "Yun " +0x803b: "Chi " +0x803c: "Dan " +0x803d: "Dan " +0x803e: "Hong " +0x803f: "Geng " +0x8040: "Zhi " +0x8041: "[?] " +0x8042: "Nie " +0x8043: "Dan " +0x8044: "Zhen " +0x8045: "Che " +0x8046: "Ling " +0x8047: "Zheng " +0x8048: "You " +0x8049: "Wa " +0x804a: "Liao " +0x804b: "Long " +0x804c: "Zhi " +0x804d: "Ning " +0x804e: "Tiao " +0x804f: "Er " +0x8050: "Ya " +0x8051: "Die " +0x8052: "Gua " +0x8053: "[?] " +0x8054: "Lian " +0x8055: "Hao " +0x8056: "Sheng " +0x8057: "Lie " +0x8058: "Pin " +0x8059: "Jing " +0x805a: "Ju " +0x805b: "Bi " +0x805c: "Di " +0x805d: "Guo " +0x805e: "Wen " +0x805f: "Xu " +0x8060: "Ping " +0x8061: "Cong " +0x8062: "Shikato " +0x8063: "[?] " +0x8064: "Ting " +0x8065: "Yu " +0x8066: "Cong " +0x8067: "Kui " +0x8068: "Tsuraneru " +0x8069: "Kui " +0x806a: "Cong " +0x806b: "Lian " +0x806c: "Weng " +0x806d: "Kui " +0x806e: "Lian " +0x806f: "Lian " +0x8070: "Cong " +0x8071: "Ao " +0x8072: "Sheng " +0x8073: "Song " +0x8074: "Ting " +0x8075: "Kui " +0x8076: "Nie " +0x8077: "Zhi " +0x8078: "Dan " +0x8079: "Ning " +0x807a: "Qie " +0x807b: "Ji " +0x807c: "Ting " +0x807d: "Ting " +0x807e: "Long " +0x807f: "Yu " +0x8080: "Yu " +0x8081: "Zhao " +0x8082: "Si " +0x8083: "Su " +0x8084: "Yi " +0x8085: "Su " +0x8086: "Si " +0x8087: "Zhao " +0x8088: "Zhao " +0x8089: "Rou " +0x808a: "Yi " +0x808b: "Le " +0x808c: "Ji " +0x808d: "Qiu " +0x808e: "Ken " +0x808f: "Cao " +0x8090: "Ge " +0x8091: "Di " +0x8092: "Huan " +0x8093: "Huang " +0x8094: "Yi " +0x8095: "Ren " +0x8096: "Xiao " +0x8097: "Ru " +0x8098: "Zhou " +0x8099: "Yuan " +0x809a: "Du " +0x809b: "Gang " +0x809c: "Rong " +0x809d: "Gan " +0x809e: "Cha " +0x809f: "Wo " +0x80a0: "Chang " +0x80a1: "Gu " +0x80a2: "Zhi " +0x80a3: "Han " +0x80a4: "Fu " +0x80a5: "Fei " +0x80a6: "Fen " +0x80a7: "Pei " +0x80a8: "Pang " +0x80a9: "Jian " +0x80aa: "Fang " +0x80ab: "Zhun " +0x80ac: "You " +0x80ad: "Na " +0x80ae: "Hang " +0x80af: "Ken " +0x80b0: "Ran " +0x80b1: "Gong " +0x80b2: "Yu " +0x80b3: "Wen " +0x80b4: "Yao " +0x80b5: "Jin " +0x80b6: "Pi " +0x80b7: "Qian " +0x80b8: "Xi " +0x80b9: "Xi " +0x80ba: "Fei " +0x80bb: "Ken " +0x80bc: "Jing " +0x80bd: "Tai " +0x80be: "Shen " +0x80bf: "Zhong " +0x80c0: "Zhang " +0x80c1: "Xie " +0x80c2: "Shen " +0x80c3: "Wei " +0x80c4: "Zhou " +0x80c5: "Die " +0x80c6: "Dan " +0x80c7: "Fei " +0x80c8: "Ba " +0x80c9: "Bo " +0x80ca: "Qu " +0x80cb: "Tian " +0x80cc: "Bei " +0x80cd: "Gua " +0x80ce: "Tai " +0x80cf: "Zi " +0x80d0: "Ku " +0x80d1: "Zhi " +0x80d2: "Ni " +0x80d3: "Ping " +0x80d4: "Zi " +0x80d5: "Fu " +0x80d6: "Pang " +0x80d7: "Zhen " +0x80d8: "Xian " +0x80d9: "Zuo " +0x80da: "Pei " +0x80db: "Jia " +0x80dc: "Sheng " +0x80dd: "Zhi " +0x80de: "Bao " +0x80df: "Mu " +0x80e0: "Qu " +0x80e1: "Hu " +0x80e2: "Ke " +0x80e3: "Yi " +0x80e4: "Yin " +0x80e5: "Xu " +0x80e6: "Yang " +0x80e7: "Long " +0x80e8: "Dong " +0x80e9: "Ka " +0x80ea: "Lu " +0x80eb: "Jing " +0x80ec: "Nu " +0x80ed: "Yan " +0x80ee: "Pang " +0x80ef: "Kua " +0x80f0: "Yi " +0x80f1: "Guang " +0x80f2: "Gai " +0x80f3: "Ge " +0x80f4: "Dong " +0x80f5: "Zhi " +0x80f6: "Xiao " +0x80f7: "Xiong " +0x80f8: "Xiong " +0x80f9: "Er " +0x80fa: "E " +0x80fb: "Xing " +0x80fc: "Pian " +0x80fd: "Neng " +0x80fe: "Zi " +0x80ff: "Gui " +/* x081 */ +0x8100: "Cheng " +0x8101: "Tiao " +0x8102: "Zhi " +0x8103: "Cui " +0x8104: "Mei " +0x8105: "Xie " +0x8106: "Cui " +0x8107: "Xie " +0x8108: "Mo " +0x8109: "Mai " +0x810a: "Ji " +0x810b: "Obiyaakasu " +0x810c: "[?] " +0x810d: "Kuai " +0x810e: "Sa " +0x810f: "Zang " +0x8110: "Qi " +0x8111: "Nao " +0x8112: "Mi " +0x8113: "Nong " +0x8114: "Luan " +0x8115: "Wan " +0x8116: "Bo " +0x8117: "Wen " +0x8118: "Guan " +0x8119: "Qiu " +0x811a: "Jiao " +0x811b: "Jing " +0x811c: "Rou " +0x811d: "Heng " +0x811e: "Cuo " +0x811f: "Lie " +0x8120: "Shan " +0x8121: "Ting " +0x8122: "Mei " +0x8123: "Chun " +0x8124: "Shen " +0x8125: "Xie " +0x8126: "De " +0x8127: "Zui " +0x8128: "Cu " +0x8129: "Xiu " +0x812a: "Xin " +0x812b: "Tuo " +0x812c: "Pao " +0x812d: "Cheng " +0x812e: "Nei " +0x812f: "Fu " +0x8130: "Dou " +0x8131: "Tuo " +0x8132: "Niao " +0x8133: "Noy " +0x8134: "Pi " +0x8135: "Gu " +0x8136: "Gua " +0x8137: "Li " +0x8138: "Lian " +0x8139: "Zhang " +0x813a: "Cui " +0x813b: "Jie " +0x813c: "Liang " +0x813d: "Zhou " +0x813e: "Pi " +0x813f: "Biao " +0x8140: "Lun " +0x8141: "Pian " +0x8142: "Guo " +0x8143: "Kui " +0x8144: "Chui " +0x8145: "Dan " +0x8146: "Tian " +0x8147: "Nei " +0x8148: "Jing " +0x8149: "Jie " +0x814a: "La " +0x814b: "Yi " +0x814c: "An " +0x814d: "Ren " +0x814e: "Shen " +0x814f: "Chuo " +0x8150: "Fu " +0x8151: "Fu " +0x8152: "Ju " +0x8153: "Fei " +0x8154: "Qiang " +0x8155: "Wan " +0x8156: "Dong " +0x8157: "Pi " +0x8158: "Guo " +0x8159: "Zong " +0x815a: "Ding " +0x815b: "Wu " +0x815c: "Mei " +0x815d: "Ruan " +0x815e: "Zhuan " +0x815f: "Zhi " +0x8160: "Cou " +0x8161: "Gua " +0x8162: "Ou " +0x8163: "Di " +0x8164: "An " +0x8165: "Xing " +0x8166: "Nao " +0x8167: "Yu " +0x8168: "Chuan " +0x8169: "Nan " +0x816a: "Yun " +0x816b: "Zhong " +0x816c: "Rou " +0x816d: "E " +0x816e: "Sai " +0x816f: "Tu " +0x8170: "Yao " +0x8171: "Jian " +0x8172: "Wei " +0x8173: "Jiao " +0x8174: "Yu " +0x8175: "Jia " +0x8176: "Duan " +0x8177: "Bi " +0x8178: "Chang " +0x8179: "Fu " +0x817a: "Xian " +0x817b: "Ni " +0x817c: "Mian " +0x817d: "Wa " +0x817e: "Teng " +0x817f: "Tui " +0x8180: "Bang " +0x8181: "Qian " +0x8182: "Lu " +0x8183: "Wa " +0x8184: "Sou " +0x8185: "Tang " +0x8186: "Su " +0x8187: "Zhui " +0x8188: "Ge " +0x8189: "Yi " +0x818a: "Bo " +0x818b: "Liao " +0x818c: "Ji " +0x818d: "Pi " +0x818e: "Xie " +0x818f: "Gao " +0x8190: "Lu " +0x8191: "Bin " +0x8192: "Ou " +0x8193: "Chang " +0x8194: "Lu " +0x8195: "Guo " +0x8196: "Pang " +0x8197: "Chuai " +0x8198: "Piao " +0x8199: "Jiang " +0x819a: "Fu " +0x819b: "Tang " +0x819c: "Mo " +0x819d: "Xi " +0x819e: "Zhuan " +0x819f: "Lu " +0x81a0: "Jiao " +0x81a1: "Ying " +0x81a2: "Lu " +0x81a3: "Zhi " +0x81a4: "Tara " +0x81a5: "Chun " +0x81a6: "Lian " +0x81a7: "Tong " +0x81a8: "Peng " +0x81a9: "Ni " +0x81aa: "Zha " +0x81ab: "Liao " +0x81ac: "Cui " +0x81ad: "Gui " +0x81ae: "Xiao " +0x81af: "Teng " +0x81b0: "Fan " +0x81b1: "Zhi " +0x81b2: "Jiao " +0x81b3: "Shan " +0x81b4: "Wu " +0x81b5: "Cui " +0x81b6: "Run " +0x81b7: "Xiang " +0x81b8: "Sui " +0x81b9: "Fen " +0x81ba: "Ying " +0x81bb: "Tan " +0x81bc: "Zhua " +0x81bd: "Dan " +0x81be: "Kuai " +0x81bf: "Nong " +0x81c0: "Tun " +0x81c1: "Lian " +0x81c2: "Bi " +0x81c3: "Yong " +0x81c4: "Jue " +0x81c5: "Chu " +0x81c6: "Yi " +0x81c7: "Juan " +0x81c8: "La " +0x81c9: "Lian " +0x81ca: "Sao " +0x81cb: "Tun " +0x81cc: "Gu " +0x81cd: "Qi " +0x81ce: "Cui " +0x81cf: "Bin " +0x81d0: "Xun " +0x81d1: "Ru " +0x81d2: "Huo " +0x81d3: "Zang " +0x81d4: "Xian " +0x81d5: "Biao " +0x81d6: "Xing " +0x81d7: "Kuan " +0x81d8: "La " +0x81d9: "Yan " +0x81da: "Lu " +0x81db: "Huo " +0x81dc: "Zang " +0x81dd: "Luo " +0x81de: "Qu " +0x81df: "Zang " +0x81e0: "Luan " +0x81e1: "Ni " +0x81e2: "Zang " +0x81e3: "Chen " +0x81e4: "Qian " +0x81e5: "Wo " +0x81e6: "Guang " +0x81e7: "Zang " +0x81e8: "Lin " +0x81e9: "Guang " +0x81ea: "Zi " +0x81eb: "Jiao " +0x81ec: "Nie " +0x81ed: "Chou " +0x81ee: "Ji " +0x81ef: "Gao " +0x81f0: "Chou " +0x81f1: "Mian " +0x81f2: "Nie " +0x81f3: "Zhi " +0x81f4: "Zhi " +0x81f5: "Ge " +0x81f6: "Jian " +0x81f7: "Die " +0x81f8: "Zhi " +0x81f9: "Xiu " +0x81fa: "Tai " +0x81fb: "Zhen " +0x81fc: "Jiu " +0x81fd: "Xian " +0x81fe: "Yu " +0x81ff: "Cha " +/* x082 */ +0x8200: "Yao " +0x8201: "Yu " +0x8202: "Chong " +0x8203: "Xi " +0x8204: "Xi " +0x8205: "Jiu " +0x8206: "Yu " +0x8207: "Yu " +0x8208: "Xing " +0x8209: "Ju " +0x820a: "Jiu " +0x820b: "Xin " +0x820c: "She " +0x820d: "She " +0x820e: "Yadoru " +0x820f: "Jiu " +0x8210: "Shi " +0x8211: "Tan " +0x8212: "Shu " +0x8213: "Shi " +0x8214: "Tian " +0x8215: "Dan " +0x8216: "Pu " +0x8217: "Pu " +0x8218: "Guan " +0x8219: "Hua " +0x821a: "Tan " +0x821b: "Chuan " +0x821c: "Shun " +0x821d: "Xia " +0x821e: "Wu " +0x821f: "Zhou " +0x8220: "Dao " +0x8221: "Gang " +0x8222: "Shan " +0x8223: "Yi " +0x8224: "[?] " +0x8225: "Pa " +0x8226: "Tai " +0x8227: "Fan " +0x8228: "Ban " +0x8229: "Chuan " +0x822a: "Hang " +0x822b: "Fang " +0x822c: "Ban " +0x822d: "Que " +0x822e: "Hesaki " +0x822f: "Zhong " +0x8230: "Jian " +0x8231: "Cang " +0x8232: "Ling " +0x8233: "Zhu " +0x8234: "Ze " +0x8235: "Duo " +0x8236: "Bo " +0x8237: "Xian " +0x8238: "Ge " +0x8239: "Chuan " +0x823a: "Jia " +0x823b: "Lu " +0x823c: "Hong " +0x823d: "Pang " +0x823e: "Xi " +0x823f: "[?] " +0x8240: "Fu " +0x8241: "Zao " +0x8242: "Feng " +0x8243: "Li " +0x8244: "Shao " +0x8245: "Yu " +0x8246: "Lang " +0x8247: "Ting " +0x8248: "[?] " +0x8249: "Wei " +0x824a: "Bo " +0x824b: "Meng " +0x824c: "Nian " +0x824d: "Ju " +0x824e: "Huang " +0x824f: "Shou " +0x8250: "Zong " +0x8251: "Bian " +0x8252: "Mao " +0x8253: "Die " +0x8254: "[?] " +0x8255: "Bang " +0x8256: "Cha " +0x8257: "Yi " +0x8258: "Sao " +0x8259: "Cang " +0x825a: "Cao " +0x825b: "Lou " +0x825c: "Dai " +0x825d: "Sori " +0x825e: "Yao " +0x825f: "Tong " +0x8260: "Yofune " +0x8261: "Dang " +0x8262: "Tan " +0x8263: "Lu " +0x8264: "Yi " +0x8265: "Jie " +0x8266: "Jian " +0x8267: "Huo " +0x8268: "Meng " +0x8269: "Qi " +0x826a: "Lu " +0x826b: "Lu " +0x826c: "Chan " +0x826d: "Shuang " +0x826e: "Gen " +0x826f: "Liang " +0x8270: "Jian " +0x8271: "Jian " +0x8272: "Se " +0x8273: "Yan " +0x8274: "Fu " +0x8275: "Ping " +0x8276: "Yan " +0x8277: "Yan " +0x8278: "Cao " +0x8279: "Cao " +0x827a: "Yi " +0x827b: "Le " +0x827c: "Ting " +0x827d: "Qiu " +0x827e: "Ai " +0x827f: "Nai " +0x8280: "Tiao " +0x8281: "Jiao " +0x8282: "Jie " +0x8283: "Peng " +0x8284: "Wan " +0x8285: "Yi " +0x8286: "Chai " +0x8287: "Mian " +0x8288: "Mie " +0x8289: "Gan " +0x828a: "Qian " +0x828b: "Yu " +0x828c: "Yu " +0x828d: "Shuo " +0x828e: "Qiong " +0x828f: "Tu " +0x8290: "Xia " +0x8291: "Qi " +0x8292: "Mang " +0x8293: "Zi " +0x8294: "Hui " +0x8295: "Sui " +0x8296: "Zhi " +0x8297: "Xiang " +0x8298: "Bi " +0x8299: "Fu " +0x829a: "Tun " +0x829b: "Wei " +0x829c: "Wu " +0x829d: "Zhi " +0x829e: "Qi " +0x829f: "Shan " +0x82a0: "Wen " +0x82a1: "Qian " +0x82a2: "Ren " +0x82a3: "Fou " +0x82a4: "Kou " +0x82a5: "Jie " +0x82a6: "Lu " +0x82a7: "Xu " +0x82a8: "Ji " +0x82a9: "Qin " +0x82aa: "Qi " +0x82ab: "Yuan " +0x82ac: "Fen " +0x82ad: "Ba " +0x82ae: "Rui " +0x82af: "Xin " +0x82b0: "Ji " +0x82b1: "Hua " +0x82b2: "Hua " +0x82b3: "Fang " +0x82b4: "Wu " +0x82b5: "Jue " +0x82b6: "Gou " +0x82b7: "Zhi " +0x82b8: "Yun " +0x82b9: "Qin " +0x82ba: "Ao " +0x82bb: "Chu " +0x82bc: "Mao " +0x82bd: "Ya " +0x82be: "Fei " +0x82bf: "Reng " +0x82c0: "Hang " +0x82c1: "Cong " +0x82c2: "Yin " +0x82c3: "You " +0x82c4: "Bian " +0x82c5: "Yi " +0x82c6: "Susa " +0x82c7: "Wei " +0x82c8: "Li " +0x82c9: "Pi " +0x82ca: "E " +0x82cb: "Xian " +0x82cc: "Chang " +0x82cd: "Cang " +0x82ce: "Meng " +0x82cf: "Su " +0x82d0: "Yi " +0x82d1: "Yuan " +0x82d2: "Ran " +0x82d3: "Ling " +0x82d4: "Tai " +0x82d5: "Tiao " +0x82d6: "Di " +0x82d7: "Miao " +0x82d8: "Qiong " +0x82d9: "Li " +0x82da: "Yong " +0x82db: "Ke " +0x82dc: "Mu " +0x82dd: "Pei " +0x82de: "Bao " +0x82df: "Gou " +0x82e0: "Min " +0x82e1: "Yi " +0x82e2: "Yi " +0x82e3: "Ju " +0x82e4: "Pi " +0x82e5: "Ruo " +0x82e6: "Ku " +0x82e7: "Zhu " +0x82e8: "Ni " +0x82e9: "Bo " +0x82ea: "Bing " +0x82eb: "Shan " +0x82ec: "Qiu " +0x82ed: "Yao " +0x82ee: "Xian " +0x82ef: "Ben " +0x82f0: "Hong " +0x82f1: "Ying " +0x82f2: "Zha " +0x82f3: "Dong " +0x82f4: "Ju " +0x82f5: "Die " +0x82f6: "Nie " +0x82f7: "Gan " +0x82f8: "Hu " +0x82f9: "Ping " +0x82fa: "Mei " +0x82fb: "Fu " +0x82fc: "Sheng " +0x82fd: "Gu " +0x82fe: "Bi " +0x82ff: "Wei " +/* x083 */ +0x8300: "Fu " +0x8301: "Zhuo " +0x8302: "Mao " +0x8303: "Fan " +0x8304: "Qie " +0x8305: "Mao " +0x8306: "Mao " +0x8307: "Ba " +0x8308: "Zi " +0x8309: "Mo " +0x830a: "Zi " +0x830b: "Di " +0x830c: "Chi " +0x830d: "Ji " +0x830e: "Jing " +0x830f: "Long " +0x8310: "[?] " +0x8311: "Niao " +0x8312: "[?] " +0x8313: "Xue " +0x8314: "Ying " +0x8315: "Qiong " +0x8316: "Ge " +0x8317: "Ming " +0x8318: "Li " +0x8319: "Rong " +0x831a: "Yin " +0x831b: "Gen " +0x831c: "Qian " +0x831d: "Chai " +0x831e: "Chen " +0x831f: "Yu " +0x8320: "Xiu " +0x8321: "Zi " +0x8322: "Lie " +0x8323: "Wu " +0x8324: "Ji " +0x8325: "Kui " +0x8326: "Ce " +0x8327: "Chong " +0x8328: "Ci " +0x8329: "Gou " +0x832a: "Guang " +0x832b: "Mang " +0x832c: "Chi " +0x832d: "Jiao " +0x832e: "Jiao " +0x832f: "Fu " +0x8330: "Yu " +0x8331: "Zhu " +0x8332: "Zi " +0x8333: "Jiang " +0x8334: "Hui " +0x8335: "Yin " +0x8336: "Cha " +0x8337: "Fa " +0x8338: "Rong " +0x8339: "Ru " +0x833a: "Chong " +0x833b: "Mang " +0x833c: "Tong " +0x833d: "Zhong " +0x833e: "[?] " +0x833f: "Zhu " +0x8340: "Xun " +0x8341: "Huan " +0x8342: "Kua " +0x8343: "Quan " +0x8344: "Gai " +0x8345: "Da " +0x8346: "Jing " +0x8347: "Xing " +0x8348: "Quan " +0x8349: "Cao " +0x834a: "Jing " +0x834b: "Er " +0x834c: "An " +0x834d: "Shou " +0x834e: "Chi " +0x834f: "Ren " +0x8350: "Jian " +0x8351: "Ti " +0x8352: "Huang " +0x8353: "Ping " +0x8354: "Li " +0x8355: "Jin " +0x8356: "Lao " +0x8357: "Shu " +0x8358: "Zhuang " +0x8359: "Da " +0x835a: "Jia " +0x835b: "Rao " +0x835c: "Bi " +0x835d: "Ze " +0x835e: "Qiao " +0x835f: "Hui " +0x8360: "Qi " +0x8361: "Dang " +0x8362: "[?] " +0x8363: "Rong " +0x8364: "Hun " +0x8365: "Ying " +0x8366: "Luo " +0x8367: "Ying " +0x8368: "Xun " +0x8369: "Jin " +0x836a: "Sun " +0x836b: "Yin " +0x836c: "Mai " +0x836d: "Hong " +0x836e: "Zhou " +0x836f: "Yao " +0x8370: "Du " +0x8371: "Wei " +0x8372: "Chu " +0x8373: "Dou " +0x8374: "Fu " +0x8375: "Ren " +0x8376: "Yin " +0x8377: "He " +0x8378: "Bi " +0x8379: "Bu " +0x837a: "Yun " +0x837b: "Di " +0x837c: "Tu " +0x837d: "Sui " +0x837e: "Sui " +0x837f: "Cheng " +0x8380: "Chen " +0x8381: "Wu " +0x8382: "Bie " +0x8383: "Xi " +0x8384: "Geng " +0x8385: "Li " +0x8386: "Fu " +0x8387: "Zhu " +0x8388: "Mo " +0x8389: "Li " +0x838a: "Zhuang " +0x838b: "Ji " +0x838c: "Duo " +0x838d: "Qiu " +0x838e: "Sha " +0x838f: "Suo " +0x8390: "Chen " +0x8391: "Feng " +0x8392: "Ju " +0x8393: "Mei " +0x8394: "Meng " +0x8395: "Xing " +0x8396: "Jing " +0x8397: "Che " +0x8398: "Xin " +0x8399: "Jun " +0x839a: "Yan " +0x839b: "Ting " +0x839c: "Diao " +0x839d: "Cuo " +0x839e: "Wan " +0x839f: "Han " +0x83a0: "You " +0x83a1: "Cuo " +0x83a2: "Jia " +0x83a3: "Wang " +0x83a4: "You " +0x83a5: "Niu " +0x83a6: "Shao " +0x83a7: "Xian " +0x83a8: "Lang " +0x83a9: "Fu " +0x83aa: "E " +0x83ab: "Mo " +0x83ac: "Wen " +0x83ad: "Jie " +0x83ae: "Nan " +0x83af: "Mu " +0x83b0: "Kan " +0x83b1: "Lai " +0x83b2: "Lian " +0x83b3: "Shi " +0x83b4: "Wo " +0x83b5: "Usagi " +0x83b6: "Lian " +0x83b7: "Huo " +0x83b8: "You " +0x83b9: "Ying " +0x83ba: "Ying " +0x83bb: "Nuc " +0x83bc: "Chun " +0x83bd: "Mang " +0x83be: "Mang " +0x83bf: "Ci " +0x83c0: "Wan " +0x83c1: "Jing " +0x83c2: "Di " +0x83c3: "Qu " +0x83c4: "Dong " +0x83c5: "Jian " +0x83c6: "Zou " +0x83c7: "Gu " +0x83c8: "La " +0x83c9: "Lu " +0x83ca: "Ju " +0x83cb: "Wei " +0x83cc: "Jun " +0x83cd: "Nie " +0x83ce: "Kun " +0x83cf: "He " +0x83d0: "Pu " +0x83d1: "Zi " +0x83d2: "Gao " +0x83d3: "Guo " +0x83d4: "Fu " +0x83d5: "Lun " +0x83d6: "Chang " +0x83d7: "Chou " +0x83d8: "Song " +0x83d9: "Chui " +0x83da: "Zhan " +0x83db: "Men " +0x83dc: "Cai " +0x83dd: "Ba " +0x83de: "Li " +0x83df: "Tu " +0x83e0: "Bo " +0x83e1: "Han " +0x83e2: "Bao " +0x83e3: "Qin " +0x83e4: "Juan " +0x83e5: "Xi " +0x83e6: "Qin " +0x83e7: "Di " +0x83e8: "Jie " +0x83e9: "Pu " +0x83ea: "Dang " +0x83eb: "Jin " +0x83ec: "Zhao " +0x83ed: "Tai " +0x83ee: "Geng " +0x83ef: "Hua " +0x83f0: "Gu " +0x83f1: "Ling " +0x83f2: "Fei " +0x83f3: "Jin " +0x83f4: "An " +0x83f5: "Wang " +0x83f6: "Beng " +0x83f7: "Zhou " +0x83f8: "Yan " +0x83f9: "Ju " +0x83fa: "Jian " +0x83fb: "Lin " +0x83fc: "Tan " +0x83fd: "Shu " +0x83fe: "Tian " +0x83ff: "Dao " +/* x084 */ +0x8400: "Hu " +0x8401: "Qi " +0x8402: "He " +0x8403: "Cui " +0x8404: "Tao " +0x8405: "Chun " +0x8406: "Bei " +0x8407: "Chang " +0x8408: "Huan " +0x8409: "Fei " +0x840a: "Lai " +0x840b: "Qi " +0x840c: "Meng " +0x840d: "Ping " +0x840e: "Wei " +0x840f: "Dan " +0x8410: "Sha " +0x8411: "Huan " +0x8412: "Yan " +0x8413: "Yi " +0x8414: "Tiao " +0x8415: "Qi " +0x8416: "Wan " +0x8417: "Ce " +0x8418: "Nai " +0x8419: "Kutabireru " +0x841a: "Tuo " +0x841b: "Jiu " +0x841c: "Tie " +0x841d: "Luo " +0x841e: "[?] " +0x841f: "[?] " +0x8420: "Meng " +0x8421: "[?] " +0x8422: "Yaji " +0x8423: "[?] " +0x8424: "Ying " +0x8425: "Ying " +0x8426: "Ying " +0x8427: "Xiao " +0x8428: "Sa " +0x8429: "Qiu " +0x842a: "Ke " +0x842b: "Xiang " +0x842c: "Wan " +0x842d: "Yu " +0x842e: "Yu " +0x842f: "Fu " +0x8430: "Lian " +0x8431: "Xuan " +0x8432: "Yuan " +0x8433: "Nan " +0x8434: "Ze " +0x8435: "Wo " +0x8436: "Chun " +0x8437: "Xiao " +0x8438: "Yu " +0x8439: "Pian " +0x843a: "Mao " +0x843b: "An " +0x843c: "E " +0x843d: "Luo " +0x843e: "Ying " +0x843f: "Huo " +0x8440: "Gua " +0x8441: "Jiang " +0x8442: "Mian " +0x8443: "Zuo " +0x8444: "Zuo " +0x8445: "Ju " +0x8446: "Bao " +0x8447: "Rou " +0x8448: "Xi " +0x8449: "Xie " +0x844a: "An " +0x844b: "Qu " +0x844c: "Jian " +0x844d: "Fu " +0x844e: "Lu " +0x844f: "Jing " +0x8450: "Pen " +0x8451: "Feng " +0x8452: "Hong " +0x8453: "Hong " +0x8454: "Hou " +0x8455: "Yan " +0x8456: "Tu " +0x8457: "Zhu " +0x8458: "Zi " +0x8459: "Xiang " +0x845a: "Shen " +0x845b: "Ge " +0x845c: "Jie " +0x845d: "Jing " +0x845e: "Mi " +0x845f: "Huang " +0x8460: "Shen " +0x8461: "Pu " +0x8462: "Gai " +0x8463: "Dong " +0x8464: "Zhou " +0x8465: "Qian " +0x8466: "Wei " +0x8467: "Bo " +0x8468: "Wei " +0x8469: "Pa " +0x846a: "Ji " +0x846b: "Hu " +0x846c: "Zang " +0x846d: "Jia " +0x846e: "Duan " +0x846f: "Yao " +0x8470: "Jun " +0x8471: "Cong " +0x8472: "Quan " +0x8473: "Wei " +0x8474: "Xian " +0x8475: "Kui " +0x8476: "Ting " +0x8477: "Hun " +0x8478: "Xi " +0x8479: "Shi " +0x847a: "Qi " +0x847b: "Lan " +0x847c: "Zong " +0x847d: "Yao " +0x847e: "Yuan " +0x847f: "Mei " +0x8480: "Yun " +0x8481: "Shu " +0x8482: "Di " +0x8483: "Zhuan " +0x8484: "Guan " +0x8485: "Sukumo " +0x8486: "Xue " +0x8487: "Chan " +0x8488: "Kai " +0x8489: "Kui " +0x848a: "[?] " +0x848b: "Jiang " +0x848c: "Lou " +0x848d: "Wei " +0x848e: "Pai " +0x848f: "[?] " +0x8490: "Sou " +0x8491: "Yin " +0x8492: "Shi " +0x8493: "Chun " +0x8494: "Shi " +0x8495: "Yun " +0x8496: "Zhen " +0x8497: "Lang " +0x8498: "Nu " +0x8499: "Meng " +0x849a: "He " +0x849b: "Que " +0x849c: "Suan " +0x849d: "Yuan " +0x849e: "Li " +0x849f: "Ju " +0x84a0: "Xi " +0x84a1: "Pang " +0x84a2: "Chu " +0x84a3: "Xu " +0x84a4: "Tu " +0x84a5: "Liu " +0x84a6: "Wo " +0x84a7: "Zhen " +0x84a8: "Qian " +0x84a9: "Zu " +0x84aa: "Po " +0x84ab: "Cuo " +0x84ac: "Yuan " +0x84ad: "Chu " +0x84ae: "Yu " +0x84af: "Kuai " +0x84b0: "Pan " +0x84b1: "Pu " +0x84b2: "Pu " +0x84b3: "Na " +0x84b4: "Shuo " +0x84b5: "Xi " +0x84b6: "Fen " +0x84b7: "Yun " +0x84b8: "Zheng " +0x84b9: "Jian " +0x84ba: "Ji " +0x84bb: "Ruo " +0x84bc: "Cang " +0x84bd: "En " +0x84be: "Mi " +0x84bf: "Hao " +0x84c0: "Sun " +0x84c1: "Zhen " +0x84c2: "Ming " +0x84c3: "Sou " +0x84c4: "Xu " +0x84c5: "Liu " +0x84c6: "Xi " +0x84c7: "Gu " +0x84c8: "Lang " +0x84c9: "Rong " +0x84ca: "Weng " +0x84cb: "Gai " +0x84cc: "Cuo " +0x84cd: "Shi " +0x84ce: "Tang " +0x84cf: "Luo " +0x84d0: "Ru " +0x84d1: "Suo " +0x84d2: "Xian " +0x84d3: "Bei " +0x84d4: "Yao " +0x84d5: "Gui " +0x84d6: "Bi " +0x84d7: "Zong " +0x84d8: "Gun " +0x84d9: "Za " +0x84da: "Xiu " +0x84db: "Ce " +0x84dc: "Hai " +0x84dd: "Lan " +0x84de: "[?] " +0x84df: "Ji " +0x84e0: "Li " +0x84e1: "Can " +0x84e2: "Lang " +0x84e3: "Yu " +0x84e4: "[?] " +0x84e5: "Ying " +0x84e6: "Mo " +0x84e7: "Diao " +0x84e8: "Tiao " +0x84e9: "Mao " +0x84ea: "Tong " +0x84eb: "Zhu " +0x84ec: "Peng " +0x84ed: "An " +0x84ee: "Lian " +0x84ef: "Cong " +0x84f0: "Xi " +0x84f1: "Ping " +0x84f2: "Qiu " +0x84f3: "Jin " +0x84f4: "Chun " +0x84f5: "Jie " +0x84f6: "Wei " +0x84f7: "Tui " +0x84f8: "Cao " +0x84f9: "Yu " +0x84fa: "Yi " +0x84fb: "Ji " +0x84fc: "Liao " +0x84fd: "Bi " +0x84fe: "Lu " +0x84ff: "Su " +/* x085 */ +0x8500: "Bu " +0x8501: "Zhang " +0x8502: "Luo " +0x8503: "Jiang " +0x8504: "Man " +0x8505: "Yan " +0x8506: "Ling " +0x8507: "Ji " +0x8508: "Piao " +0x8509: "Gun " +0x850a: "Han " +0x850b: "Di " +0x850c: "Su " +0x850d: "Lu " +0x850e: "She " +0x850f: "Shang " +0x8510: "Di " +0x8511: "Mie " +0x8512: "Xun " +0x8513: "Man " +0x8514: "Bo " +0x8515: "Di " +0x8516: "Cuo " +0x8517: "Zhe " +0x8518: "Sen " +0x8519: "Xuan " +0x851a: "Wei " +0x851b: "Hu " +0x851c: "Ao " +0x851d: "Mi " +0x851e: "Lou " +0x851f: "Cu " +0x8520: "Zhong " +0x8521: "Cai " +0x8522: "Po " +0x8523: "Jiang " +0x8524: "Mi " +0x8525: "Cong " +0x8526: "Niao " +0x8527: "Hui " +0x8528: "Jun " +0x8529: "Yin " +0x852a: "Jian " +0x852b: "Yan " +0x852c: "Shu " +0x852d: "Yin " +0x852e: "Kui " +0x852f: "Chen " +0x8530: "Hu " +0x8531: "Sha " +0x8532: "Kou " +0x8533: "Qian " +0x8534: "Ma " +0x8535: "Zang " +0x8536: "Sonoko " +0x8537: "Qiang " +0x8538: "Dou " +0x8539: "Lian " +0x853a: "Lin " +0x853b: "Kou " +0x853c: "Ai " +0x853d: "Bi " +0x853e: "Li " +0x853f: "Wei " +0x8540: "Ji " +0x8541: "Xun " +0x8542: "Sheng " +0x8543: "Fan " +0x8544: "Meng " +0x8545: "Ou " +0x8546: "Chan " +0x8547: "Dian " +0x8548: "Xun " +0x8549: "Jiao " +0x854a: "Rui " +0x854b: "Rui " +0x854c: "Lei " +0x854d: "Yu " +0x854e: "Qiao " +0x854f: "Chu " +0x8550: "Hua " +0x8551: "Jian " +0x8552: "Mai " +0x8553: "Yun " +0x8554: "Bao " +0x8555: "You " +0x8556: "Qu " +0x8557: "Lu " +0x8558: "Rao " +0x8559: "Hui " +0x855a: "E " +0x855b: "Teng " +0x855c: "Fei " +0x855d: "Jue " +0x855e: "Zui " +0x855f: "Fa " +0x8560: "Ru " +0x8561: "Fen " +0x8562: "Kui " +0x8563: "Shun " +0x8564: "Rui " +0x8565: "Ya " +0x8566: "Xu " +0x8567: "Fu " +0x8568: "Jue " +0x8569: "Dang " +0x856a: "Wu " +0x856b: "Tong " +0x856c: "Si " +0x856d: "Xiao " +0x856e: "Xi " +0x856f: "Long " +0x8570: "Yun " +0x8571: "[?] " +0x8572: "Qi " +0x8573: "Jian " +0x8574: "Yun " +0x8575: "Sun " +0x8576: "Ling " +0x8577: "Yu " +0x8578: "Xia " +0x8579: "Yong " +0x857a: "Ji " +0x857b: "Hong " +0x857c: "Si " +0x857d: "Nong " +0x857e: "Lei " +0x857f: "Xuan " +0x8580: "Yun " +0x8581: "Yu " +0x8582: "Xi " +0x8583: "Hao " +0x8584: "Bo " +0x8585: "Hao " +0x8586: "Ai " +0x8587: "Wei " +0x8588: "Hui " +0x8589: "Wei " +0x858a: "Ji " +0x858b: "Ci " +0x858c: "Xiang " +0x858d: "Luan " +0x858e: "Mie " +0x858f: "Yi " +0x8590: "Leng " +0x8591: "Jiang " +0x8592: "Can " +0x8593: "Shen " +0x8594: "Qiang " +0x8595: "Lian " +0x8596: "Ke " +0x8597: "Yuan " +0x8598: "Da " +0x8599: "Ti " +0x859a: "Tang " +0x859b: "Xie " +0x859c: "Bi " +0x859d: "Zhan " +0x859e: "Sun " +0x859f: "Lian " +0x85a0: "Fan " +0x85a1: "Ding " +0x85a2: "Jie " +0x85a3: "Gu " +0x85a4: "Xie " +0x85a5: "Shu " +0x85a6: "Jian " +0x85a7: "Kao " +0x85a8: "Hong " +0x85a9: "Sa " +0x85aa: "Xin " +0x85ab: "Xun " +0x85ac: "Yao " +0x85ad: "Hie " +0x85ae: "Sou " +0x85af: "Shu " +0x85b0: "Xun " +0x85b1: "Dui " +0x85b2: "Pin " +0x85b3: "Wei " +0x85b4: "Neng " +0x85b5: "Chou " +0x85b6: "Mai " +0x85b7: "Ru " +0x85b8: "Piao " +0x85b9: "Tai " +0x85ba: "Qi " +0x85bb: "Zao " +0x85bc: "Chen " +0x85bd: "Zhen " +0x85be: "Er " +0x85bf: "Ni " +0x85c0: "Ying " +0x85c1: "Gao " +0x85c2: "Cong " +0x85c3: "Xiao " +0x85c4: "Qi " +0x85c5: "Fa " +0x85c6: "Jian " +0x85c7: "Xu " +0x85c8: "Kui " +0x85c9: "Jie " +0x85ca: "Bian " +0x85cb: "Diao " +0x85cc: "Mi " +0x85cd: "Lan " +0x85ce: "Jin " +0x85cf: "Cang " +0x85d0: "Miao " +0x85d1: "Qiong " +0x85d2: "Qie " +0x85d3: "Xian " +0x85d4: "[?] " +0x85d5: "Ou " +0x85d6: "Xian " +0x85d7: "Su " +0x85d8: "Lu " +0x85d9: "Yi " +0x85da: "Xu " +0x85db: "Xie " +0x85dc: "Li " +0x85dd: "Yi " +0x85de: "La " +0x85df: "Lei " +0x85e0: "Xiao " +0x85e1: "Di " +0x85e2: "Zhi " +0x85e3: "Bei " +0x85e4: "Teng " +0x85e5: "Yao " +0x85e6: "Mo " +0x85e7: "Huan " +0x85e8: "Piao " +0x85e9: "Fan " +0x85ea: "Sou " +0x85eb: "Tan " +0x85ec: "Tui " +0x85ed: "Qiong " +0x85ee: "Qiao " +0x85ef: "Wei " +0x85f0: "Liu " +0x85f1: "Hui " +0x85f2: "[?] " +0x85f3: "Gao " +0x85f4: "Yun " +0x85f5: "[?] " +0x85f6: "Li " +0x85f7: "Shu " +0x85f8: "Chu " +0x85f9: "Ai " +0x85fa: "Lin " +0x85fb: "Zao " +0x85fc: "Xuan " +0x85fd: "Chen " +0x85fe: "Lai " +0x85ff: "Huo " +/* x086 */ +0x8600: "Tuo " +0x8601: "Wu " +0x8602: "Rui " +0x8603: "Rui " +0x8604: "Qi " +0x8605: "Heng " +0x8606: "Lu " +0x8607: "Su " +0x8608: "Tui " +0x8609: "Mang " +0x860a: "Yun " +0x860b: "Pin " +0x860c: "Yu " +0x860d: "Xun " +0x860e: "Ji " +0x860f: "Jiong " +0x8610: "Xian " +0x8611: "Mo " +0x8612: "Hagi " +0x8613: "Su " +0x8614: "Jiong " +0x8615: "[?] " +0x8616: "Nie " +0x8617: "Bo " +0x8618: "Rang " +0x8619: "Yi " +0x861a: "Xian " +0x861b: "Yu " +0x861c: "Ju " +0x861d: "Lian " +0x861e: "Lian " +0x861f: "Yin " +0x8620: "Qiang " +0x8621: "Ying " +0x8622: "Long " +0x8623: "Tong " +0x8624: "Wei " +0x8625: "Yue " +0x8626: "Ling " +0x8627: "Qu " +0x8628: "Yao " +0x8629: "Fan " +0x862a: "Mi " +0x862b: "Lan " +0x862c: "Kui " +0x862d: "Lan " +0x862e: "Ji " +0x862f: "Dang " +0x8630: "Katsura " +0x8631: "Lei " +0x8632: "Lei " +0x8633: "Hua " +0x8634: "Feng " +0x8635: "Zhi " +0x8636: "Wei " +0x8637: "Kui " +0x8638: "Zhan " +0x8639: "Huai " +0x863a: "Li " +0x863b: "Ji " +0x863c: "Mi " +0x863d: "Lei " +0x863e: "Huai " +0x863f: "Luo " +0x8640: "Ji " +0x8641: "Kui " +0x8642: "Lu " +0x8643: "Jian " +0x8644: "San " +0x8645: "[?] " +0x8646: "Lei " +0x8647: "Quan " +0x8648: "Xiao " +0x8649: "Yi " +0x864a: "Luan " +0x864b: "Men " +0x864c: "Bie " +0x864d: "Hu " +0x864e: "Hu " +0x864f: "Lu " +0x8650: "Nue " +0x8651: "Lu " +0x8652: "Si " +0x8653: "Xiao " +0x8654: "Qian " +0x8655: "Chu " +0x8656: "Hu " +0x8657: "Xu " +0x8658: "Cuo " +0x8659: "Fu " +0x865a: "Xu " +0x865b: "Xu " +0x865c: "Lu " +0x865d: "Hu " +0x865e: "Yu " +0x865f: "Hao " +0x8660: "Jiao " +0x8661: "Ju " +0x8662: "Guo " +0x8663: "Bao " +0x8664: "Yan " +0x8665: "Zhan " +0x8666: "Zhan " +0x8667: "Kui " +0x8668: "Ban " +0x8669: "Xi " +0x866a: "Shu " +0x866b: "Chong " +0x866c: "Qiu " +0x866d: "Diao " +0x866e: "Ji " +0x866f: "Qiu " +0x8670: "Cheng " +0x8671: "Shi " +0x8672: "[?] " +0x8673: "Di " +0x8674: "Zhe " +0x8675: "She " +0x8676: "Yu " +0x8677: "Gan " +0x8678: "Zi " +0x8679: "Hong " +0x867a: "Hui " +0x867b: "Meng " +0x867c: "Ge " +0x867d: "Sui " +0x867e: "Xia " +0x867f: "Chai " +0x8680: "Shi " +0x8681: "Yi " +0x8682: "Ma " +0x8683: "Xiang " +0x8684: "Fang " +0x8685: "E " +0x8686: "Pa " +0x8687: "Chi " +0x8688: "Qian " +0x8689: "Wen " +0x868a: "Wen " +0x868b: "Rui " +0x868c: "Bang " +0x868d: "Bi " +0x868e: "Yue " +0x868f: "Yue " +0x8690: "Jun " +0x8691: "Qi " +0x8692: "Ran " +0x8693: "Yin " +0x8694: "Qi " +0x8695: "Tian " +0x8696: "Yuan " +0x8697: "Jue " +0x8698: "Hui " +0x8699: "Qin " +0x869a: "Qi " +0x869b: "Zhong " +0x869c: "Ya " +0x869d: "Ci " +0x869e: "Mu " +0x869f: "Wang " +0x86a0: "Fen " +0x86a1: "Fen " +0x86a2: "Hang " +0x86a3: "Gong " +0x86a4: "Zao " +0x86a5: "Fu " +0x86a6: "Ran " +0x86a7: "Jie " +0x86a8: "Fu " +0x86a9: "Chi " +0x86aa: "Dou " +0x86ab: "Piao " +0x86ac: "Xian " +0x86ad: "Ni " +0x86ae: "Te " +0x86af: "Qiu " +0x86b0: "You " +0x86b1: "Zha " +0x86b2: "Ping " +0x86b3: "Chi " +0x86b4: "You " +0x86b5: "He " +0x86b6: "Han " +0x86b7: "Ju " +0x86b8: "Li " +0x86b9: "Fu " +0x86ba: "Ran " +0x86bb: "Zha " +0x86bc: "Gou " +0x86bd: "Pi " +0x86be: "Bo " +0x86bf: "Xian " +0x86c0: "Zhu " +0x86c1: "Diao " +0x86c2: "Bie " +0x86c3: "Bing " +0x86c4: "Gu " +0x86c5: "Ran " +0x86c6: "Qu " +0x86c7: "She " +0x86c8: "Tie " +0x86c9: "Ling " +0x86ca: "Gu " +0x86cb: "Dan " +0x86cc: "Gu " +0x86cd: "Ying " +0x86ce: "Li " +0x86cf: "Cheng " +0x86d0: "Qu " +0x86d1: "Mou " +0x86d2: "Ge " +0x86d3: "Ci " +0x86d4: "Hui " +0x86d5: "Hui " +0x86d6: "Mang " +0x86d7: "Fu " +0x86d8: "Yang " +0x86d9: "Wa " +0x86da: "Lie " +0x86db: "Zhu " +0x86dc: "Yi " +0x86dd: "Xian " +0x86de: "Kuo " +0x86df: "Jiao " +0x86e0: "Li " +0x86e1: "Yi " +0x86e2: "Ping " +0x86e3: "Ji " +0x86e4: "Ha " +0x86e5: "She " +0x86e6: "Yi " +0x86e7: "Wang " +0x86e8: "Mo " +0x86e9: "Qiong " +0x86ea: "Qie " +0x86eb: "Gui " +0x86ec: "Gong " +0x86ed: "Zhi " +0x86ee: "Man " +0x86ef: "Ebi " +0x86f0: "Zhi " +0x86f1: "Jia " +0x86f2: "Rao " +0x86f3: "Si " +0x86f4: "Qi " +0x86f5: "Xing " +0x86f6: "Lie " +0x86f7: "Qiu " +0x86f8: "Shao " +0x86f9: "Yong " +0x86fa: "Jia " +0x86fb: "Shui " +0x86fc: "Che " +0x86fd: "Bai " +0x86fe: "E " +0x86ff: "Han " +/* x087 */ +0x8700: "Shu " +0x8701: "Xuan " +0x8702: "Feng " +0x8703: "Shen " +0x8704: "Zhen " +0x8705: "Fu " +0x8706: "Xian " +0x8707: "Zhe " +0x8708: "Wu " +0x8709: "Fu " +0x870a: "Li " +0x870b: "Lang " +0x870c: "Bi " +0x870d: "Chu " +0x870e: "Yuan " +0x870f: "You " +0x8710: "Jie " +0x8711: "Dan " +0x8712: "Yan " +0x8713: "Ting " +0x8714: "Dian " +0x8715: "Shui " +0x8716: "Hui " +0x8717: "Gua " +0x8718: "Zhi " +0x8719: "Song " +0x871a: "Fei " +0x871b: "Ju " +0x871c: "Mi " +0x871d: "Qi " +0x871e: "Qi " +0x871f: "Yu " +0x8720: "Jun " +0x8721: "Zha " +0x8722: "Meng " +0x8723: "Qiang " +0x8724: "Si " +0x8725: "Xi " +0x8726: "Lun " +0x8727: "Li " +0x8728: "Die " +0x8729: "Tiao " +0x872a: "Tao " +0x872b: "Kun " +0x872c: "Gan " +0x872d: "Han " +0x872e: "Yu " +0x872f: "Bang " +0x8730: "Fei " +0x8731: "Pi " +0x8732: "Wei " +0x8733: "Dun " +0x8734: "Yi " +0x8735: "Yuan " +0x8736: "Su " +0x8737: "Quan " +0x8738: "Qian " +0x8739: "Rui " +0x873a: "Ni " +0x873b: "Qing " +0x873c: "Wei " +0x873d: "Liang " +0x873e: "Guo " +0x873f: "Wan " +0x8740: "Dong " +0x8741: "E " +0x8742: "Ban " +0x8743: "Di " +0x8744: "Wang " +0x8745: "Can " +0x8746: "Yang " +0x8747: "Ying " +0x8748: "Guo " +0x8749: "Chan " +0x874a: "[?] " +0x874b: "La " +0x874c: "Ke " +0x874d: "Ji " +0x874e: "He " +0x874f: "Ting " +0x8750: "Mai " +0x8751: "Xu " +0x8752: "Mian " +0x8753: "Yu " +0x8754: "Jie " +0x8755: "Shi " +0x8756: "Xuan " +0x8757: "Huang " +0x8758: "Yan " +0x8759: "Bian " +0x875a: "Rou " +0x875b: "Wei " +0x875c: "Fu " +0x875d: "Yuan " +0x875e: "Mei " +0x875f: "Wei " +0x8760: "Fu " +0x8761: "Ruan " +0x8762: "Xie " +0x8763: "You " +0x8764: "Qiu " +0x8765: "Mao " +0x8766: "Xia " +0x8767: "Ying " +0x8768: "Shi " +0x8769: "Chong " +0x876a: "Tang " +0x876b: "Zhu " +0x876c: "Zong " +0x876d: "Ti " +0x876e: "Fu " +0x876f: "Yuan " +0x8770: "Hui " +0x8771: "Meng " +0x8772: "La " +0x8773: "Du " +0x8774: "Hu " +0x8775: "Qiu " +0x8776: "Die " +0x8777: "Li " +0x8778: "Gua " +0x8779: "Yun " +0x877a: "Ju " +0x877b: "Nan " +0x877c: "Lou " +0x877d: "Qun " +0x877e: "Rong " +0x877f: "Ying " +0x8780: "Jiang " +0x8781: "[?] " +0x8782: "Lang " +0x8783: "Pang " +0x8784: "Si " +0x8785: "Xi " +0x8786: "Ci " +0x8787: "Xi " +0x8788: "Yuan " +0x8789: "Weng " +0x878a: "Lian " +0x878b: "Sou " +0x878c: "Ban " +0x878d: "Rong " +0x878e: "Rong " +0x878f: "Ji " +0x8790: "Wu " +0x8791: "Qiu " +0x8792: "Han " +0x8793: "Qin " +0x8794: "Yi " +0x8795: "Bi " +0x8796: "Hua " +0x8797: "Tang " +0x8798: "Yi " +0x8799: "Du " +0x879a: "Nai " +0x879b: "He " +0x879c: "Hu " +0x879d: "Hui " +0x879e: "Ma " +0x879f: "Ming " +0x87a0: "Yi " +0x87a1: "Wen " +0x87a2: "Ying " +0x87a3: "Teng " +0x87a4: "Yu " +0x87a5: "Cang " +0x87a6: "So " +0x87a7: "Ebi " +0x87a8: "Man " +0x87a9: "[?] " +0x87aa: "Shang " +0x87ab: "Zhe " +0x87ac: "Cao " +0x87ad: "Chi " +0x87ae: "Di " +0x87af: "Ao " +0x87b0: "Lu " +0x87b1: "Wei " +0x87b2: "Zhi " +0x87b3: "Tang " +0x87b4: "Chen " +0x87b5: "Piao " +0x87b6: "Qu " +0x87b7: "Pi " +0x87b8: "Yu " +0x87b9: "Jian " +0x87ba: "Luo " +0x87bb: "Lou " +0x87bc: "Qin " +0x87bd: "Zhong " +0x87be: "Yin " +0x87bf: "Jiang " +0x87c0: "Shuai " +0x87c1: "Wen " +0x87c2: "Jiao " +0x87c3: "Wan " +0x87c4: "Zhi " +0x87c5: "Zhe " +0x87c6: "Ma " +0x87c7: "Ma " +0x87c8: "Guo " +0x87c9: "Liu " +0x87ca: "Mao " +0x87cb: "Xi " +0x87cc: "Cong " +0x87cd: "Li " +0x87ce: "Man " +0x87cf: "Xiao " +0x87d0: "Kamakiri " +0x87d1: "Zhang " +0x87d2: "Mang " +0x87d3: "Xiang " +0x87d4: "Mo " +0x87d5: "Zui " +0x87d6: "Si " +0x87d7: "Qiu " +0x87d8: "Te " +0x87d9: "Zhi " +0x87da: "Peng " +0x87db: "Peng " +0x87dc: "Jiao " +0x87dd: "Qu " +0x87de: "Bie " +0x87df: "Liao " +0x87e0: "Pan " +0x87e1: "Gui " +0x87e2: "Xi " +0x87e3: "Ji " +0x87e4: "Zhuan " +0x87e5: "Huang " +0x87e6: "Fei " +0x87e7: "Lao " +0x87e8: "Jue " +0x87e9: "Jue " +0x87ea: "Hui " +0x87eb: "Yin " +0x87ec: "Chan " +0x87ed: "Jiao " +0x87ee: "Shan " +0x87ef: "Rao " +0x87f0: "Xiao " +0x87f1: "Mou " +0x87f2: "Chong " +0x87f3: "Xun " +0x87f4: "Si " +0x87f5: "[?] " +0x87f6: "Cheng " +0x87f7: "Dang " +0x87f8: "Li " +0x87f9: "Xie " +0x87fa: "Shan " +0x87fb: "Yi " +0x87fc: "Jing " +0x87fd: "Da " +0x87fe: "Chan " +0x87ff: "Qi " +/* x088 */ +0x8800: "Ci " +0x8801: "Xiang " +0x8802: "She " +0x8803: "Luo " +0x8804: "Qin " +0x8805: "Ying " +0x8806: "Chai " +0x8807: "Li " +0x8808: "Ze " +0x8809: "Xuan " +0x880a: "Lian " +0x880b: "Zhu " +0x880c: "Ze " +0x880d: "Xie " +0x880e: "Mang " +0x880f: "Xie " +0x8810: "Qi " +0x8811: "Rong " +0x8812: "Jian " +0x8813: "Meng " +0x8814: "Hao " +0x8815: "Ruan " +0x8816: "Huo " +0x8817: "Zhuo " +0x8818: "Jie " +0x8819: "Bin " +0x881a: "He " +0x881b: "Mie " +0x881c: "Fan " +0x881d: "Lei " +0x881e: "Jie " +0x881f: "La " +0x8820: "Mi " +0x8821: "Li " +0x8822: "Chun " +0x8823: "Li " +0x8824: "Qiu " +0x8825: "Nie " +0x8826: "Lu " +0x8827: "Du " +0x8828: "Xiao " +0x8829: "Zhu " +0x882a: "Long " +0x882b: "Li " +0x882c: "Long " +0x882d: "Feng " +0x882e: "Ye " +0x882f: "Beng " +0x8830: "Shang " +0x8831: "Gu " +0x8832: "Juan " +0x8833: "Ying " +0x8834: "[?] " +0x8835: "Xi " +0x8836: "Can " +0x8837: "Qu " +0x8838: "Quan " +0x8839: "Du " +0x883a: "Can " +0x883b: "Man " +0x883c: "Jue " +0x883d: "Jie " +0x883e: "Zhu " +0x883f: "Zha " +0x8840: "Xie " +0x8841: "Huang " +0x8842: "Niu " +0x8843: "Pei " +0x8844: "Nu " +0x8845: "Xin " +0x8846: "Zhong " +0x8847: "Mo " +0x8848: "Er " +0x8849: "Ke " +0x884a: "Mie " +0x884b: "Xi " +0x884c: "Xing " +0x884d: "Yan " +0x884e: "Kan " +0x884f: "Yuan " +0x8850: "[?] " +0x8851: "Ling " +0x8852: "Xuan " +0x8853: "Shu " +0x8854: "Xian " +0x8855: "Tong " +0x8856: "Long " +0x8857: "Jie " +0x8858: "Xian " +0x8859: "Ya " +0x885a: "Hu " +0x885b: "Wei " +0x885c: "Dao " +0x885d: "Chong " +0x885e: "Wei " +0x885f: "Dao " +0x8860: "Zhun " +0x8861: "Heng " +0x8862: "Qu " +0x8863: "Yi " +0x8864: "Yi " +0x8865: "Bu " +0x8866: "Gan " +0x8867: "Yu " +0x8868: "Biao " +0x8869: "Cha " +0x886a: "Yi " +0x886b: "Shan " +0x886c: "Chen " +0x886d: "Fu " +0x886e: "Gun " +0x886f: "Fen " +0x8870: "Shuai " +0x8871: "Jie " +0x8872: "Na " +0x8873: "Zhong " +0x8874: "Dan " +0x8875: "Ri " +0x8876: "Zhong " +0x8877: "Zhong " +0x8878: "Xie " +0x8879: "Qi " +0x887a: "Xie " +0x887b: "Ran " +0x887c: "Zhi " +0x887d: "Ren " +0x887e: "Qin " +0x887f: "Jin " +0x8880: "Jun " +0x8881: "Yuan " +0x8882: "Mei " +0x8883: "Chai " +0x8884: "Ao " +0x8885: "Niao " +0x8886: "Hui " +0x8887: "Ran " +0x8888: "Jia " +0x8889: "Tuo " +0x888a: "Ling " +0x888b: "Dai " +0x888c: "Bao " +0x888d: "Pao " +0x888e: "Yao " +0x888f: "Zuo " +0x8890: "Bi " +0x8891: "Shao " +0x8892: "Tan " +0x8893: "Ju " +0x8894: "He " +0x8895: "Shu " +0x8896: "Xiu " +0x8897: "Zhen " +0x8898: "Yi " +0x8899: "Pa " +0x889a: "Bo " +0x889b: "Di " +0x889c: "Wa " +0x889d: "Fu " +0x889e: "Gun " +0x889f: "Zhi " +0x88a0: "Zhi " +0x88a1: "Ran " +0x88a2: "Pan " +0x88a3: "Yi " +0x88a4: "Mao " +0x88a5: "Tuo " +0x88a6: "Na " +0x88a7: "Kou " +0x88a8: "Xian " +0x88a9: "Chan " +0x88aa: "Qu " +0x88ab: "Bei " +0x88ac: "Gun " +0x88ad: "Xi " +0x88ae: "Ne " +0x88af: "Bo " +0x88b0: "Horo " +0x88b1: "Fu " +0x88b2: "Yi " +0x88b3: "Chi " +0x88b4: "Ku " +0x88b5: "Ren " +0x88b6: "Jiang " +0x88b7: "Jia " +0x88b8: "Cun " +0x88b9: "Mo " +0x88ba: "Jie " +0x88bb: "Er " +0x88bc: "Luo " +0x88bd: "Ru " +0x88be: "Zhu " +0x88bf: "Gui " +0x88c0: "Yin " +0x88c1: "Cai " +0x88c2: "Lie " +0x88c3: "Kamishimo " +0x88c4: "Yuki " +0x88c5: "Zhuang " +0x88c6: "Dang " +0x88c7: "[?] " +0x88c8: "Kun " +0x88c9: "Ken " +0x88ca: "Niao " +0x88cb: "Shu " +0x88cc: "Jia " +0x88cd: "Kun " +0x88ce: "Cheng " +0x88cf: "Li " +0x88d0: "Juan " +0x88d1: "Shen " +0x88d2: "Pou " +0x88d3: "Ge " +0x88d4: "Yi " +0x88d5: "Yu " +0x88d6: "Zhen " +0x88d7: "Liu " +0x88d8: "Qiu " +0x88d9: "Qun " +0x88da: "Ji " +0x88db: "Yi " +0x88dc: "Bu " +0x88dd: "Zhuang " +0x88de: "Shui " +0x88df: "Sha " +0x88e0: "Qun " +0x88e1: "Li " +0x88e2: "Lian " +0x88e3: "Lian " +0x88e4: "Ku " +0x88e5: "Jian " +0x88e6: "Fou " +0x88e7: "Chan " +0x88e8: "Bi " +0x88e9: "Gun " +0x88ea: "Tao " +0x88eb: "Yuan " +0x88ec: "Ling " +0x88ed: "Chi " +0x88ee: "Chang " +0x88ef: "Chou " +0x88f0: "Duo " +0x88f1: "Biao " +0x88f2: "Liang " +0x88f3: "Chang " +0x88f4: "Pei " +0x88f5: "Pei " +0x88f6: "Fei " +0x88f7: "Yuan " +0x88f8: "Luo " +0x88f9: "Guo " +0x88fa: "Yan " +0x88fb: "Du " +0x88fc: "Xi " +0x88fd: "Zhi " +0x88fe: "Ju " +0x88ff: "Qi " +/* x089 */ +0x8900: "Ji " +0x8901: "Zhi " +0x8902: "Gua " +0x8903: "Ken " +0x8904: "Che " +0x8905: "Ti " +0x8906: "Ti " +0x8907: "Fu " +0x8908: "Chong " +0x8909: "Xie " +0x890a: "Bian " +0x890b: "Die " +0x890c: "Kun " +0x890d: "Duan " +0x890e: "Xiu " +0x890f: "Xiu " +0x8910: "He " +0x8911: "Yuan " +0x8912: "Bao " +0x8913: "Bao " +0x8914: "Fu " +0x8915: "Yu " +0x8916: "Tuan " +0x8917: "Yan " +0x8918: "Hui " +0x8919: "Bei " +0x891a: "Chu " +0x891b: "Lu " +0x891c: "Ena " +0x891d: "Hitoe " +0x891e: "Yun " +0x891f: "Da " +0x8920: "Gou " +0x8921: "Da " +0x8922: "Huai " +0x8923: "Rong " +0x8924: "Yuan " +0x8925: "Ru " +0x8926: "Nai " +0x8927: "Jiong " +0x8928: "Suo " +0x8929: "Ban " +0x892a: "Tun " +0x892b: "Chi " +0x892c: "Sang " +0x892d: "Niao " +0x892e: "Ying " +0x892f: "Jie " +0x8930: "Qian " +0x8931: "Huai " +0x8932: "Ku " +0x8933: "Lian " +0x8934: "Bao " +0x8935: "Li " +0x8936: "Zhe " +0x8937: "Shi " +0x8938: "Lu " +0x8939: "Yi " +0x893a: "Die " +0x893b: "Xie " +0x893c: "Xian " +0x893d: "Wei " +0x893e: "Biao " +0x893f: "Cao " +0x8940: "Ji " +0x8941: "Jiang " +0x8942: "Sen " +0x8943: "Bao " +0x8944: "Xiang " +0x8945: "Chihaya " +0x8946: "Pu " +0x8947: "Jian " +0x8948: "Zhuan " +0x8949: "Jian " +0x894a: "Zui " +0x894b: "Ji " +0x894c: "Dan " +0x894d: "Za " +0x894e: "Fan " +0x894f: "Bo " +0x8950: "Xiang " +0x8951: "Xin " +0x8952: "Bie " +0x8953: "Rao " +0x8954: "Man " +0x8955: "Lan " +0x8956: "Ao " +0x8957: "Duo " +0x8958: "Gui " +0x8959: "Cao " +0x895a: "Sui " +0x895b: "Nong " +0x895c: "Chan " +0x895d: "Lian " +0x895e: "Bi " +0x895f: "Jin " +0x8960: "Dang " +0x8961: "Shu " +0x8962: "Tan " +0x8963: "Bi " +0x8964: "Lan " +0x8965: "Pu " +0x8966: "Ru " +0x8967: "Zhi " +0x8968: "[?] " +0x8969: "Shu " +0x896a: "Wa " +0x896b: "Shi " +0x896c: "Bai " +0x896d: "Xie " +0x896e: "Bo " +0x896f: "Chen " +0x8970: "Lai " +0x8971: "Long " +0x8972: "Xi " +0x8973: "Xian " +0x8974: "Lan " +0x8975: "Zhe " +0x8976: "Dai " +0x8977: "Tasuki " +0x8978: "Zan " +0x8979: "Shi " +0x897a: "Jian " +0x897b: "Pan " +0x897c: "Yi " +0x897d: "Ran " +0x897e: "Ya " +0x897f: "Xi " +0x8980: "Xi " +0x8981: "Yao " +0x8982: "Feng " +0x8983: "Tan " +0x8984: "[?] " +0x8985: "Biao " +0x8986: "Fu " +0x8987: "Ba " +0x8988: "He " +0x8989: "Ji " +0x898a: "Ji " +0x898b: "Jian " +0x898c: "Guan " +0x898d: "Bian " +0x898e: "Yan " +0x898f: "Gui " +0x8990: "Jue " +0x8991: "Pian " +0x8992: "Mao " +0x8993: "Mi " +0x8994: "Mi " +0x8995: "Mie " +0x8996: "Shi " +0x8997: "Si " +0x8998: "Zhan " +0x8999: "Luo " +0x899a: "Jue " +0x899b: "Mi " +0x899c: "Tiao " +0x899d: "Lian " +0x899e: "Yao " +0x899f: "Zhi " +0x89a0: "Jun " +0x89a1: "Xi " +0x89a2: "Shan " +0x89a3: "Wei " +0x89a4: "Xi " +0x89a5: "Tian " +0x89a6: "Yu " +0x89a7: "Lan " +0x89a8: "E " +0x89a9: "Du " +0x89aa: "Qin " +0x89ab: "Pang " +0x89ac: "Ji " +0x89ad: "Ming " +0x89ae: "Ying " +0x89af: "Gou " +0x89b0: "Qu " +0x89b1: "Zhan " +0x89b2: "Jin " +0x89b3: "Guan " +0x89b4: "Deng " +0x89b5: "Jian " +0x89b6: "Luo " +0x89b7: "Qu " +0x89b8: "Jian " +0x89b9: "Wei " +0x89ba: "Jue " +0x89bb: "Qu " +0x89bc: "Luo " +0x89bd: "Lan " +0x89be: "Shen " +0x89bf: "Di " +0x89c0: "Guan " +0x89c1: "Jian " +0x89c2: "Guan " +0x89c3: "Yan " +0x89c4: "Gui " +0x89c5: "Mi " +0x89c6: "Shi " +0x89c7: "Zhan " +0x89c8: "Lan " +0x89c9: "Jue " +0x89ca: "Ji " +0x89cb: "Xi " +0x89cc: "Di " +0x89cd: "Tian " +0x89ce: "Yu " +0x89cf: "Gou " +0x89d0: "Jin " +0x89d1: "Qu " +0x89d2: "Jiao " +0x89d3: "Jiu " +0x89d4: "Jin " +0x89d5: "Cu " +0x89d6: "Jue " +0x89d7: "Zhi " +0x89d8: "Chao " +0x89d9: "Ji " +0x89da: "Gu " +0x89db: "Dan " +0x89dc: "Zui " +0x89dd: "Di " +0x89de: "Shang " +0x89df: "Hua " +0x89e0: "Quan " +0x89e1: "Ge " +0x89e2: "Chi " +0x89e3: "Jie " +0x89e4: "Gui " +0x89e5: "Gong " +0x89e6: "Hong " +0x89e7: "Jie " +0x89e8: "Hun " +0x89e9: "Qiu " +0x89ea: "Xing " +0x89eb: "Su " +0x89ec: "Ni " +0x89ed: "Ji " +0x89ee: "Lu " +0x89ef: "Zhi " +0x89f0: "Zha " +0x89f1: "Bi " +0x89f2: "Xing " +0x89f3: "Hu " +0x89f4: "Shang " +0x89f5: "Gong " +0x89f6: "Zhi " +0x89f7: "Xue " +0x89f8: "Chu " +0x89f9: "Xi " +0x89fa: "Yi " +0x89fb: "Lu " +0x89fc: "Jue " +0x89fd: "Xi " +0x89fe: "Yan " +0x89ff: "Xi " +/* x08a */ +0x8a00: "Yan " +0x8a01: "Yan " +0x8a02: "Ding " +0x8a03: "Fu " +0x8a04: "Qiu " +0x8a05: "Qiu " +0x8a06: "Jiao " +0x8a07: "Hong " +0x8a08: "Ji " +0x8a09: "Fan " +0x8a0a: "Xun " +0x8a0b: "Diao " +0x8a0c: "Hong " +0x8a0d: "Cha " +0x8a0e: "Tao " +0x8a0f: "Xu " +0x8a10: "Jie " +0x8a11: "Yi " +0x8a12: "Ren " +0x8a13: "Xun " +0x8a14: "Yin " +0x8a15: "Shan " +0x8a16: "Qi " +0x8a17: "Tuo " +0x8a18: "Ji " +0x8a19: "Xun " +0x8a1a: "Yin " +0x8a1b: "E " +0x8a1c: "Fen " +0x8a1d: "Ya " +0x8a1e: "Yao " +0x8a1f: "Song " +0x8a20: "Shen " +0x8a21: "Yin " +0x8a22: "Xin " +0x8a23: "Jue " +0x8a24: "Xiao " +0x8a25: "Ne " +0x8a26: "Chen " +0x8a27: "You " +0x8a28: "Zhi " +0x8a29: "Xiong " +0x8a2a: "Fang " +0x8a2b: "Xin " +0x8a2c: "Chao " +0x8a2d: "She " +0x8a2e: "Xian " +0x8a2f: "Sha " +0x8a30: "Tun " +0x8a31: "Xu " +0x8a32: "Yi " +0x8a33: "Yi " +0x8a34: "Su " +0x8a35: "Chi " +0x8a36: "He " +0x8a37: "Shen " +0x8a38: "He " +0x8a39: "Xu " +0x8a3a: "Zhen " +0x8a3b: "Zhu " +0x8a3c: "Zheng " +0x8a3d: "Gou " +0x8a3e: "Zi " +0x8a3f: "Zi " +0x8a40: "Zhan " +0x8a41: "Gu " +0x8a42: "Fu " +0x8a43: "Quan " +0x8a44: "Die " +0x8a45: "Ling " +0x8a46: "Di " +0x8a47: "Yang " +0x8a48: "Li " +0x8a49: "Nao " +0x8a4a: "Pan " +0x8a4b: "Zhou " +0x8a4c: "Gan " +0x8a4d: "Yi " +0x8a4e: "Ju " +0x8a4f: "Ao " +0x8a50: "Zha " +0x8a51: "Tuo " +0x8a52: "Yi " +0x8a53: "Qu " +0x8a54: "Zhao " +0x8a55: "Ping " +0x8a56: "Bi " +0x8a57: "Xiong " +0x8a58: "Qu " +0x8a59: "Ba " +0x8a5a: "Da " +0x8a5b: "Zu " +0x8a5c: "Tao " +0x8a5d: "Zhu " +0x8a5e: "Ci " +0x8a5f: "Zhe " +0x8a60: "Yong " +0x8a61: "Xu " +0x8a62: "Xun " +0x8a63: "Yi " +0x8a64: "Huang " +0x8a65: "He " +0x8a66: "Shi " +0x8a67: "Cha " +0x8a68: "Jiao " +0x8a69: "Shi " +0x8a6a: "Hen " +0x8a6b: "Cha " +0x8a6c: "Gou " +0x8a6d: "Gui " +0x8a6e: "Quan " +0x8a6f: "Hui " +0x8a70: "Jie " +0x8a71: "Hua " +0x8a72: "Gai " +0x8a73: "Xiang " +0x8a74: "Wei " +0x8a75: "Shen " +0x8a76: "Chou " +0x8a77: "Tong " +0x8a78: "Mi " +0x8a79: "Zhan " +0x8a7a: "Ming " +0x8a7b: "E " +0x8a7c: "Hui " +0x8a7d: "Yan " +0x8a7e: "Xiong " +0x8a7f: "Gua " +0x8a80: "Er " +0x8a81: "Beng " +0x8a82: "Tiao " +0x8a83: "Chi " +0x8a84: "Lei " +0x8a85: "Zhu " +0x8a86: "Kuang " +0x8a87: "Kua " +0x8a88: "Wu " +0x8a89: "Yu " +0x8a8a: "Teng " +0x8a8b: "Ji " +0x8a8c: "Zhi " +0x8a8d: "Ren " +0x8a8e: "Su " +0x8a8f: "Lang " +0x8a90: "E " +0x8a91: "Kuang " +0x8a92: "E " +0x8a93: "Shi " +0x8a94: "Ting " +0x8a95: "Dan " +0x8a96: "Bo " +0x8a97: "Chan " +0x8a98: "You " +0x8a99: "Heng " +0x8a9a: "Qiao " +0x8a9b: "Qin " +0x8a9c: "Shua " +0x8a9d: "An " +0x8a9e: "Yu " +0x8a9f: "Xiao " +0x8aa0: "Cheng " +0x8aa1: "Jie " +0x8aa2: "Xian " +0x8aa3: "Wu " +0x8aa4: "Wu " +0x8aa5: "Gao " +0x8aa6: "Song " +0x8aa7: "Pu " +0x8aa8: "Hui " +0x8aa9: "Jing " +0x8aaa: "Shuo " +0x8aab: "Zhen " +0x8aac: "Shuo " +0x8aad: "Du " +0x8aae: "Yasashi " +0x8aaf: "Chang " +0x8ab0: "Shui " +0x8ab1: "Jie " +0x8ab2: "Ke " +0x8ab3: "Qu " +0x8ab4: "Cong " +0x8ab5: "Xiao " +0x8ab6: "Sui " +0x8ab7: "Wang " +0x8ab8: "Xuan " +0x8ab9: "Fei " +0x8aba: "Chi " +0x8abb: "Ta " +0x8abc: "Yi " +0x8abd: "Na " +0x8abe: "Yin " +0x8abf: "Diao " +0x8ac0: "Pi " +0x8ac1: "Chuo " +0x8ac2: "Chan " +0x8ac3: "Chen " +0x8ac4: "Zhun " +0x8ac5: "Ji " +0x8ac6: "Qi " +0x8ac7: "Tan " +0x8ac8: "Zhui " +0x8ac9: "Wei " +0x8aca: "Ju " +0x8acb: "Qing " +0x8acc: "Jian " +0x8acd: "Zheng " +0x8ace: "Ze " +0x8acf: "Zou " +0x8ad0: "Qian " +0x8ad1: "Zhuo " +0x8ad2: "Liang " +0x8ad3: "Jian " +0x8ad4: "Zhu " +0x8ad5: "Hao " +0x8ad6: "Lun " +0x8ad7: "Shen " +0x8ad8: "Biao " +0x8ad9: "Huai " +0x8ada: "Pian " +0x8adb: "Yu " +0x8adc: "Die " +0x8add: "Xu " +0x8ade: "Pian " +0x8adf: "Shi " +0x8ae0: "Xuan " +0x8ae1: "Shi " +0x8ae2: "Hun " +0x8ae3: "Hua " +0x8ae4: "E " +0x8ae5: "Zhong " +0x8ae6: "Di " +0x8ae7: "Xie " +0x8ae8: "Fu " +0x8ae9: "Pu " +0x8aea: "Ting " +0x8aeb: "Jian " +0x8aec: "Qi " +0x8aed: "Yu " +0x8aee: "Zi " +0x8aef: "Chuan " +0x8af0: "Xi " +0x8af1: "Hui " +0x8af2: "Yin " +0x8af3: "An " +0x8af4: "Xian " +0x8af5: "Nan " +0x8af6: "Chen " +0x8af7: "Feng " +0x8af8: "Zhu " +0x8af9: "Yang " +0x8afa: "Yan " +0x8afb: "Heng " +0x8afc: "Xuan " +0x8afd: "Ge " +0x8afe: "Nuo " +0x8aff: "Qi " +/* x08b */ +0x8b00: "Mou " +0x8b01: "Ye " +0x8b02: "Wei " +0x8b03: "[?] " +0x8b04: "Teng " +0x8b05: "Zou " +0x8b06: "Shan " +0x8b07: "Jian " +0x8b08: "Bo " +0x8b09: "Ku " +0x8b0a: "Huang " +0x8b0b: "Huo " +0x8b0c: "Ge " +0x8b0d: "Ying " +0x8b0e: "Mi " +0x8b0f: "Xiao " +0x8b10: "Mi " +0x8b11: "Xi " +0x8b12: "Qiang " +0x8b13: "Chen " +0x8b14: "Nue " +0x8b15: "Ti " +0x8b16: "Su " +0x8b17: "Bang " +0x8b18: "Chi " +0x8b19: "Qian " +0x8b1a: "Shi " +0x8b1b: "Jiang " +0x8b1c: "Yuan " +0x8b1d: "Xie " +0x8b1e: "Xue " +0x8b1f: "Tao " +0x8b20: "Yao " +0x8b21: "Yao " +0x8b22: "[?] " +0x8b23: "Yu " +0x8b24: "Biao " +0x8b25: "Cong " +0x8b26: "Qing " +0x8b27: "Li " +0x8b28: "Mo " +0x8b29: "Mo " +0x8b2a: "Shang " +0x8b2b: "Zhe " +0x8b2c: "Miu " +0x8b2d: "Jian " +0x8b2e: "Ze " +0x8b2f: "Jie " +0x8b30: "Lian " +0x8b31: "Lou " +0x8b32: "Can " +0x8b33: "Ou " +0x8b34: "Guan " +0x8b35: "Xi " +0x8b36: "Zhuo " +0x8b37: "Ao " +0x8b38: "Ao " +0x8b39: "Jin " +0x8b3a: "Zhe " +0x8b3b: "Yi " +0x8b3c: "Hu " +0x8b3d: "Jiang " +0x8b3e: "Man " +0x8b3f: "Chao " +0x8b40: "Han " +0x8b41: "Hua " +0x8b42: "Chan " +0x8b43: "Xu " +0x8b44: "Zeng " +0x8b45: "Se " +0x8b46: "Xi " +0x8b47: "She " +0x8b48: "Dui " +0x8b49: "Zheng " +0x8b4a: "Nao " +0x8b4b: "Lan " +0x8b4c: "E " +0x8b4d: "Ying " +0x8b4e: "Jue " +0x8b4f: "Ji " +0x8b50: "Zun " +0x8b51: "Jiao " +0x8b52: "Bo " +0x8b53: "Hui " +0x8b54: "Zhuan " +0x8b55: "Mu " +0x8b56: "Zen " +0x8b57: "Zha " +0x8b58: "Shi " +0x8b59: "Qiao " +0x8b5a: "Tan " +0x8b5b: "Zen " +0x8b5c: "Pu " +0x8b5d: "Sheng " +0x8b5e: "Xuan " +0x8b5f: "Zao " +0x8b60: "Tan " +0x8b61: "Dang " +0x8b62: "Sui " +0x8b63: "Qian " +0x8b64: "Ji " +0x8b65: "Jiao " +0x8b66: "Jing " +0x8b67: "Lian " +0x8b68: "Nou " +0x8b69: "Yi " +0x8b6a: "Ai " +0x8b6b: "Zhan " +0x8b6c: "Pi " +0x8b6d: "Hui " +0x8b6e: "Hua " +0x8b6f: "Yi " +0x8b70: "Yi " +0x8b71: "Shan " +0x8b72: "Rang " +0x8b73: "Nou " +0x8b74: "Qian " +0x8b75: "Zhui " +0x8b76: "Ta " +0x8b77: "Hu " +0x8b78: "Zhou " +0x8b79: "Hao " +0x8b7a: "Ye " +0x8b7b: "Ying " +0x8b7c: "Jian " +0x8b7d: "Yu " +0x8b7e: "Jian " +0x8b7f: "Hui " +0x8b80: "Du " +0x8b81: "Zhe " +0x8b82: "Xuan " +0x8b83: "Zan " +0x8b84: "Lei " +0x8b85: "Shen " +0x8b86: "Wei " +0x8b87: "Chan " +0x8b88: "Li " +0x8b89: "Yi " +0x8b8a: "Bian " +0x8b8b: "Zhe " +0x8b8c: "Yan " +0x8b8d: "E " +0x8b8e: "Chou " +0x8b8f: "Wei " +0x8b90: "Chou " +0x8b91: "Yao " +0x8b92: "Chan " +0x8b93: "Rang " +0x8b94: "Yin " +0x8b95: "Lan " +0x8b96: "Chen " +0x8b97: "Huo " +0x8b98: "Zhe " +0x8b99: "Huan " +0x8b9a: "Zan " +0x8b9b: "Yi " +0x8b9c: "Dang " +0x8b9d: "Zhan " +0x8b9e: "Yan " +0x8b9f: "Du " +0x8ba0: "Yan " +0x8ba1: "Ji " +0x8ba2: "Ding " +0x8ba3: "Fu " +0x8ba4: "Ren " +0x8ba5: "Ji " +0x8ba6: "Jie " +0x8ba7: "Hong " +0x8ba8: "Tao " +0x8ba9: "Rang " +0x8baa: "Shan " +0x8bab: "Qi " +0x8bac: "Tuo " +0x8bad: "Xun " +0x8bae: "Yi " +0x8baf: "Xun " +0x8bb0: "Ji " +0x8bb1: "Ren " +0x8bb2: "Jiang " +0x8bb3: "Hui " +0x8bb4: "Ou " +0x8bb5: "Ju " +0x8bb6: "Ya " +0x8bb7: "Ne " +0x8bb8: "Xu " +0x8bb9: "E " +0x8bba: "Lun " +0x8bbb: "Xiong " +0x8bbc: "Song " +0x8bbd: "Feng " +0x8bbe: "She " +0x8bbf: "Fang " +0x8bc0: "Jue " +0x8bc1: "Zheng " +0x8bc2: "Gu " +0x8bc3: "He " +0x8bc4: "Ping " +0x8bc5: "Zu " +0x8bc6: "Shi " +0x8bc7: "Xiong " +0x8bc8: "Zha " +0x8bc9: "Su " +0x8bca: "Zhen " +0x8bcb: "Di " +0x8bcc: "Zou " +0x8bcd: "Ci " +0x8bce: "Qu " +0x8bcf: "Zhao " +0x8bd0: "Bi " +0x8bd1: "Yi " +0x8bd2: "Yi " +0x8bd3: "Kuang " +0x8bd4: "Lei " +0x8bd5: "Shi " +0x8bd6: "Gua " +0x8bd7: "Shi " +0x8bd8: "Jie " +0x8bd9: "Hui " +0x8bda: "Cheng " +0x8bdb: "Zhu " +0x8bdc: "Shen " +0x8bdd: "Hua " +0x8bde: "Dan " +0x8bdf: "Gou " +0x8be0: "Quan " +0x8be1: "Gui " +0x8be2: "Xun " +0x8be3: "Yi " +0x8be4: "Zheng " +0x8be5: "Gai " +0x8be6: "Xiang " +0x8be7: "Cha " +0x8be8: "Hun " +0x8be9: "Xu " +0x8bea: "Zhou " +0x8beb: "Jie " +0x8bec: "Wu " +0x8bed: "Yu " +0x8bee: "Qiao " +0x8bef: "Wu " +0x8bf0: "Gao " +0x8bf1: "You " +0x8bf2: "Hui " +0x8bf3: "Kuang " +0x8bf4: "Shuo " +0x8bf5: "Song " +0x8bf6: "Ai " +0x8bf7: "Qing " +0x8bf8: "Zhu " +0x8bf9: "Zou " +0x8bfa: "Nuo " +0x8bfb: "Du " +0x8bfc: "Zhuo " +0x8bfd: "Fei " +0x8bfe: "Ke " +0x8bff: "Wei " +/* x08c */ +0x8c00: "Yu " +0x8c01: "Shui " +0x8c02: "Shen " +0x8c03: "Diao " +0x8c04: "Chan " +0x8c05: "Liang " +0x8c06: "Zhun " +0x8c07: "Sui " +0x8c08: "Tan " +0x8c09: "Shen " +0x8c0a: "Yi " +0x8c0b: "Mou " +0x8c0c: "Chen " +0x8c0d: "Die " +0x8c0e: "Huang " +0x8c0f: "Jian " +0x8c10: "Xie " +0x8c11: "Nue " +0x8c12: "Ye " +0x8c13: "Wei " +0x8c14: "E " +0x8c15: "Yu " +0x8c16: "Xuan " +0x8c17: "Chan " +0x8c18: "Zi " +0x8c19: "An " +0x8c1a: "Yan " +0x8c1b: "Di " +0x8c1c: "Mi " +0x8c1d: "Pian " +0x8c1e: "Xu " +0x8c1f: "Mo " +0x8c20: "Dang " +0x8c21: "Su " +0x8c22: "Xie " +0x8c23: "Yao " +0x8c24: "Bang " +0x8c25: "Shi " +0x8c26: "Qian " +0x8c27: "Mi " +0x8c28: "Jin " +0x8c29: "Man " +0x8c2a: "Zhe " +0x8c2b: "Jian " +0x8c2c: "Miu " +0x8c2d: "Tan " +0x8c2e: "Zen " +0x8c2f: "Qiao " +0x8c30: "Lan " +0x8c31: "Pu " +0x8c32: "Jue " +0x8c33: "Yan " +0x8c34: "Qian " +0x8c35: "Zhan " +0x8c36: "Chen " +0x8c37: "Gu " +0x8c38: "Qian " +0x8c39: "Hong " +0x8c3a: "Xia " +0x8c3b: "Jue " +0x8c3c: "Hong " +0x8c3d: "Han " +0x8c3e: "Hong " +0x8c3f: "Xi " +0x8c40: "Xi " +0x8c41: "Huo " +0x8c42: "Liao " +0x8c43: "Han " +0x8c44: "Du " +0x8c45: "Long " +0x8c46: "Dou " +0x8c47: "Jiang " +0x8c48: "Qi " +0x8c49: "Shi " +0x8c4a: "Li " +0x8c4b: "Deng " +0x8c4c: "Wan " +0x8c4d: "Bi " +0x8c4e: "Shu " +0x8c4f: "Xian " +0x8c50: "Feng " +0x8c51: "Zhi " +0x8c52: "Zhi " +0x8c53: "Yan " +0x8c54: "Yan " +0x8c55: "Shi " +0x8c56: "Chu " +0x8c57: "Hui " +0x8c58: "Tun " +0x8c59: "Yi " +0x8c5a: "Tun " +0x8c5b: "Yi " +0x8c5c: "Jian " +0x8c5d: "Ba " +0x8c5e: "Hou " +0x8c5f: "E " +0x8c60: "Cu " +0x8c61: "Xiang " +0x8c62: "Huan " +0x8c63: "Jian " +0x8c64: "Ken " +0x8c65: "Gai " +0x8c66: "Qu " +0x8c67: "Fu " +0x8c68: "Xi " +0x8c69: "Bin " +0x8c6a: "Hao " +0x8c6b: "Yu " +0x8c6c: "Zhu " +0x8c6d: "Jia " +0x8c6e: "[?] " +0x8c6f: "Xi " +0x8c70: "Bo " +0x8c71: "Wen " +0x8c72: "Huan " +0x8c73: "Bin " +0x8c74: "Di " +0x8c75: "Zong " +0x8c76: "Fen " +0x8c77: "Yi " +0x8c78: "Zhi " +0x8c79: "Bao " +0x8c7a: "Chai " +0x8c7b: "Han " +0x8c7c: "Pi " +0x8c7d: "Na " +0x8c7e: "Pi " +0x8c7f: "Gou " +0x8c80: "Na " +0x8c81: "You " +0x8c82: "Diao " +0x8c83: "Mo " +0x8c84: "Si " +0x8c85: "Xiu " +0x8c86: "Huan " +0x8c87: "Kun " +0x8c88: "He " +0x8c89: "He " +0x8c8a: "Mo " +0x8c8b: "Han " +0x8c8c: "Mao " +0x8c8d: "Li " +0x8c8e: "Ni " +0x8c8f: "Bi " +0x8c90: "Yu " +0x8c91: "Jia " +0x8c92: "Tuan " +0x8c93: "Mao " +0x8c94: "Pi " +0x8c95: "Xi " +0x8c96: "E " +0x8c97: "Ju " +0x8c98: "Mo " +0x8c99: "Chu " +0x8c9a: "Tan " +0x8c9b: "Huan " +0x8c9c: "Jue " +0x8c9d: "Bei " +0x8c9e: "Zhen " +0x8c9f: "Yuan " +0x8ca0: "Fu " +0x8ca1: "Cai " +0x8ca2: "Gong " +0x8ca3: "Te " +0x8ca4: "Yi " +0x8ca5: "Hang " +0x8ca6: "Wan " +0x8ca7: "Pin " +0x8ca8: "Huo " +0x8ca9: "Fan " +0x8caa: "Tan " +0x8cab: "Guan " +0x8cac: "Ze " +0x8cad: "Zhi " +0x8cae: "Er " +0x8caf: "Zhu " +0x8cb0: "Shi " +0x8cb1: "Bi " +0x8cb2: "Zi " +0x8cb3: "Er " +0x8cb4: "Gui " +0x8cb5: "Pian " +0x8cb6: "Bian " +0x8cb7: "Mai " +0x8cb8: "Dai " +0x8cb9: "Sheng " +0x8cba: "Kuang " +0x8cbb: "Fei " +0x8cbc: "Tie " +0x8cbd: "Yi " +0x8cbe: "Chi " +0x8cbf: "Mao " +0x8cc0: "He " +0x8cc1: "Bi " +0x8cc2: "Lu " +0x8cc3: "Ren " +0x8cc4: "Hui " +0x8cc5: "Gai " +0x8cc6: "Pian " +0x8cc7: "Zi " +0x8cc8: "Jia " +0x8cc9: "Xu " +0x8cca: "Zei " +0x8ccb: "Jiao " +0x8ccc: "Gai " +0x8ccd: "Zang " +0x8cce: "Jian " +0x8ccf: "Ying " +0x8cd0: "Xun " +0x8cd1: "Zhen " +0x8cd2: "She " +0x8cd3: "Bin " +0x8cd4: "Bin " +0x8cd5: "Qiu " +0x8cd6: "She " +0x8cd7: "Chuan " +0x8cd8: "Zang " +0x8cd9: "Zhou " +0x8cda: "Lai " +0x8cdb: "Zan " +0x8cdc: "Si " +0x8cdd: "Chen " +0x8cde: "Shang " +0x8cdf: "Tian " +0x8ce0: "Pei " +0x8ce1: "Geng " +0x8ce2: "Xian " +0x8ce3: "Mai " +0x8ce4: "Jian " +0x8ce5: "Sui " +0x8ce6: "Fu " +0x8ce7: "Tan " +0x8ce8: "Cong " +0x8ce9: "Cong " +0x8cea: "Zhi " +0x8ceb: "Ji " +0x8cec: "Zhang " +0x8ced: "Du " +0x8cee: "Jin " +0x8cef: "Xiong " +0x8cf0: "Shun " +0x8cf1: "Yun " +0x8cf2: "Bao " +0x8cf3: "Zai " +0x8cf4: "Lai " +0x8cf5: "Feng " +0x8cf6: "Cang " +0x8cf7: "Ji " +0x8cf8: "Sheng " +0x8cf9: "Ai " +0x8cfa: "Zhuan " +0x8cfb: "Fu " +0x8cfc: "Gou " +0x8cfd: "Sai " +0x8cfe: "Ze " +0x8cff: "Liao " +/* x08d */ +0x8d00: "Wei " +0x8d01: "Bai " +0x8d02: "Chen " +0x8d03: "Zhuan " +0x8d04: "Zhi " +0x8d05: "Zhui " +0x8d06: "Biao " +0x8d07: "Yun " +0x8d08: "Zeng " +0x8d09: "Tan " +0x8d0a: "Zan " +0x8d0b: "Yan " +0x8d0c: "[?] " +0x8d0d: "Shan " +0x8d0e: "Wan " +0x8d0f: "Ying " +0x8d10: "Jin " +0x8d11: "Gan " +0x8d12: "Xian " +0x8d13: "Zang " +0x8d14: "Bi " +0x8d15: "Du " +0x8d16: "Shu " +0x8d17: "Yan " +0x8d18: "[?] " +0x8d19: "Xuan " +0x8d1a: "Long " +0x8d1b: "Gan " +0x8d1c: "Zang " +0x8d1d: "Bei " +0x8d1e: "Zhen " +0x8d1f: "Fu " +0x8d20: "Yuan " +0x8d21: "Gong " +0x8d22: "Cai " +0x8d23: "Ze " +0x8d24: "Xian " +0x8d25: "Bai " +0x8d26: "Zhang " +0x8d27: "Huo " +0x8d28: "Zhi " +0x8d29: "Fan " +0x8d2a: "Tan " +0x8d2b: "Pin " +0x8d2c: "Bian " +0x8d2d: "Gou " +0x8d2e: "Zhu " +0x8d2f: "Guan " +0x8d30: "Er " +0x8d31: "Jian " +0x8d32: "Bi " +0x8d33: "Shi " +0x8d34: "Tie " +0x8d35: "Gui " +0x8d36: "Kuang " +0x8d37: "Dai " +0x8d38: "Mao " +0x8d39: "Fei " +0x8d3a: "He " +0x8d3b: "Yi " +0x8d3c: "Zei " +0x8d3d: "Zhi " +0x8d3e: "Jia " +0x8d3f: "Hui " +0x8d40: "Zi " +0x8d41: "Ren " +0x8d42: "Lu " +0x8d43: "Zang " +0x8d44: "Zi " +0x8d45: "Gai " +0x8d46: "Jin " +0x8d47: "Qiu " +0x8d48: "Zhen " +0x8d49: "Lai " +0x8d4a: "She " +0x8d4b: "Fu " +0x8d4c: "Du " +0x8d4d: "Ji " +0x8d4e: "Shu " +0x8d4f: "Shang " +0x8d50: "Si " +0x8d51: "Bi " +0x8d52: "Zhou " +0x8d53: "Geng " +0x8d54: "Pei " +0x8d55: "Tan " +0x8d56: "Lai " +0x8d57: "Feng " +0x8d58: "Zhui " +0x8d59: "Fu " +0x8d5a: "Zhuan " +0x8d5b: "Sai " +0x8d5c: "Ze " +0x8d5d: "Yan " +0x8d5e: "Zan " +0x8d5f: "Yun " +0x8d60: "Zeng " +0x8d61: "Shan " +0x8d62: "Ying " +0x8d63: "Gan " +0x8d64: "Chi " +0x8d65: "Xi " +0x8d66: "She " +0x8d67: "Nan " +0x8d68: "Xiong " +0x8d69: "Xi " +0x8d6a: "Cheng " +0x8d6b: "He " +0x8d6c: "Cheng " +0x8d6d: "Zhe " +0x8d6e: "Xia " +0x8d6f: "Tang " +0x8d70: "Zou " +0x8d71: "Zou " +0x8d72: "Li " +0x8d73: "Jiu " +0x8d74: "Fu " +0x8d75: "Zhao " +0x8d76: "Gan " +0x8d77: "Qi " +0x8d78: "Shan " +0x8d79: "Qiong " +0x8d7a: "Qin " +0x8d7b: "Xian " +0x8d7c: "Ci " +0x8d7d: "Jue " +0x8d7e: "Qin " +0x8d7f: "Chi " +0x8d80: "Ci " +0x8d81: "Chen " +0x8d82: "Chen " +0x8d83: "Die " +0x8d84: "Ju " +0x8d85: "Chao " +0x8d86: "Di " +0x8d87: "Se " +0x8d88: "Zhan " +0x8d89: "Zhu " +0x8d8a: "Yue " +0x8d8b: "Qu " +0x8d8c: "Jie " +0x8d8d: "Chi " +0x8d8e: "Chu " +0x8d8f: "Gua " +0x8d90: "Xue " +0x8d91: "Ci " +0x8d92: "Tiao " +0x8d93: "Duo " +0x8d94: "Lie " +0x8d95: "Gan " +0x8d96: "Suo " +0x8d97: "Cu " +0x8d98: "Xi " +0x8d99: "Zhao " +0x8d9a: "Su " +0x8d9b: "Yin " +0x8d9c: "Ju " +0x8d9d: "Jian " +0x8d9e: "Que " +0x8d9f: "Tang " +0x8da0: "Chuo " +0x8da1: "Cui " +0x8da2: "Lu " +0x8da3: "Qu " +0x8da4: "Dang " +0x8da5: "Qiu " +0x8da6: "Zi " +0x8da7: "Ti " +0x8da8: "Qu " +0x8da9: "Chi " +0x8daa: "Huang " +0x8dab: "Qiao " +0x8dac: "Qiao " +0x8dad: "Yao " +0x8dae: "Zao " +0x8daf: "Ti " +0x8db0: "[?] " +0x8db1: "Zan " +0x8db2: "Zan " +0x8db3: "Zu " +0x8db4: "Pa " +0x8db5: "Bao " +0x8db6: "Ku " +0x8db7: "Ke " +0x8db8: "Dun " +0x8db9: "Jue " +0x8dba: "Fu " +0x8dbb: "Chen " +0x8dbc: "Jian " +0x8dbd: "Fang " +0x8dbe: "Zhi " +0x8dbf: "Sa " +0x8dc0: "Yue " +0x8dc1: "Pa " +0x8dc2: "Qi " +0x8dc3: "Yue " +0x8dc4: "Qiang " +0x8dc5: "Tuo " +0x8dc6: "Tai " +0x8dc7: "Yi " +0x8dc8: "Nian " +0x8dc9: "Ling " +0x8dca: "Mei " +0x8dcb: "Ba " +0x8dcc: "Die " +0x8dcd: "Ku " +0x8dce: "Tuo " +0x8dcf: "Jia " +0x8dd0: "Ci " +0x8dd1: "Pao " +0x8dd2: "Qia " +0x8dd3: "Zhu " +0x8dd4: "Ju " +0x8dd5: "Die " +0x8dd6: "Zhi " +0x8dd7: "Fu " +0x8dd8: "Pan " +0x8dd9: "Ju " +0x8dda: "Shan " +0x8ddb: "Bo " +0x8ddc: "Ni " +0x8ddd: "Ju " +0x8dde: "Li " +0x8ddf: "Gen " +0x8de0: "Yi " +0x8de1: "Ji " +0x8de2: "Dai " +0x8de3: "Xian " +0x8de4: "Jiao " +0x8de5: "Duo " +0x8de6: "Zhu " +0x8de7: "Zhuan " +0x8de8: "Kua " +0x8de9: "Zhuai " +0x8dea: "Gui " +0x8deb: "Qiong " +0x8dec: "Kui " +0x8ded: "Xiang " +0x8dee: "Chi " +0x8def: "Lu " +0x8df0: "Beng " +0x8df1: "Zhi " +0x8df2: "Jia " +0x8df3: "Tiao " +0x8df4: "Cai " +0x8df5: "Jian " +0x8df6: "Ta " +0x8df7: "Qiao " +0x8df8: "Bi " +0x8df9: "Xian " +0x8dfa: "Duo " +0x8dfb: "Ji " +0x8dfc: "Ju " +0x8dfd: "Ji " +0x8dfe: "Shu " +0x8dff: "Tu " +/* x08e */ +0x8e00: "Chu " +0x8e01: "Jing " +0x8e02: "Nie " +0x8e03: "Xiao " +0x8e04: "Bo " +0x8e05: "Chi " +0x8e06: "Qun " +0x8e07: "Mou " +0x8e08: "Shu " +0x8e09: "Lang " +0x8e0a: "Yong " +0x8e0b: "Jiao " +0x8e0c: "Chou " +0x8e0d: "Qiao " +0x8e0e: "[?] " +0x8e0f: "Ta " +0x8e10: "Jian " +0x8e11: "Qi " +0x8e12: "Wo " +0x8e13: "Wei " +0x8e14: "Zhuo " +0x8e15: "Jie " +0x8e16: "Ji " +0x8e17: "Nie " +0x8e18: "Ju " +0x8e19: "Ju " +0x8e1a: "Lun " +0x8e1b: "Lu " +0x8e1c: "Leng " +0x8e1d: "Huai " +0x8e1e: "Ju " +0x8e1f: "Chi " +0x8e20: "Wan " +0x8e21: "Quan " +0x8e22: "Ti " +0x8e23: "Bo " +0x8e24: "Zu " +0x8e25: "Qie " +0x8e26: "Ji " +0x8e27: "Cu " +0x8e28: "Zong " +0x8e29: "Cai " +0x8e2a: "Zong " +0x8e2b: "Peng " +0x8e2c: "Zhi " +0x8e2d: "Zheng " +0x8e2e: "Dian " +0x8e2f: "Zhi " +0x8e30: "Yu " +0x8e31: "Duo " +0x8e32: "Dun " +0x8e33: "Chun " +0x8e34: "Yong " +0x8e35: "Zhong " +0x8e36: "Di " +0x8e37: "Zhe " +0x8e38: "Chen " +0x8e39: "Chuai " +0x8e3a: "Jian " +0x8e3b: "Gua " +0x8e3c: "Tang " +0x8e3d: "Ju " +0x8e3e: "Fu " +0x8e3f: "Zu " +0x8e40: "Die " +0x8e41: "Pian " +0x8e42: "Rou " +0x8e43: "Nuo " +0x8e44: "Ti " +0x8e45: "Cha " +0x8e46: "Tui " +0x8e47: "Jian " +0x8e48: "Dao " +0x8e49: "Cuo " +0x8e4a: "Xi " +0x8e4b: "Ta " +0x8e4c: "Qiang " +0x8e4d: "Zhan " +0x8e4e: "Dian " +0x8e4f: "Ti " +0x8e50: "Ji " +0x8e51: "Nie " +0x8e52: "Man " +0x8e53: "Liu " +0x8e54: "Zhan " +0x8e55: "Bi " +0x8e56: "Chong " +0x8e57: "Lu " +0x8e58: "Liao " +0x8e59: "Cu " +0x8e5a: "Tang " +0x8e5b: "Dai " +0x8e5c: "Suo " +0x8e5d: "Xi " +0x8e5e: "Kui " +0x8e5f: "Ji " +0x8e60: "Zhi " +0x8e61: "Qiang " +0x8e62: "Di " +0x8e63: "Man " +0x8e64: "Zong " +0x8e65: "Lian " +0x8e66: "Beng " +0x8e67: "Zao " +0x8e68: "Nian " +0x8e69: "Bie " +0x8e6a: "Tui " +0x8e6b: "Ju " +0x8e6c: "Deng " +0x8e6d: "Ceng " +0x8e6e: "Xian " +0x8e6f: "Fan " +0x8e70: "Chu " +0x8e71: "Zhong " +0x8e72: "Dun " +0x8e73: "Bo " +0x8e74: "Cu " +0x8e75: "Zu " +0x8e76: "Jue " +0x8e77: "Jue " +0x8e78: "Lin " +0x8e79: "Ta " +0x8e7a: "Qiao " +0x8e7b: "Qiao " +0x8e7c: "Pu " +0x8e7d: "Liao " +0x8e7e: "Dun " +0x8e7f: "Cuan " +0x8e80: "Kuang " +0x8e81: "Zao " +0x8e82: "Ta " +0x8e83: "Bi " +0x8e84: "Bi " +0x8e85: "Zhu " +0x8e86: "Ju " +0x8e87: "Chu " +0x8e88: "Qiao " +0x8e89: "Dun " +0x8e8a: "Chou " +0x8e8b: "Ji " +0x8e8c: "Wu " +0x8e8d: "Yue " +0x8e8e: "Nian " +0x8e8f: "Lin " +0x8e90: "Lie " +0x8e91: "Zhi " +0x8e92: "Li " +0x8e93: "Zhi " +0x8e94: "Chan " +0x8e95: "Chu " +0x8e96: "Duan " +0x8e97: "Wei " +0x8e98: "Long " +0x8e99: "Lin " +0x8e9a: "Xian " +0x8e9b: "Wei " +0x8e9c: "Zuan " +0x8e9d: "Lan " +0x8e9e: "Xie " +0x8e9f: "Rang " +0x8ea0: "Xie " +0x8ea1: "Nie " +0x8ea2: "Ta " +0x8ea3: "Qu " +0x8ea4: "Jie " +0x8ea5: "Cuan " +0x8ea6: "Zuan " +0x8ea7: "Xi " +0x8ea8: "Kui " +0x8ea9: "Jue " +0x8eaa: "Lin " +0x8eab: "Shen " +0x8eac: "Gong " +0x8ead: "Dan " +0x8eae: "Segare " +0x8eaf: "Qu " +0x8eb0: "Ti " +0x8eb1: "Duo " +0x8eb2: "Duo " +0x8eb3: "Gong " +0x8eb4: "Lang " +0x8eb5: "Nerau " +0x8eb6: "Luo " +0x8eb7: "Ai " +0x8eb8: "Ji " +0x8eb9: "Ju " +0x8eba: "Tang " +0x8ebb: "Utsuke " +0x8ebc: "[?] " +0x8ebd: "Yan " +0x8ebe: "Shitsuke " +0x8ebf: "Kang " +0x8ec0: "Qu " +0x8ec1: "Lou " +0x8ec2: "Lao " +0x8ec3: "Tuo " +0x8ec4: "Zhi " +0x8ec5: "Yagate " +0x8ec6: "Ti " +0x8ec7: "Dao " +0x8ec8: "Yagate " +0x8ec9: "Yu " +0x8eca: "Che " +0x8ecb: "Ya " +0x8ecc: "Gui " +0x8ecd: "Jun " +0x8ece: "Wei " +0x8ecf: "Yue " +0x8ed0: "Xin " +0x8ed1: "Di " +0x8ed2: "Xuan " +0x8ed3: "Fan " +0x8ed4: "Ren " +0x8ed5: "Shan " +0x8ed6: "Qiang " +0x8ed7: "Shu " +0x8ed8: "Tun " +0x8ed9: "Chen " +0x8eda: "Dai " +0x8edb: "E " +0x8edc: "Na " +0x8edd: "Qi " +0x8ede: "Mao " +0x8edf: "Ruan " +0x8ee0: "Ren " +0x8ee1: "Fan " +0x8ee2: "Zhuan " +0x8ee3: "Hong " +0x8ee4: "Hu " +0x8ee5: "Qu " +0x8ee6: "Huang " +0x8ee7: "Di " +0x8ee8: "Ling " +0x8ee9: "Dai " +0x8eea: "Ao " +0x8eeb: "Zhen " +0x8eec: "Fan " +0x8eed: "Kuang " +0x8eee: "Ang " +0x8eef: "Peng " +0x8ef0: "Bei " +0x8ef1: "Gu " +0x8ef2: "Ku " +0x8ef3: "Pao " +0x8ef4: "Zhu " +0x8ef5: "Rong " +0x8ef6: "E " +0x8ef7: "Ba " +0x8ef8: "Zhou " +0x8ef9: "Zhi " +0x8efa: "Yao " +0x8efb: "Ke " +0x8efc: "Yi " +0x8efd: "Qing " +0x8efe: "Shi " +0x8eff: "Ping " +/* x08f */ +0x8f00: "Er " +0x8f01: "Qiong " +0x8f02: "Ju " +0x8f03: "Jiao " +0x8f04: "Guang " +0x8f05: "Lu " +0x8f06: "Kai " +0x8f07: "Quan " +0x8f08: "Zhou " +0x8f09: "Zai " +0x8f0a: "Zhi " +0x8f0b: "She " +0x8f0c: "Liang " +0x8f0d: "Yu " +0x8f0e: "Shao " +0x8f0f: "You " +0x8f10: "Huan " +0x8f11: "Yun " +0x8f12: "Zhe " +0x8f13: "Wan " +0x8f14: "Fu " +0x8f15: "Qing " +0x8f16: "Zhou " +0x8f17: "Ni " +0x8f18: "Ling " +0x8f19: "Zhe " +0x8f1a: "Zhan " +0x8f1b: "Liang " +0x8f1c: "Zi " +0x8f1d: "Hui " +0x8f1e: "Wang " +0x8f1f: "Chuo " +0x8f20: "Guo " +0x8f21: "Kan " +0x8f22: "Yi " +0x8f23: "Peng " +0x8f24: "Qian " +0x8f25: "Gun " +0x8f26: "Nian " +0x8f27: "Pian " +0x8f28: "Guan " +0x8f29: "Bei " +0x8f2a: "Lun " +0x8f2b: "Pai " +0x8f2c: "Liang " +0x8f2d: "Ruan " +0x8f2e: "Rou " +0x8f2f: "Ji " +0x8f30: "Yang " +0x8f31: "Xian " +0x8f32: "Chuan " +0x8f33: "Cou " +0x8f34: "Qun " +0x8f35: "Ge " +0x8f36: "You " +0x8f37: "Hong " +0x8f38: "Shu " +0x8f39: "Fu " +0x8f3a: "Zi " +0x8f3b: "Fu " +0x8f3c: "Wen " +0x8f3d: "Ben " +0x8f3e: "Zhan " +0x8f3f: "Yu " +0x8f40: "Wen " +0x8f41: "Tao " +0x8f42: "Gu " +0x8f43: "Zhen " +0x8f44: "Xia " +0x8f45: "Yuan " +0x8f46: "Lu " +0x8f47: "Jiu " +0x8f48: "Chao " +0x8f49: "Zhuan " +0x8f4a: "Wei " +0x8f4b: "Hun " +0x8f4c: "Sori " +0x8f4d: "Che " +0x8f4e: "Jiao " +0x8f4f: "Zhan " +0x8f50: "Pu " +0x8f51: "Lao " +0x8f52: "Fen " +0x8f53: "Fan " +0x8f54: "Lin " +0x8f55: "Ge " +0x8f56: "Se " +0x8f57: "Kan " +0x8f58: "Huan " +0x8f59: "Yi " +0x8f5a: "Ji " +0x8f5b: "Dui " +0x8f5c: "Er " +0x8f5d: "Yu " +0x8f5e: "Xian " +0x8f5f: "Hong " +0x8f60: "Lei " +0x8f61: "Pei " +0x8f62: "Li " +0x8f63: "Li " +0x8f64: "Lu " +0x8f65: "Lin " +0x8f66: "Che " +0x8f67: "Ya " +0x8f68: "Gui " +0x8f69: "Xuan " +0x8f6a: "Di " +0x8f6b: "Ren " +0x8f6c: "Zhuan " +0x8f6d: "E " +0x8f6e: "Lun " +0x8f6f: "Ruan " +0x8f70: "Hong " +0x8f71: "Ku " +0x8f72: "Ke " +0x8f73: "Lu " +0x8f74: "Zhou " +0x8f75: "Zhi " +0x8f76: "Yi " +0x8f77: "Hu " +0x8f78: "Zhen " +0x8f79: "Li " +0x8f7a: "Yao " +0x8f7b: "Qing " +0x8f7c: "Shi " +0x8f7d: "Zai " +0x8f7e: "Zhi " +0x8f7f: "Jiao " +0x8f80: "Zhou " +0x8f81: "Quan " +0x8f82: "Lu " +0x8f83: "Jiao " +0x8f84: "Zhe " +0x8f85: "Fu " +0x8f86: "Liang " +0x8f87: "Nian " +0x8f88: "Bei " +0x8f89: "Hui " +0x8f8a: "Gun " +0x8f8b: "Wang " +0x8f8c: "Liang " +0x8f8d: "Chuo " +0x8f8e: "Zi " +0x8f8f: "Cou " +0x8f90: "Fu " +0x8f91: "Ji " +0x8f92: "Wen " +0x8f93: "Shu " +0x8f94: "Pei " +0x8f95: "Yuan " +0x8f96: "Xia " +0x8f97: "Zhan " +0x8f98: "Lu " +0x8f99: "Che " +0x8f9a: "Lin " +0x8f9b: "Xin " +0x8f9c: "Gu " +0x8f9d: "Ci " +0x8f9e: "Ci " +0x8f9f: "Pi " +0x8fa0: "Zui " +0x8fa1: "Bian " +0x8fa2: "La " +0x8fa3: "La " +0x8fa4: "Ci " +0x8fa5: "Xue " +0x8fa6: "Ban " +0x8fa7: "Bian " +0x8fa8: "Bian " +0x8fa9: "Bian " +0x8faa: "[?] " +0x8fab: "Bian " +0x8fac: "Ban " +0x8fad: "Ci " +0x8fae: "Bian " +0x8faf: "Bian " +0x8fb0: "Chen " +0x8fb1: "Ru " +0x8fb2: "Nong " +0x8fb3: "Nong " +0x8fb4: "Zhen " +0x8fb5: "Chuo " +0x8fb6: "Chuo " +0x8fb7: "Suberu " +0x8fb8: "Reng " +0x8fb9: "Bian " +0x8fba: "Bian " +0x8fbb: "Sip " +0x8fbc: "Ip " +0x8fbd: "Liao " +0x8fbe: "Da " +0x8fbf: "Chan " +0x8fc0: "Gan " +0x8fc1: "Qian " +0x8fc2: "Yu " +0x8fc3: "Yu " +0x8fc4: "Qi " +0x8fc5: "Xun " +0x8fc6: "Yi " +0x8fc7: "Guo " +0x8fc8: "Mai " +0x8fc9: "Qi " +0x8fca: "Za " +0x8fcb: "Wang " +0x8fcc: "Jia " +0x8fcd: "Zhun " +0x8fce: "Ying " +0x8fcf: "Ti " +0x8fd0: "Yun " +0x8fd1: "Jin " +0x8fd2: "Hang " +0x8fd3: "Ya " +0x8fd4: "Fan " +0x8fd5: "Wu " +0x8fd6: "Da " +0x8fd7: "E " +0x8fd8: "Huan " +0x8fd9: "Zhe " +0x8fda: "Totemo " +0x8fdb: "Jin " +0x8fdc: "Yuan " +0x8fdd: "Wei " +0x8fde: "Lian " +0x8fdf: "Chi " +0x8fe0: "Che " +0x8fe1: "Ni " +0x8fe2: "Tiao " +0x8fe3: "Zhi " +0x8fe4: "Yi " +0x8fe5: "Jiong " +0x8fe6: "Jia " +0x8fe7: "Chen " +0x8fe8: "Dai " +0x8fe9: "Er " +0x8fea: "Di " +0x8feb: "Po " +0x8fec: "Wang " +0x8fed: "Die " +0x8fee: "Ze " +0x8fef: "Tao " +0x8ff0: "Shu " +0x8ff1: "Tuo " +0x8ff2: "Kep " +0x8ff3: "Jing " +0x8ff4: "Hui " +0x8ff5: "Tong " +0x8ff6: "You " +0x8ff7: "Mi " +0x8ff8: "Beng " +0x8ff9: "Ji " +0x8ffa: "Nai " +0x8ffb: "Yi " +0x8ffc: "Jie " +0x8ffd: "Zhui " +0x8ffe: "Lie " +0x8fff: "Xun " +/* x090 */ +0x9000: "Tui " +0x9001: "Song " +0x9002: "Gua " +0x9003: "Tao " +0x9004: "Pang " +0x9005: "Hou " +0x9006: "Ni " +0x9007: "Dun " +0x9008: "Jiong " +0x9009: "Xuan " +0x900a: "Xun " +0x900b: "Bu " +0x900c: "You " +0x900d: "Xiao " +0x900e: "Qiu " +0x900f: "Tou " +0x9010: "Zhu " +0x9011: "Qiu " +0x9012: "Di " +0x9013: "Di " +0x9014: "Tu " +0x9015: "Jing " +0x9016: "Ti " +0x9017: "Dou " +0x9018: "Yi " +0x9019: "Zhe " +0x901a: "Tong " +0x901b: "Guang " +0x901c: "Wu " +0x901d: "Shi " +0x901e: "Cheng " +0x901f: "Su " +0x9020: "Zao " +0x9021: "Qun " +0x9022: "Feng " +0x9023: "Lian " +0x9024: "Suo " +0x9025: "Hui " +0x9026: "Li " +0x9027: "Sako " +0x9028: "Lai " +0x9029: "Ben " +0x902a: "Cuo " +0x902b: "Jue " +0x902c: "Beng " +0x902d: "Huan " +0x902e: "Dai " +0x902f: "Lu " +0x9030: "You " +0x9031: "Zhou " +0x9032: "Jin " +0x9033: "Yu " +0x9034: "Chuo " +0x9035: "Kui " +0x9036: "Wei " +0x9037: "Ti " +0x9038: "Yi " +0x9039: "Da " +0x903a: "Yuan " +0x903b: "Luo " +0x903c: "Bi " +0x903d: "Nuo " +0x903e: "Yu " +0x903f: "Dang " +0x9040: "Sui " +0x9041: "Dun " +0x9042: "Sui " +0x9043: "Yan " +0x9044: "Chuan " +0x9045: "Chi " +0x9046: "Ti " +0x9047: "Yu " +0x9048: "Shi " +0x9049: "Zhen " +0x904a: "You " +0x904b: "Yun " +0x904c: "E " +0x904d: "Bian " +0x904e: "Guo " +0x904f: "E " +0x9050: "Xia " +0x9051: "Huang " +0x9052: "Qiu " +0x9053: "Dao " +0x9054: "Da " +0x9055: "Wei " +0x9056: "Appare " +0x9057: "Yi " +0x9058: "Gou " +0x9059: "Yao " +0x905a: "Chu " +0x905b: "Liu " +0x905c: "Xun " +0x905d: "Ta " +0x905e: "Di " +0x905f: "Chi " +0x9060: "Yuan " +0x9061: "Su " +0x9062: "Ta " +0x9063: "Qian " +0x9064: "[?] " +0x9065: "Yao " +0x9066: "Guan " +0x9067: "Zhang " +0x9068: "Ao " +0x9069: "Shi " +0x906a: "Ce " +0x906b: "Chi " +0x906c: "Su " +0x906d: "Zao " +0x906e: "Zhe " +0x906f: "Dun " +0x9070: "Di " +0x9071: "Lou " +0x9072: "Chi " +0x9073: "Cuo " +0x9074: "Lin " +0x9075: "Zun " +0x9076: "Rao " +0x9077: "Qian " +0x9078: "Xuan " +0x9079: "Yu " +0x907a: "Yi " +0x907b: "Wu " +0x907c: "Liao " +0x907d: "Ju " +0x907e: "Shi " +0x907f: "Bi " +0x9080: "Yao " +0x9081: "Mai " +0x9082: "Xie " +0x9083: "Sui " +0x9084: "Huan " +0x9085: "Zhan " +0x9086: "Teng " +0x9087: "Er " +0x9088: "Miao " +0x9089: "Bian " +0x908a: "Bian " +0x908b: "La " +0x908c: "Li " +0x908d: "Yuan " +0x908e: "Yao " +0x908f: "Luo " +0x9090: "Li " +0x9091: "Yi " +0x9092: "Ting " +0x9093: "Deng " +0x9094: "Qi " +0x9095: "Yong " +0x9096: "Shan " +0x9097: "Han " +0x9098: "Yu " +0x9099: "Mang " +0x909a: "Ru " +0x909b: "Qiong " +0x909c: "[?] " +0x909d: "Kuang " +0x909e: "Fu " +0x909f: "Kang " +0x90a0: "Bin " +0x90a1: "Fang " +0x90a2: "Xing " +0x90a3: "Na " +0x90a4: "Xin " +0x90a5: "Shen " +0x90a6: "Bang " +0x90a7: "Yuan " +0x90a8: "Cun " +0x90a9: "Huo " +0x90aa: "Xie " +0x90ab: "Bang " +0x90ac: "Wu " +0x90ad: "Ju " +0x90ae: "You " +0x90af: "Han " +0x90b0: "Tai " +0x90b1: "Qiu " +0x90b2: "Bi " +0x90b3: "Pei " +0x90b4: "Bing " +0x90b5: "Shao " +0x90b6: "Bei " +0x90b7: "Wa " +0x90b8: "Di " +0x90b9: "Zou " +0x90ba: "Ye " +0x90bb: "Lin " +0x90bc: "Kuang " +0x90bd: "Gui " +0x90be: "Zhu " +0x90bf: "Shi " +0x90c0: "Ku " +0x90c1: "Yu " +0x90c2: "Gai " +0x90c3: "Ge " +0x90c4: "Xi " +0x90c5: "Zhi " +0x90c6: "Ji " +0x90c7: "Xun " +0x90c8: "Hou " +0x90c9: "Xing " +0x90ca: "Jiao " +0x90cb: "Xi " +0x90cc: "Gui " +0x90cd: "Nuo " +0x90ce: "Lang " +0x90cf: "Jia " +0x90d0: "Kuai " +0x90d1: "Zheng " +0x90d2: "Otoko " +0x90d3: "Yun " +0x90d4: "Yan " +0x90d5: "Cheng " +0x90d6: "Dou " +0x90d7: "Chi " +0x90d8: "Lu " +0x90d9: "Fu " +0x90da: "Wu " +0x90db: "Fu " +0x90dc: "Gao " +0x90dd: "Hao " +0x90de: "Lang " +0x90df: "Jia " +0x90e0: "Geng " +0x90e1: "Jun " +0x90e2: "Ying " +0x90e3: "Bo " +0x90e4: "Xi " +0x90e5: "Bei " +0x90e6: "Li " +0x90e7: "Yun " +0x90e8: "Bu " +0x90e9: "Xiao " +0x90ea: "Qi " +0x90eb: "Pi " +0x90ec: "Qing " +0x90ed: "Guo " +0x90ee: "Zhou " +0x90ef: "Tan " +0x90f0: "Zou " +0x90f1: "Ping " +0x90f2: "Lai " +0x90f3: "Ni " +0x90f4: "Chen " +0x90f5: "You " +0x90f6: "Bu " +0x90f7: "Xiang " +0x90f8: "Dan " +0x90f9: "Ju " +0x90fa: "Yong " +0x90fb: "Qiao " +0x90fc: "Yi " +0x90fd: "Du " +0x90fe: "Yan " +0x90ff: "Mei " +/* x091 */ +0x9100: "Ruo " +0x9101: "Bei " +0x9102: "E " +0x9103: "Yu " +0x9104: "Juan " +0x9105: "Yu " +0x9106: "Yun " +0x9107: "Hou " +0x9108: "Kui " +0x9109: "Xiang " +0x910a: "Xiang " +0x910b: "Sou " +0x910c: "Tang " +0x910d: "Ming " +0x910e: "Xi " +0x910f: "Ru " +0x9110: "Chu " +0x9111: "Zi " +0x9112: "Zou " +0x9113: "Ju " +0x9114: "Wu " +0x9115: "Xiang " +0x9116: "Yun " +0x9117: "Hao " +0x9118: "Yong " +0x9119: "Bi " +0x911a: "Mo " +0x911b: "Chao " +0x911c: "Fu " +0x911d: "Liao " +0x911e: "Yin " +0x911f: "Zhuan " +0x9120: "Hu " +0x9121: "Qiao " +0x9122: "Yan " +0x9123: "Zhang " +0x9124: "Fan " +0x9125: "Qiao " +0x9126: "Xu " +0x9127: "Deng " +0x9128: "Bi " +0x9129: "Xin " +0x912a: "Bi " +0x912b: "Ceng " +0x912c: "Wei " +0x912d: "Zheng " +0x912e: "Mao " +0x912f: "Shan " +0x9130: "Lin " +0x9131: "Po " +0x9132: "Dan " +0x9133: "Meng " +0x9134: "Ye " +0x9135: "Cao " +0x9136: "Kuai " +0x9137: "Feng " +0x9138: "Meng " +0x9139: "Zou " +0x913a: "Kuang " +0x913b: "Lian " +0x913c: "Zan " +0x913d: "Chan " +0x913e: "You " +0x913f: "Qi " +0x9140: "Yan " +0x9141: "Chan " +0x9142: "Zan " +0x9143: "Ling " +0x9144: "Huan " +0x9145: "Xi " +0x9146: "Feng " +0x9147: "Zan " +0x9148: "Li " +0x9149: "You " +0x914a: "Ding " +0x914b: "Qiu " +0x914c: "Zhuo " +0x914d: "Pei " +0x914e: "Zhou " +0x914f: "Yi " +0x9150: "Hang " +0x9151: "Yu " +0x9152: "Jiu " +0x9153: "Yan " +0x9154: "Zui " +0x9155: "Mao " +0x9156: "Dan " +0x9157: "Xu " +0x9158: "Tou " +0x9159: "Zhen " +0x915a: "Fen " +0x915b: "Sakenomoto " +0x915c: "[?] " +0x915d: "Yun " +0x915e: "Tai " +0x915f: "Tian " +0x9160: "Qia " +0x9161: "Tuo " +0x9162: "Zuo " +0x9163: "Han " +0x9164: "Gu " +0x9165: "Su " +0x9166: "Po " +0x9167: "Chou " +0x9168: "Zai " +0x9169: "Ming " +0x916a: "Luo " +0x916b: "Chuo " +0x916c: "Chou " +0x916d: "You " +0x916e: "Tong " +0x916f: "Zhi " +0x9170: "Xian " +0x9171: "Jiang " +0x9172: "Cheng " +0x9173: "Yin " +0x9174: "Tu " +0x9175: "Xiao " +0x9176: "Mei " +0x9177: "Ku " +0x9178: "Suan " +0x9179: "Lei " +0x917a: "Pu " +0x917b: "Zui " +0x917c: "Hai " +0x917d: "Yan " +0x917e: "Xi " +0x917f: "Niang " +0x9180: "Wei " +0x9181: "Lu " +0x9182: "Lan " +0x9183: "Yan " +0x9184: "Tao " +0x9185: "Pei " +0x9186: "Zhan " +0x9187: "Chun " +0x9188: "Tan " +0x9189: "Zui " +0x918a: "Chuo " +0x918b: "Cu " +0x918c: "Kun " +0x918d: "Ti " +0x918e: "Mian " +0x918f: "Du " +0x9190: "Hu " +0x9191: "Xu " +0x9192: "Xing " +0x9193: "Tan " +0x9194: "Jiu " +0x9195: "Chun " +0x9196: "Yun " +0x9197: "Po " +0x9198: "Ke " +0x9199: "Sou " +0x919a: "Mi " +0x919b: "Quan " +0x919c: "Chou " +0x919d: "Cuo " +0x919e: "Yun " +0x919f: "Yong " +0x91a0: "Ang " +0x91a1: "Zha " +0x91a2: "Hai " +0x91a3: "Tang " +0x91a4: "Jiang " +0x91a5: "Piao " +0x91a6: "Shan " +0x91a7: "Yu " +0x91a8: "Li " +0x91a9: "Zao " +0x91aa: "Lao " +0x91ab: "Yi " +0x91ac: "Jiang " +0x91ad: "Pu " +0x91ae: "Jiao " +0x91af: "Xi " +0x91b0: "Tan " +0x91b1: "Po " +0x91b2: "Nong " +0x91b3: "Yi " +0x91b4: "Li " +0x91b5: "Ju " +0x91b6: "Jiao " +0x91b7: "Yi " +0x91b8: "Niang " +0x91b9: "Ru " +0x91ba: "Xun " +0x91bb: "Chou " +0x91bc: "Yan " +0x91bd: "Ling " +0x91be: "Mi " +0x91bf: "Mi " +0x91c0: "Niang " +0x91c1: "Xin " +0x91c2: "Jiao " +0x91c3: "Xi " +0x91c4: "Mi " +0x91c5: "Yan " +0x91c6: "Bian " +0x91c7: "Cai " +0x91c8: "Shi " +0x91c9: "You " +0x91ca: "Shi " +0x91cb: "Shi " +0x91cc: "Li " +0x91cd: "Zhong " +0x91ce: "Ye " +0x91cf: "Liang " +0x91d0: "Li " +0x91d1: "Jin " +0x91d2: "Jin " +0x91d3: "Qiu " +0x91d4: "Yi " +0x91d5: "Diao " +0x91d6: "Dao " +0x91d7: "Zhao " +0x91d8: "Ding " +0x91d9: "Po " +0x91da: "Qiu " +0x91db: "He " +0x91dc: "Fu " +0x91dd: "Zhen " +0x91de: "Zhi " +0x91df: "Ba " +0x91e0: "Luan " +0x91e1: "Fu " +0x91e2: "Nai " +0x91e3: "Diao " +0x91e4: "Shan " +0x91e5: "Qiao " +0x91e6: "Kou " +0x91e7: "Chuan " +0x91e8: "Zi " +0x91e9: "Fan " +0x91ea: "Yu " +0x91eb: "Hua " +0x91ec: "Han " +0x91ed: "Gong " +0x91ee: "Qi " +0x91ef: "Mang " +0x91f0: "Ri " +0x91f1: "Di " +0x91f2: "Si " +0x91f3: "Xi " +0x91f4: "Yi " +0x91f5: "Chai " +0x91f6: "Shi " +0x91f7: "Tu " +0x91f8: "Xi " +0x91f9: "Nu " +0x91fa: "Qian " +0x91fb: "Ishiyumi " +0x91fc: "Jian " +0x91fd: "Pi " +0x91fe: "Ye " +0x91ff: "Yin " +/* x092 */ +0x9200: "Ba " +0x9201: "Fang " +0x9202: "Chen " +0x9203: "Xing " +0x9204: "Tou " +0x9205: "Yue " +0x9206: "Yan " +0x9207: "Fu " +0x9208: "Pi " +0x9209: "Na " +0x920a: "Xin " +0x920b: "E " +0x920c: "Jue " +0x920d: "Dun " +0x920e: "Gou " +0x920f: "Yin " +0x9210: "Qian " +0x9211: "Ban " +0x9212: "Ji " +0x9213: "Ren " +0x9214: "Chao " +0x9215: "Niu " +0x9216: "Fen " +0x9217: "Yun " +0x9218: "Ji " +0x9219: "Qin " +0x921a: "Pi " +0x921b: "Guo " +0x921c: "Hong " +0x921d: "Yin " +0x921e: "Jun " +0x921f: "Shi " +0x9220: "Yi " +0x9221: "Zhong " +0x9222: "Nie " +0x9223: "Gai " +0x9224: "Ri " +0x9225: "Huo " +0x9226: "Tai " +0x9227: "Kang " +0x9228: "Habaki " +0x9229: "Irori " +0x922a: "Ngaak " +0x922b: "[?] " +0x922c: "Duo " +0x922d: "Zi " +0x922e: "Ni " +0x922f: "Tu " +0x9230: "Shi " +0x9231: "Min " +0x9232: "Gu " +0x9233: "E " +0x9234: "Ling " +0x9235: "Bing " +0x9236: "Yi " +0x9237: "Gu " +0x9238: "Ba " +0x9239: "Pi " +0x923a: "Yu " +0x923b: "Si " +0x923c: "Zuo " +0x923d: "Bu " +0x923e: "You " +0x923f: "Dian " +0x9240: "Jia " +0x9241: "Zhen " +0x9242: "Shi " +0x9243: "Shi " +0x9244: "Tie " +0x9245: "Ju " +0x9246: "Zhan " +0x9247: "Shi " +0x9248: "She " +0x9249: "Xuan " +0x924a: "Zhao " +0x924b: "Bao " +0x924c: "He " +0x924d: "Bi " +0x924e: "Sheng " +0x924f: "Chu " +0x9250: "Shi " +0x9251: "Bo " +0x9252: "Zhu " +0x9253: "Chi " +0x9254: "Za " +0x9255: "Po " +0x9256: "Tong " +0x9257: "Qian " +0x9258: "Fu " +0x9259: "Zhai " +0x925a: "Liu " +0x925b: "Qian " +0x925c: "Fu " +0x925d: "Li " +0x925e: "Yue " +0x925f: "Pi " +0x9260: "Yang " +0x9261: "Ban " +0x9262: "Bo " +0x9263: "Jie " +0x9264: "Gou " +0x9265: "Shu " +0x9266: "Zheng " +0x9267: "Mu " +0x9268: "Ni " +0x9269: "Nie " +0x926a: "Di " +0x926b: "Jia " +0x926c: "Mu " +0x926d: "Dan " +0x926e: "Shen " +0x926f: "Yi " +0x9270: "Si " +0x9271: "Kuang " +0x9272: "Ka " +0x9273: "Bei " +0x9274: "Jian " +0x9275: "Tong " +0x9276: "Xing " +0x9277: "Hong " +0x9278: "Jiao " +0x9279: "Chi " +0x927a: "Er " +0x927b: "Ge " +0x927c: "Bing " +0x927d: "Shi " +0x927e: "Mou " +0x927f: "Jia " +0x9280: "Yin " +0x9281: "Jun " +0x9282: "Zhou " +0x9283: "Chong " +0x9284: "Shang " +0x9285: "Tong " +0x9286: "Mo " +0x9287: "Lei " +0x9288: "Ji " +0x9289: "Yu " +0x928a: "Xu " +0x928b: "Ren " +0x928c: "Zun " +0x928d: "Zhi " +0x928e: "Qiong " +0x928f: "Shan " +0x9290: "Chi " +0x9291: "Xian " +0x9292: "Xing " +0x9293: "Quan " +0x9294: "Pi " +0x9295: "Tie " +0x9296: "Zhu " +0x9297: "Hou " +0x9298: "Ming " +0x9299: "Kua " +0x929a: "Yao " +0x929b: "Xian " +0x929c: "Xian " +0x929d: "Xiu " +0x929e: "Jun " +0x929f: "Cha " +0x92a0: "Lao " +0x92a1: "Ji " +0x92a2: "Pi " +0x92a3: "Ru " +0x92a4: "Mi " +0x92a5: "Yi " +0x92a6: "Yin " +0x92a7: "Guang " +0x92a8: "An " +0x92a9: "Diou " +0x92aa: "You " +0x92ab: "Se " +0x92ac: "Kao " +0x92ad: "Qian " +0x92ae: "Luan " +0x92af: "Kasugai " +0x92b0: "Ai " +0x92b1: "Diao " +0x92b2: "Han " +0x92b3: "Rui " +0x92b4: "Shi " +0x92b5: "Keng " +0x92b6: "Qiu " +0x92b7: "Xiao " +0x92b8: "Zhe " +0x92b9: "Xiu " +0x92ba: "Zang " +0x92bb: "Ti " +0x92bc: "Cuo " +0x92bd: "Gua " +0x92be: "Gong " +0x92bf: "Zhong " +0x92c0: "Dou " +0x92c1: "Lu " +0x92c2: "Mei " +0x92c3: "Lang " +0x92c4: "Wan " +0x92c5: "Xin " +0x92c6: "Yun " +0x92c7: "Bei " +0x92c8: "Wu " +0x92c9: "Su " +0x92ca: "Yu " +0x92cb: "Chan " +0x92cc: "Ting " +0x92cd: "Bo " +0x92ce: "Han " +0x92cf: "Jia " +0x92d0: "Hong " +0x92d1: "Cuan " +0x92d2: "Feng " +0x92d3: "Chan " +0x92d4: "Wan " +0x92d5: "Zhi " +0x92d6: "Si " +0x92d7: "Xuan " +0x92d8: "Wu " +0x92d9: "Wu " +0x92da: "Tiao " +0x92db: "Gong " +0x92dc: "Zhuo " +0x92dd: "Lue " +0x92de: "Xing " +0x92df: "Qian " +0x92e0: "Shen " +0x92e1: "Han " +0x92e2: "Lue " +0x92e3: "Xie " +0x92e4: "Chu " +0x92e5: "Zheng " +0x92e6: "Ju " +0x92e7: "Xian " +0x92e8: "Tie " +0x92e9: "Mang " +0x92ea: "Pu " +0x92eb: "Li " +0x92ec: "Pan " +0x92ed: "Rui " +0x92ee: "Cheng " +0x92ef: "Gao " +0x92f0: "Li " +0x92f1: "Te " +0x92f2: "Pyeng " +0x92f3: "Zhu " +0x92f4: "[?] " +0x92f5: "Tu " +0x92f6: "Liu " +0x92f7: "Zui " +0x92f8: "Ju " +0x92f9: "Chang " +0x92fa: "Yuan " +0x92fb: "Jian " +0x92fc: "Gang " +0x92fd: "Diao " +0x92fe: "Tao " +0x92ff: "Chang " +/* x093 */ +0x9300: "Lun " +0x9301: "Kua " +0x9302: "Ling " +0x9303: "Bei " +0x9304: "Lu " +0x9305: "Li " +0x9306: "Qiang " +0x9307: "Pou " +0x9308: "Juan " +0x9309: "Min " +0x930a: "Zui " +0x930b: "Peng " +0x930c: "An " +0x930d: "Pi " +0x930e: "Xian " +0x930f: "Ya " +0x9310: "Zhui " +0x9311: "Lei " +0x9312: "A " +0x9313: "Kong " +0x9314: "Ta " +0x9315: "Kun " +0x9316: "Du " +0x9317: "Wei " +0x9318: "Chui " +0x9319: "Zi " +0x931a: "Zheng " +0x931b: "Ben " +0x931c: "Nie " +0x931d: "Cong " +0x931e: "Qun " +0x931f: "Tan " +0x9320: "Ding " +0x9321: "Qi " +0x9322: "Qian " +0x9323: "Zhuo " +0x9324: "Qi " +0x9325: "Yu " +0x9326: "Jin " +0x9327: "Guan " +0x9328: "Mao " +0x9329: "Chang " +0x932a: "Tian " +0x932b: "Xi " +0x932c: "Lian " +0x932d: "Tao " +0x932e: "Gu " +0x932f: "Cuo " +0x9330: "Shu " +0x9331: "Zhen " +0x9332: "Lu " +0x9333: "Meng " +0x9334: "Lu " +0x9335: "Hua " +0x9336: "Biao " +0x9337: "Ga " +0x9338: "Lai " +0x9339: "Ken " +0x933a: "Kazari " +0x933b: "Bu " +0x933c: "Nai " +0x933d: "Wan " +0x933e: "Zan " +0x933f: "[?] " +0x9340: "De " +0x9341: "Xian " +0x9342: "[?] " +0x9343: "Huo " +0x9344: "Liang " +0x9345: "[?] " +0x9346: "Men " +0x9347: "Kai " +0x9348: "Ying " +0x9349: "Di " +0x934a: "Lian " +0x934b: "Guo " +0x934c: "Xian " +0x934d: "Du " +0x934e: "Tu " +0x934f: "Wei " +0x9350: "Cong " +0x9351: "Fu " +0x9352: "Rou " +0x9353: "Ji " +0x9354: "E " +0x9355: "Rou " +0x9356: "Chen " +0x9357: "Ti " +0x9358: "Zha " +0x9359: "Hong " +0x935a: "Yang " +0x935b: "Duan " +0x935c: "Xia " +0x935d: "Yu " +0x935e: "Keng " +0x935f: "Xing " +0x9360: "Huang " +0x9361: "Wei " +0x9362: "Fu " +0x9363: "Zhao " +0x9364: "Cha " +0x9365: "Qie " +0x9366: "She " +0x9367: "Hong " +0x9368: "Kui " +0x9369: "Tian " +0x936a: "Mou " +0x936b: "Qiao " +0x936c: "Qiao " +0x936d: "Hou " +0x936e: "Tou " +0x936f: "Cong " +0x9370: "Huan " +0x9371: "Ye " +0x9372: "Min " +0x9373: "Jian " +0x9374: "Duan " +0x9375: "Jian " +0x9376: "Song " +0x9377: "Kui " +0x9378: "Hu " +0x9379: "Xuan " +0x937a: "Duo " +0x937b: "Jie " +0x937c: "Zhen " +0x937d: "Bian " +0x937e: "Zhong " +0x937f: "Zi " +0x9380: "Xiu " +0x9381: "Ye " +0x9382: "Mei " +0x9383: "Pai " +0x9384: "Ai " +0x9385: "Jie " +0x9386: "[?] " +0x9387: "Mei " +0x9388: "Chuo " +0x9389: "Ta " +0x938a: "Bang " +0x938b: "Xia " +0x938c: "Lian " +0x938d: "Suo " +0x938e: "Xi " +0x938f: "Liu " +0x9390: "Zu " +0x9391: "Ye " +0x9392: "Nou " +0x9393: "Weng " +0x9394: "Rong " +0x9395: "Tang " +0x9396: "Suo " +0x9397: "Qiang " +0x9398: "Ge " +0x9399: "Shuo " +0x939a: "Chui " +0x939b: "Bo " +0x939c: "Pan " +0x939d: "Sa " +0x939e: "Bi " +0x939f: "Sang " +0x93a0: "Gang " +0x93a1: "Zi " +0x93a2: "Wu " +0x93a3: "Ying " +0x93a4: "Huang " +0x93a5: "Tiao " +0x93a6: "Liu " +0x93a7: "Kai " +0x93a8: "Sun " +0x93a9: "Sha " +0x93aa: "Sou " +0x93ab: "Wan " +0x93ac: "Hao " +0x93ad: "Zhen " +0x93ae: "Zhen " +0x93af: "Luo " +0x93b0: "Yi " +0x93b1: "Yuan " +0x93b2: "Tang " +0x93b3: "Nie " +0x93b4: "Xi " +0x93b5: "Jia " +0x93b6: "Ge " +0x93b7: "Ma " +0x93b8: "Juan " +0x93b9: "Kasugai " +0x93ba: "Habaki " +0x93bb: "Suo " +0x93bc: "[?] " +0x93bd: "[?] " +0x93be: "[?] " +0x93bf: "Na " +0x93c0: "Lu " +0x93c1: "Suo " +0x93c2: "Ou " +0x93c3: "Zu " +0x93c4: "Tuan " +0x93c5: "Xiu " +0x93c6: "Guan " +0x93c7: "Xuan " +0x93c8: "Lian " +0x93c9: "Shou " +0x93ca: "Ao " +0x93cb: "Man " +0x93cc: "Mo " +0x93cd: "Luo " +0x93ce: "Bi " +0x93cf: "Wei " +0x93d0: "Liu " +0x93d1: "Di " +0x93d2: "Qiao " +0x93d3: "Cong " +0x93d4: "Yi " +0x93d5: "Lu " +0x93d6: "Ao " +0x93d7: "Keng " +0x93d8: "Qiang " +0x93d9: "Cui " +0x93da: "Qi " +0x93db: "Chang " +0x93dc: "Tang " +0x93dd: "Man " +0x93de: "Yong " +0x93df: "Chan " +0x93e0: "Feng " +0x93e1: "Jing " +0x93e2: "Biao " +0x93e3: "Shu " +0x93e4: "Lou " +0x93e5: "Xiu " +0x93e6: "Cong " +0x93e7: "Long " +0x93e8: "Zan " +0x93e9: "Jian " +0x93ea: "Cao " +0x93eb: "Li " +0x93ec: "Xia " +0x93ed: "Xi " +0x93ee: "Kang " +0x93ef: "[?] " +0x93f0: "Beng " +0x93f1: "[?] " +0x93f2: "[?] " +0x93f3: "Zheng " +0x93f4: "Lu " +0x93f5: "Hua " +0x93f6: "Ji " +0x93f7: "Pu " +0x93f8: "Hui " +0x93f9: "Qiang " +0x93fa: "Po " +0x93fb: "Lin " +0x93fc: "Suo " +0x93fd: "Xiu " +0x93fe: "San " +0x93ff: "Cheng " +/* x094 */ +0x9400: "Kui " +0x9401: "Si " +0x9402: "Liu " +0x9403: "Nao " +0x9404: "Heng " +0x9405: "Pie " +0x9406: "Sui " +0x9407: "Fan " +0x9408: "Qiao " +0x9409: "Quan " +0x940a: "Yang " +0x940b: "Tang " +0x940c: "Xiang " +0x940d: "Jue " +0x940e: "Jiao " +0x940f: "Zun " +0x9410: "Liao " +0x9411: "Jie " +0x9412: "Lao " +0x9413: "Dui " +0x9414: "Tan " +0x9415: "Zan " +0x9416: "Ji " +0x9417: "Jian " +0x9418: "Zhong " +0x9419: "Deng " +0x941a: "Ya " +0x941b: "Ying " +0x941c: "Dui " +0x941d: "Jue " +0x941e: "Nou " +0x941f: "Ti " +0x9420: "Pu " +0x9421: "Tie " +0x9422: "[?] " +0x9423: "[?] " +0x9424: "Ding " +0x9425: "Shan " +0x9426: "Kai " +0x9427: "Jian " +0x9428: "Fei " +0x9429: "Sui " +0x942a: "Lu " +0x942b: "Juan " +0x942c: "Hui " +0x942d: "Yu " +0x942e: "Lian " +0x942f: "Zhuo " +0x9430: "Qiao " +0x9431: "Qian " +0x9432: "Zhuo " +0x9433: "Lei " +0x9434: "Bi " +0x9435: "Tie " +0x9436: "Huan " +0x9437: "Ye " +0x9438: "Duo " +0x9439: "Guo " +0x943a: "Dang " +0x943b: "Ju " +0x943c: "Fen " +0x943d: "Da " +0x943e: "Bei " +0x943f: "Yi " +0x9440: "Ai " +0x9441: "Zong " +0x9442: "Xun " +0x9443: "Diao " +0x9444: "Zhu " +0x9445: "Heng " +0x9446: "Zhui " +0x9447: "Ji " +0x9448: "Nie " +0x9449: "Ta " +0x944a: "Huo " +0x944b: "Qing " +0x944c: "Bin " +0x944d: "Ying " +0x944e: "Kui " +0x944f: "Ning " +0x9450: "Xu " +0x9451: "Jian " +0x9452: "Jian " +0x9453: "Yari " +0x9454: "Cha " +0x9455: "Zhi " +0x9456: "Mie " +0x9457: "Li " +0x9458: "Lei " +0x9459: "Ji " +0x945a: "Zuan " +0x945b: "Kuang " +0x945c: "Shang " +0x945d: "Peng " +0x945e: "La " +0x945f: "Du " +0x9460: "Shuo " +0x9461: "Chuo " +0x9462: "Lu " +0x9463: "Biao " +0x9464: "Bao " +0x9465: "Lu " +0x9466: "[?] " +0x9467: "[?] " +0x9468: "Long " +0x9469: "E " +0x946a: "Lu " +0x946b: "Xin " +0x946c: "Jian " +0x946d: "Lan " +0x946e: "Bo " +0x946f: "Jian " +0x9470: "Yao " +0x9471: "Chan " +0x9472: "Xiang " +0x9473: "Jian " +0x9474: "Xi " +0x9475: "Guan " +0x9476: "Cang " +0x9477: "Nie " +0x9478: "Lei " +0x9479: "Cuan " +0x947a: "Qu " +0x947b: "Pan " +0x947c: "Luo " +0x947d: "Zuan " +0x947e: "Luan " +0x947f: "Zao " +0x9480: "Nie " +0x9481: "Jue " +0x9482: "Tang " +0x9483: "Shu " +0x9484: "Lan " +0x9485: "Jin " +0x9486: "Qiu " +0x9487: "Yi " +0x9488: "Zhen " +0x9489: "Ding " +0x948a: "Zhao " +0x948b: "Po " +0x948c: "Diao " +0x948d: "Tu " +0x948e: "Qian " +0x948f: "Chuan " +0x9490: "Shan " +0x9491: "Ji " +0x9492: "Fan " +0x9493: "Diao " +0x9494: "Men " +0x9495: "Nu " +0x9496: "Xi " +0x9497: "Chai " +0x9498: "Xing " +0x9499: "Gai " +0x949a: "Bu " +0x949b: "Tai " +0x949c: "Ju " +0x949d: "Dun " +0x949e: "Chao " +0x949f: "Zhong " +0x94a0: "Na " +0x94a1: "Bei " +0x94a2: "Gang " +0x94a3: "Ban " +0x94a4: "Qian " +0x94a5: "Yao " +0x94a6: "Qin " +0x94a7: "Jun " +0x94a8: "Wu " +0x94a9: "Gou " +0x94aa: "Kang " +0x94ab: "Fang " +0x94ac: "Huo " +0x94ad: "Tou " +0x94ae: "Niu " +0x94af: "Ba " +0x94b0: "Yu " +0x94b1: "Qian " +0x94b2: "Zheng " +0x94b3: "Qian " +0x94b4: "Gu " +0x94b5: "Bo " +0x94b6: "E " +0x94b7: "Po " +0x94b8: "Bu " +0x94b9: "Ba " +0x94ba: "Yue " +0x94bb: "Zuan " +0x94bc: "Mu " +0x94bd: "Dan " +0x94be: "Jia " +0x94bf: "Dian " +0x94c0: "You " +0x94c1: "Tie " +0x94c2: "Bo " +0x94c3: "Ling " +0x94c4: "Shuo " +0x94c5: "Qian " +0x94c6: "Liu " +0x94c7: "Bao " +0x94c8: "Shi " +0x94c9: "Xuan " +0x94ca: "She " +0x94cb: "Bi " +0x94cc: "Ni " +0x94cd: "Pi " +0x94ce: "Duo " +0x94cf: "Xing " +0x94d0: "Kao " +0x94d1: "Lao " +0x94d2: "Er " +0x94d3: "Mang " +0x94d4: "Ya " +0x94d5: "You " +0x94d6: "Cheng " +0x94d7: "Jia " +0x94d8: "Ye " +0x94d9: "Nao " +0x94da: "Zhi " +0x94db: "Dang " +0x94dc: "Tong " +0x94dd: "Lu " +0x94de: "Diao " +0x94df: "Yin " +0x94e0: "Kai " +0x94e1: "Zha " +0x94e2: "Zhu " +0x94e3: "Xian " +0x94e4: "Ting " +0x94e5: "Diu " +0x94e6: "Xian " +0x94e7: "Hua " +0x94e8: "Quan " +0x94e9: "Sha " +0x94ea: "Jia " +0x94eb: "Yao " +0x94ec: "Ge " +0x94ed: "Ming " +0x94ee: "Zheng " +0x94ef: "Se " +0x94f0: "Jiao " +0x94f1: "Yi " +0x94f2: "Chan " +0x94f3: "Chong " +0x94f4: "Tang " +0x94f5: "An " +0x94f6: "Yin " +0x94f7: "Ru " +0x94f8: "Zhu " +0x94f9: "Lao " +0x94fa: "Pu " +0x94fb: "Wu " +0x94fc: "Lai " +0x94fd: "Te " +0x94fe: "Lian " +0x94ff: "Keng " +/* x095 */ +0x9500: "Xiao " +0x9501: "Suo " +0x9502: "Li " +0x9503: "Zheng " +0x9504: "Chu " +0x9505: "Guo " +0x9506: "Gao " +0x9507: "Tie " +0x9508: "Xiu " +0x9509: "Cuo " +0x950a: "Lue " +0x950b: "Feng " +0x950c: "Xin " +0x950d: "Liu " +0x950e: "Kai " +0x950f: "Jian " +0x9510: "Rui " +0x9511: "Ti " +0x9512: "Lang " +0x9513: "Qian " +0x9514: "Ju " +0x9515: "A " +0x9516: "Qiang " +0x9517: "Duo " +0x9518: "Tian " +0x9519: "Cuo " +0x951a: "Mao " +0x951b: "Ben " +0x951c: "Qi " +0x951d: "De " +0x951e: "Kua " +0x951f: "Kun " +0x9520: "Chang " +0x9521: "Xi " +0x9522: "Gu " +0x9523: "Luo " +0x9524: "Chui " +0x9525: "Zhui " +0x9526: "Jin " +0x9527: "Zhi " +0x9528: "Xian " +0x9529: "Juan " +0x952a: "Huo " +0x952b: "Pou " +0x952c: "Tan " +0x952d: "Ding " +0x952e: "Jian " +0x952f: "Ju " +0x9530: "Meng " +0x9531: "Zi " +0x9532: "Qie " +0x9533: "Ying " +0x9534: "Kai " +0x9535: "Qiang " +0x9536: "Song " +0x9537: "E " +0x9538: "Cha " +0x9539: "Qiao " +0x953a: "Zhong " +0x953b: "Duan " +0x953c: "Sou " +0x953d: "Huang " +0x953e: "Huan " +0x953f: "Ai " +0x9540: "Du " +0x9541: "Mei " +0x9542: "Lou " +0x9543: "Zi " +0x9544: "Fei " +0x9545: "Mei " +0x9546: "Mo " +0x9547: "Zhen " +0x9548: "Bo " +0x9549: "Ge " +0x954a: "Nie " +0x954b: "Tang " +0x954c: "Juan " +0x954d: "Nie " +0x954e: "Na " +0x954f: "Liu " +0x9550: "Hao " +0x9551: "Bang " +0x9552: "Yi " +0x9553: "Jia " +0x9554: "Bin " +0x9555: "Rong " +0x9556: "Biao " +0x9557: "Tang " +0x9558: "Man " +0x9559: "Luo " +0x955a: "Beng " +0x955b: "Yong " +0x955c: "Jing " +0x955d: "Di " +0x955e: "Zu " +0x955f: "Xuan " +0x9560: "Liu " +0x9561: "Tan " +0x9562: "Jue " +0x9563: "Liao " +0x9564: "Pu " +0x9565: "Lu " +0x9566: "Dui " +0x9567: "Lan " +0x9568: "Pu " +0x9569: "Cuan " +0x956a: "Qiang " +0x956b: "Deng " +0x956c: "Huo " +0x956d: "Lei " +0x956e: "Huan " +0x956f: "Zhuo " +0x9570: "Lian " +0x9571: "Yi " +0x9572: "Cha " +0x9573: "Biao " +0x9574: "La " +0x9575: "Chan " +0x9576: "Xiang " +0x9577: "Chang " +0x9578: "Chang " +0x9579: "Jiu " +0x957a: "Ao " +0x957b: "Die " +0x957c: "Qu " +0x957d: "Liao " +0x957e: "Mi " +0x957f: "Chang " +0x9580: "Men " +0x9581: "Ma " +0x9582: "Shuan " +0x9583: "Shan " +0x9584: "Huo " +0x9585: "Men " +0x9586: "Yan " +0x9587: "Bi " +0x9588: "Han " +0x9589: "Bi " +0x958a: "San " +0x958b: "Kai " +0x958c: "Kang " +0x958d: "Beng " +0x958e: "Hong " +0x958f: "Run " +0x9590: "San " +0x9591: "Xian " +0x9592: "Xian " +0x9593: "Jian " +0x9594: "Min " +0x9595: "Xia " +0x9596: "Yuru " +0x9597: "Dou " +0x9598: "Zha " +0x9599: "Nao " +0x959a: "Jian " +0x959b: "Peng " +0x959c: "Xia " +0x959d: "Ling " +0x959e: "Bian " +0x959f: "Bi " +0x95a0: "Run " +0x95a1: "He " +0x95a2: "Guan " +0x95a3: "Ge " +0x95a4: "Ge " +0x95a5: "Fa " +0x95a6: "Chu " +0x95a7: "Hong " +0x95a8: "Gui " +0x95a9: "Min " +0x95aa: "Se " +0x95ab: "Kun " +0x95ac: "Lang " +0x95ad: "Lu " +0x95ae: "Ting " +0x95af: "Sha " +0x95b0: "Ju " +0x95b1: "Yue " +0x95b2: "Yue " +0x95b3: "Chan " +0x95b4: "Qu " +0x95b5: "Lin " +0x95b6: "Chang " +0x95b7: "Shai " +0x95b8: "Kun " +0x95b9: "Yan " +0x95ba: "Min " +0x95bb: "Yan " +0x95bc: "E " +0x95bd: "Hun " +0x95be: "Yu " +0x95bf: "Wen " +0x95c0: "Xiang " +0x95c1: "Bao " +0x95c2: "Xiang " +0x95c3: "Qu " +0x95c4: "Yao " +0x95c5: "Wen " +0x95c6: "Ban " +0x95c7: "An " +0x95c8: "Wei " +0x95c9: "Yin " +0x95ca: "Kuo " +0x95cb: "Que " +0x95cc: "Lan " +0x95cd: "Du " +0x95ce: "[?] " +0x95cf: "Phwung " +0x95d0: "Tian " +0x95d1: "Nie " +0x95d2: "Ta " +0x95d3: "Kai " +0x95d4: "He " +0x95d5: "Que " +0x95d6: "Chuang " +0x95d7: "Guan " +0x95d8: "Dou " +0x95d9: "Qi " +0x95da: "Kui " +0x95db: "Tang " +0x95dc: "Guan " +0x95dd: "Piao " +0x95de: "Kan " +0x95df: "Xi " +0x95e0: "Hui " +0x95e1: "Chan " +0x95e2: "Pi " +0x95e3: "Dang " +0x95e4: "Huan " +0x95e5: "Ta " +0x95e6: "Wen " +0x95e7: "[?] " +0x95e8: "Men " +0x95e9: "Shuan " +0x95ea: "Shan " +0x95eb: "Yan " +0x95ec: "Han " +0x95ed: "Bi " +0x95ee: "Wen " +0x95ef: "Chuang " +0x95f0: "Run " +0x95f1: "Wei " +0x95f2: "Xian " +0x95f3: "Hong " +0x95f4: "Jian " +0x95f5: "Min " +0x95f6: "Kang " +0x95f7: "Men " +0x95f8: "Zha " +0x95f9: "Nao " +0x95fa: "Gui " +0x95fb: "Wen " +0x95fc: "Ta " +0x95fd: "Min " +0x95fe: "Lu " +0x95ff: "Kai " +/* x096 */ +0x9600: "Fa " +0x9601: "Ge " +0x9602: "He " +0x9603: "Kun " +0x9604: "Jiu " +0x9605: "Yue " +0x9606: "Lang " +0x9607: "Du " +0x9608: "Yu " +0x9609: "Yan " +0x960a: "Chang " +0x960b: "Xi " +0x960c: "Wen " +0x960d: "Hun " +0x960e: "Yan " +0x960f: "E " +0x9610: "Chan " +0x9611: "Lan " +0x9612: "Qu " +0x9613: "Hui " +0x9614: "Kuo " +0x9615: "Que " +0x9616: "Ge " +0x9617: "Tian " +0x9618: "Ta " +0x9619: "Que " +0x961a: "Kan " +0x961b: "Huan " +0x961c: "Fu " +0x961d: "Fu " +0x961e: "Le " +0x961f: "Dui " +0x9620: "Xin " +0x9621: "Qian " +0x9622: "Wu " +0x9623: "Yi " +0x9624: "Tuo " +0x9625: "Yin " +0x9626: "Yang " +0x9627: "Dou " +0x9628: "E " +0x9629: "Sheng " +0x962a: "Ban " +0x962b: "Pei " +0x962c: "Keng " +0x962d: "Yun " +0x962e: "Ruan " +0x962f: "Zhi " +0x9630: "Pi " +0x9631: "Jing " +0x9632: "Fang " +0x9633: "Yang " +0x9634: "Yin " +0x9635: "Zhen " +0x9636: "Jie " +0x9637: "Cheng " +0x9638: "E " +0x9639: "Qu " +0x963a: "Di " +0x963b: "Zu " +0x963c: "Zuo " +0x963d: "Dian " +0x963e: "Ling " +0x963f: "A " +0x9640: "Tuo " +0x9641: "Tuo " +0x9642: "Po " +0x9643: "Bing " +0x9644: "Fu " +0x9645: "Ji " +0x9646: "Lu " +0x9647: "Long " +0x9648: "Chen " +0x9649: "Xing " +0x964a: "Duo " +0x964b: "Lou " +0x964c: "Mo " +0x964d: "Jiang " +0x964e: "Shu " +0x964f: "Duo " +0x9650: "Xian " +0x9651: "Er " +0x9652: "Gui " +0x9653: "Yu " +0x9654: "Gai " +0x9655: "Shan " +0x9656: "Xun " +0x9657: "Qiao " +0x9658: "Xing " +0x9659: "Chun " +0x965a: "Fu " +0x965b: "Bi " +0x965c: "Xia " +0x965d: "Shan " +0x965e: "Sheng " +0x965f: "Zhi " +0x9660: "Pu " +0x9661: "Dou " +0x9662: "Yuan " +0x9663: "Zhen " +0x9664: "Chu " +0x9665: "Xian " +0x9666: "Tou " +0x9667: "Nie " +0x9668: "Yun " +0x9669: "Xian " +0x966a: "Pei " +0x966b: "Pei " +0x966c: "Zou " +0x966d: "Yi " +0x966e: "Dui " +0x966f: "Lun " +0x9670: "Yin " +0x9671: "Ju " +0x9672: "Chui " +0x9673: "Chen " +0x9674: "Pi " +0x9675: "Ling " +0x9676: "Tao " +0x9677: "Xian " +0x9678: "Lu " +0x9679: "Sheng " +0x967a: "Xian " +0x967b: "Yin " +0x967c: "Zhu " +0x967d: "Yang " +0x967e: "Reng " +0x967f: "Shan " +0x9680: "Chong " +0x9681: "Yan " +0x9682: "Yin " +0x9683: "Yu " +0x9684: "Ti " +0x9685: "Yu " +0x9686: "Long " +0x9687: "Wei " +0x9688: "Wei " +0x9689: "Nie " +0x968a: "Dui " +0x968b: "Sui " +0x968c: "An " +0x968d: "Huang " +0x968e: "Jie " +0x968f: "Sui " +0x9690: "Yin " +0x9691: "Gai " +0x9692: "Yan " +0x9693: "Hui " +0x9694: "Ge " +0x9695: "Yun " +0x9696: "Wu " +0x9697: "Wei " +0x9698: "Ai " +0x9699: "Xi " +0x969a: "Tang " +0x969b: "Ji " +0x969c: "Zhang " +0x969d: "Dao " +0x969e: "Ao " +0x969f: "Xi " +0x96a0: "Yin " +0x96a1: "[?] " +0x96a2: "Rao " +0x96a3: "Lin " +0x96a4: "Tui " +0x96a5: "Deng " +0x96a6: "Pi " +0x96a7: "Sui " +0x96a8: "Sui " +0x96a9: "Yu " +0x96aa: "Xian " +0x96ab: "Fen " +0x96ac: "Ni " +0x96ad: "Er " +0x96ae: "Ji " +0x96af: "Dao " +0x96b0: "Xi " +0x96b1: "Yin " +0x96b2: "E " +0x96b3: "Hui " +0x96b4: "Long " +0x96b5: "Xi " +0x96b6: "Li " +0x96b7: "Li " +0x96b8: "Li " +0x96b9: "Zhui " +0x96ba: "He " +0x96bb: "Zhi " +0x96bc: "Zhun " +0x96bd: "Jun " +0x96be: "Nan " +0x96bf: "Yi " +0x96c0: "Que " +0x96c1: "Yan " +0x96c2: "Qian " +0x96c3: "Ya " +0x96c4: "Xiong " +0x96c5: "Ya " +0x96c6: "Ji " +0x96c7: "Gu " +0x96c8: "Huan " +0x96c9: "Zhi " +0x96ca: "Gou " +0x96cb: "Jun " +0x96cc: "Ci " +0x96cd: "Yong " +0x96ce: "Ju " +0x96cf: "Chu " +0x96d0: "Hu " +0x96d1: "Za " +0x96d2: "Luo " +0x96d3: "Yu " +0x96d4: "Chou " +0x96d5: "Diao " +0x96d6: "Sui " +0x96d7: "Han " +0x96d8: "Huo " +0x96d9: "Shuang " +0x96da: "Guan " +0x96db: "Chu " +0x96dc: "Za " +0x96dd: "Yong " +0x96de: "Ji " +0x96df: "Xi " +0x96e0: "Chou " +0x96e1: "Liu " +0x96e2: "Li " +0x96e3: "Nan " +0x96e4: "Xue " +0x96e5: "Za " +0x96e6: "Ji " +0x96e7: "Ji " +0x96e8: "Yu " +0x96e9: "Yu " +0x96ea: "Xue " +0x96eb: "Na " +0x96ec: "Fou " +0x96ed: "Se " +0x96ee: "Mu " +0x96ef: "Wen " +0x96f0: "Fen " +0x96f1: "Pang " +0x96f2: "Yun " +0x96f3: "Li " +0x96f4: "Li " +0x96f5: "Ang " +0x96f6: "Ling " +0x96f7: "Lei " +0x96f8: "An " +0x96f9: "Bao " +0x96fa: "Meng " +0x96fb: "Dian " +0x96fc: "Dang " +0x96fd: "Xing " +0x96fe: "Wu " +0x96ff: "Zhao " +/* x097 */ +0x9700: "Xu " +0x9701: "Ji " +0x9702: "Mu " +0x9703: "Chen " +0x9704: "Xiao " +0x9705: "Zha " +0x9706: "Ting " +0x9707: "Zhen " +0x9708: "Pei " +0x9709: "Mei " +0x970a: "Ling " +0x970b: "Qi " +0x970c: "Chou " +0x970d: "Huo " +0x970e: "Sha " +0x970f: "Fei " +0x9710: "Weng " +0x9711: "Zhan " +0x9712: "Yin " +0x9713: "Ni " +0x9714: "Chou " +0x9715: "Tun " +0x9716: "Lin " +0x9717: "[?] " +0x9718: "Dong " +0x9719: "Ying " +0x971a: "Wu " +0x971b: "Ling " +0x971c: "Shuang " +0x971d: "Ling " +0x971e: "Xia " +0x971f: "Hong " +0x9720: "Yin " +0x9721: "Mo " +0x9722: "Mai " +0x9723: "Yun " +0x9724: "Liu " +0x9725: "Meng " +0x9726: "Bin " +0x9727: "Wu " +0x9728: "Wei " +0x9729: "Huo " +0x972a: "Yin " +0x972b: "Xi " +0x972c: "Yi " +0x972d: "Ai " +0x972e: "Dan " +0x972f: "Deng " +0x9730: "Xian " +0x9731: "Yu " +0x9732: "Lu " +0x9733: "Long " +0x9734: "Dai " +0x9735: "Ji " +0x9736: "Pang " +0x9737: "Yang " +0x9738: "Ba " +0x9739: "Pi " +0x973a: "Wei " +0x973b: "[?] " +0x973c: "Xi " +0x973d: "Ji " +0x973e: "Mai " +0x973f: "Meng " +0x9740: "Meng " +0x9741: "Lei " +0x9742: "Li " +0x9743: "Huo " +0x9744: "Ai " +0x9745: "Fei " +0x9746: "Dai " +0x9747: "Long " +0x9748: "Ling " +0x9749: "Ai " +0x974a: "Feng " +0x974b: "Li " +0x974c: "Bao " +0x974d: "[?] " +0x974e: "He " +0x974f: "He " +0x9750: "Bing " +0x9751: "Qing " +0x9752: "Qing " +0x9753: "Jing " +0x9754: "Tian " +0x9755: "Zhen " +0x9756: "Jing " +0x9757: "Cheng " +0x9758: "Qing " +0x9759: "Jing " +0x975a: "Jing " +0x975b: "Dian " +0x975c: "Jing " +0x975d: "Tian " +0x975e: "Fei " +0x975f: "Fei " +0x9760: "Kao " +0x9761: "Mi " +0x9762: "Mian " +0x9763: "Mian " +0x9764: "Pao " +0x9765: "Ye " +0x9766: "Tian " +0x9767: "Hui " +0x9768: "Ye " +0x9769: "Ge " +0x976a: "Ding " +0x976b: "Cha " +0x976c: "Jian " +0x976d: "Ren " +0x976e: "Di " +0x976f: "Du " +0x9770: "Wu " +0x9771: "Ren " +0x9772: "Qin " +0x9773: "Jin " +0x9774: "Xue " +0x9775: "Niu " +0x9776: "Ba " +0x9777: "Yin " +0x9778: "Sa " +0x9779: "Na " +0x977a: "Mo " +0x977b: "Zu " +0x977c: "Da " +0x977d: "Ban " +0x977e: "Yi " +0x977f: "Yao " +0x9780: "Tao " +0x9781: "Tuo " +0x9782: "Jia " +0x9783: "Hong " +0x9784: "Pao " +0x9785: "Yang " +0x9786: "Tomo " +0x9787: "Yin " +0x9788: "Jia " +0x9789: "Tao " +0x978a: "Ji " +0x978b: "Xie " +0x978c: "An " +0x978d: "An " +0x978e: "Hen " +0x978f: "Gong " +0x9790: "Kohaze " +0x9791: "Da " +0x9792: "Qiao " +0x9793: "Ting " +0x9794: "Wan " +0x9795: "Ying " +0x9796: "Sui " +0x9797: "Tiao " +0x9798: "Qiao " +0x9799: "Xuan " +0x979a: "Kong " +0x979b: "Beng " +0x979c: "Ta " +0x979d: "Zhang " +0x979e: "Bing " +0x979f: "Kuo " +0x97a0: "Ju " +0x97a1: "La " +0x97a2: "Xie " +0x97a3: "Rou " +0x97a4: "Bang " +0x97a5: "Yi " +0x97a6: "Qiu " +0x97a7: "Qiu " +0x97a8: "He " +0x97a9: "Xiao " +0x97aa: "Mu " +0x97ab: "Ju " +0x97ac: "Jian " +0x97ad: "Bian " +0x97ae: "Di " +0x97af: "Jian " +0x97b0: "On " +0x97b1: "Tao " +0x97b2: "Gou " +0x97b3: "Ta " +0x97b4: "Bei " +0x97b5: "Xie " +0x97b6: "Pan " +0x97b7: "Ge " +0x97b8: "Bi " +0x97b9: "Kuo " +0x97ba: "Tang " +0x97bb: "Lou " +0x97bc: "Gui " +0x97bd: "Qiao " +0x97be: "Xue " +0x97bf: "Ji " +0x97c0: "Jian " +0x97c1: "Jiang " +0x97c2: "Chan " +0x97c3: "Da " +0x97c4: "Huo " +0x97c5: "Xian " +0x97c6: "Qian " +0x97c7: "Du " +0x97c8: "Wa " +0x97c9: "Jian " +0x97ca: "Lan " +0x97cb: "Wei " +0x97cc: "Ren " +0x97cd: "Fu " +0x97ce: "Mei " +0x97cf: "Juan " +0x97d0: "Ge " +0x97d1: "Wei " +0x97d2: "Qiao " +0x97d3: "Han " +0x97d4: "Chang " +0x97d5: "[?] " +0x97d6: "Rou " +0x97d7: "Xun " +0x97d8: "She " +0x97d9: "Wei " +0x97da: "Ge " +0x97db: "Bei " +0x97dc: "Tao " +0x97dd: "Gou " +0x97de: "Yun " +0x97df: "[?] " +0x97e0: "Bi " +0x97e1: "Wei " +0x97e2: "Hui " +0x97e3: "Du " +0x97e4: "Wa " +0x97e5: "Du " +0x97e6: "Wei " +0x97e7: "Ren " +0x97e8: "Fu " +0x97e9: "Han " +0x97ea: "Wei " +0x97eb: "Yun " +0x97ec: "Tao " +0x97ed: "Jiu " +0x97ee: "Jiu " +0x97ef: "Xian " +0x97f0: "Xie " +0x97f1: "Xian " +0x97f2: "Ji " +0x97f3: "Yin " +0x97f4: "Za " +0x97f5: "Yun " +0x97f6: "Shao " +0x97f7: "Le " +0x97f8: "Peng " +0x97f9: "Heng " +0x97fa: "Ying " +0x97fb: "Yun " +0x97fc: "Peng " +0x97fd: "Yin " +0x97fe: "Yin " +0x97ff: "Xiang " +/* x098 */ +0x9800: "Hu " +0x9801: "Ye " +0x9802: "Ding " +0x9803: "Qing " +0x9804: "Pan " +0x9805: "Xiang " +0x9806: "Shun " +0x9807: "Han " +0x9808: "Xu " +0x9809: "Yi " +0x980a: "Xu " +0x980b: "Gu " +0x980c: "Song " +0x980d: "Kui " +0x980e: "Qi " +0x980f: "Hang " +0x9810: "Yu " +0x9811: "Wan " +0x9812: "Ban " +0x9813: "Dun " +0x9814: "Di " +0x9815: "Dan " +0x9816: "Pan " +0x9817: "Po " +0x9818: "Ling " +0x9819: "Ce " +0x981a: "Jing " +0x981b: "Lei " +0x981c: "He " +0x981d: "Qiao " +0x981e: "E " +0x981f: "E " +0x9820: "Wei " +0x9821: "Jie " +0x9822: "Gua " +0x9823: "Shen " +0x9824: "Yi " +0x9825: "Shen " +0x9826: "Hai " +0x9827: "Dui " +0x9828: "Pian " +0x9829: "Ping " +0x982a: "Lei " +0x982b: "Fu " +0x982c: "Jia " +0x982d: "Tou " +0x982e: "Hui " +0x982f: "Kui " +0x9830: "Jia " +0x9831: "Le " +0x9832: "Tian " +0x9833: "Cheng " +0x9834: "Ying " +0x9835: "Jun " +0x9836: "Hu " +0x9837: "Han " +0x9838: "Jing " +0x9839: "Tui " +0x983a: "Tui " +0x983b: "Pin " +0x983c: "Lai " +0x983d: "Tui " +0x983e: "Zi " +0x983f: "Zi " +0x9840: "Chui " +0x9841: "Ding " +0x9842: "Lai " +0x9843: "Yan " +0x9844: "Han " +0x9845: "Jian " +0x9846: "Ke " +0x9847: "Cui " +0x9848: "Jiong " +0x9849: "Qin " +0x984a: "Yi " +0x984b: "Sai " +0x984c: "Ti " +0x984d: "E " +0x984e: "E " +0x984f: "Yan " +0x9850: "Hun " +0x9851: "Kan " +0x9852: "Yong " +0x9853: "Zhuan " +0x9854: "Yan " +0x9855: "Xian " +0x9856: "Xin " +0x9857: "Yi " +0x9858: "Yuan " +0x9859: "Sang " +0x985a: "Dian " +0x985b: "Dian " +0x985c: "Jiang " +0x985d: "Ku " +0x985e: "Lei " +0x985f: "Liao " +0x9860: "Piao " +0x9861: "Yi " +0x9862: "Man " +0x9863: "Qi " +0x9864: "Rao " +0x9865: "Hao " +0x9866: "Qiao " +0x9867: "Gu " +0x9868: "Xun " +0x9869: "Qian " +0x986a: "Hui " +0x986b: "Zhan " +0x986c: "Ru " +0x986d: "Hong " +0x986e: "Bin " +0x986f: "Xian " +0x9870: "Pin " +0x9871: "Lu " +0x9872: "Lan " +0x9873: "Nie " +0x9874: "Quan " +0x9875: "Ye " +0x9876: "Ding " +0x9877: "Qing " +0x9878: "Han " +0x9879: "Xiang " +0x987a: "Shun " +0x987b: "Xu " +0x987c: "Xu " +0x987d: "Wan " +0x987e: "Gu " +0x987f: "Dun " +0x9880: "Qi " +0x9881: "Ban " +0x9882: "Song " +0x9883: "Hang " +0x9884: "Yu " +0x9885: "Lu " +0x9886: "Ling " +0x9887: "Po " +0x9888: "Jing " +0x9889: "Jie " +0x988a: "Jia " +0x988b: "Tian " +0x988c: "Han " +0x988d: "Ying " +0x988e: "Jiong " +0x988f: "Hai " +0x9890: "Yi " +0x9891: "Pin " +0x9892: "Hui " +0x9893: "Tui " +0x9894: "Han " +0x9895: "Ying " +0x9896: "Ying " +0x9897: "Ke " +0x9898: "Ti " +0x9899: "Yong " +0x989a: "E " +0x989b: "Zhuan " +0x989c: "Yan " +0x989d: "E " +0x989e: "Nie " +0x989f: "Man " +0x98a0: "Dian " +0x98a1: "Sang " +0x98a2: "Hao " +0x98a3: "Lei " +0x98a4: "Zhan " +0x98a5: "Ru " +0x98a6: "Pin " +0x98a7: "Quan " +0x98a8: "Feng " +0x98a9: "Biao " +0x98aa: "Oroshi " +0x98ab: "Fu " +0x98ac: "Xia " +0x98ad: "Zhan " +0x98ae: "Biao " +0x98af: "Sa " +0x98b0: "Ba " +0x98b1: "Tai " +0x98b2: "Lie " +0x98b3: "Gua " +0x98b4: "Xuan " +0x98b5: "Shao " +0x98b6: "Ju " +0x98b7: "Bi " +0x98b8: "Si " +0x98b9: "Wei " +0x98ba: "Yang " +0x98bb: "Yao " +0x98bc: "Sou " +0x98bd: "Kai " +0x98be: "Sao " +0x98bf: "Fan " +0x98c0: "Liu " +0x98c1: "Xi " +0x98c2: "Liao " +0x98c3: "Piao " +0x98c4: "Piao " +0x98c5: "Liu " +0x98c6: "Biao " +0x98c7: "Biao " +0x98c8: "Biao " +0x98c9: "Liao " +0x98ca: "[?] " +0x98cb: "Se " +0x98cc: "Feng " +0x98cd: "Biao " +0x98ce: "Feng " +0x98cf: "Yang " +0x98d0: "Zhan " +0x98d1: "Biao " +0x98d2: "Sa " +0x98d3: "Ju " +0x98d4: "Si " +0x98d5: "Sou " +0x98d6: "Yao " +0x98d7: "Liu " +0x98d8: "Piao " +0x98d9: "Biao " +0x98da: "Biao " +0x98db: "Fei " +0x98dc: "Fan " +0x98dd: "Fei " +0x98de: "Fei " +0x98df: "Shi " +0x98e0: "Shi " +0x98e1: "Can " +0x98e2: "Ji " +0x98e3: "Ding " +0x98e4: "Si " +0x98e5: "Tuo " +0x98e6: "Zhan " +0x98e7: "Sun " +0x98e8: "Xiang " +0x98e9: "Tun " +0x98ea: "Ren " +0x98eb: "Yu " +0x98ec: "Juan " +0x98ed: "Chi " +0x98ee: "Yin " +0x98ef: "Fan " +0x98f0: "Fan " +0x98f1: "Sun " +0x98f2: "Yin " +0x98f3: "Zhu " +0x98f4: "Yi " +0x98f5: "Zhai " +0x98f6: "Bi " +0x98f7: "Jie " +0x98f8: "Tao " +0x98f9: "Liu " +0x98fa: "Ci " +0x98fb: "Tie " +0x98fc: "Si " +0x98fd: "Bao " +0x98fe: "Shi " +0x98ff: "Duo " +/* x099 */ +0x9900: "Hai " +0x9901: "Ren " +0x9902: "Tian " +0x9903: "Jiao " +0x9904: "Jia " +0x9905: "Bing " +0x9906: "Yao " +0x9907: "Tong " +0x9908: "Ci " +0x9909: "Xiang " +0x990a: "Yang " +0x990b: "Yang " +0x990c: "Er " +0x990d: "Yan " +0x990e: "Le " +0x990f: "Yi " +0x9910: "Can " +0x9911: "Bo " +0x9912: "Nei " +0x9913: "E " +0x9914: "Bu " +0x9915: "Jun " +0x9916: "Dou " +0x9917: "Su " +0x9918: "Yu " +0x9919: "Shi " +0x991a: "Yao " +0x991b: "Hun " +0x991c: "Guo " +0x991d: "Shi " +0x991e: "Jian " +0x991f: "Zhui " +0x9920: "Bing " +0x9921: "Xian " +0x9922: "Bu " +0x9923: "Ye " +0x9924: "Tan " +0x9925: "Fei " +0x9926: "Zhang " +0x9927: "Wei " +0x9928: "Guan " +0x9929: "E " +0x992a: "Nuan " +0x992b: "Hun " +0x992c: "Hu " +0x992d: "Huang " +0x992e: "Tie " +0x992f: "Hui " +0x9930: "Jian " +0x9931: "Hou " +0x9932: "He " +0x9933: "Xing " +0x9934: "Fen " +0x9935: "Wei " +0x9936: "Gu " +0x9937: "Cha " +0x9938: "Song " +0x9939: "Tang " +0x993a: "Bo " +0x993b: "Gao " +0x993c: "Xi " +0x993d: "Kui " +0x993e: "Liu " +0x993f: "Sou " +0x9940: "Tao " +0x9941: "Ye " +0x9942: "Yun " +0x9943: "Mo " +0x9944: "Tang " +0x9945: "Man " +0x9946: "Bi " +0x9947: "Yu " +0x9948: "Xiu " +0x9949: "Jin " +0x994a: "San " +0x994b: "Kui " +0x994c: "Zhuan " +0x994d: "Shan " +0x994e: "Chi " +0x994f: "Dan " +0x9950: "Yi " +0x9951: "Ji " +0x9952: "Rao " +0x9953: "Cheng " +0x9954: "Yong " +0x9955: "Tao " +0x9956: "Hui " +0x9957: "Xiang " +0x9958: "Zhan " +0x9959: "Fen " +0x995a: "Hai " +0x995b: "Meng " +0x995c: "Yan " +0x995d: "Mo " +0x995e: "Chan " +0x995f: "Xiang " +0x9960: "Luo " +0x9961: "Zuan " +0x9962: "Nang " +0x9963: "Shi " +0x9964: "Ding " +0x9965: "Ji " +0x9966: "Tuo " +0x9967: "Xing " +0x9968: "Tun " +0x9969: "Xi " +0x996a: "Ren " +0x996b: "Yu " +0x996c: "Chi " +0x996d: "Fan " +0x996e: "Yin " +0x996f: "Jian " +0x9970: "Shi " +0x9971: "Bao " +0x9972: "Si " +0x9973: "Duo " +0x9974: "Yi " +0x9975: "Er " +0x9976: "Rao " +0x9977: "Xiang " +0x9978: "Jia " +0x9979: "Le " +0x997a: "Jiao " +0x997b: "Yi " +0x997c: "Bing " +0x997d: "Bo " +0x997e: "Dou " +0x997f: "E " +0x9980: "Yu " +0x9981: "Nei " +0x9982: "Jun " +0x9983: "Guo " +0x9984: "Hun " +0x9985: "Xian " +0x9986: "Guan " +0x9987: "Cha " +0x9988: "Kui " +0x9989: "Gu " +0x998a: "Sou " +0x998b: "Chan " +0x998c: "Ye " +0x998d: "Mo " +0x998e: "Bo " +0x998f: "Liu " +0x9990: "Xiu " +0x9991: "Jin " +0x9992: "Man " +0x9993: "San " +0x9994: "Zhuan " +0x9995: "Nang " +0x9996: "Shou " +0x9997: "Kui " +0x9998: "Guo " +0x9999: "Xiang " +0x999a: "Fen " +0x999b: "Ba " +0x999c: "Ni " +0x999d: "Bi " +0x999e: "Bo " +0x999f: "Tu " +0x99a0: "Han " +0x99a1: "Fei " +0x99a2: "Jian " +0x99a3: "An " +0x99a4: "Ai " +0x99a5: "Fu " +0x99a6: "Xian " +0x99a7: "Wen " +0x99a8: "Xin " +0x99a9: "Fen " +0x99aa: "Bin " +0x99ab: "Xing " +0x99ac: "Ma " +0x99ad: "Yu " +0x99ae: "Feng " +0x99af: "Han " +0x99b0: "Di " +0x99b1: "Tuo " +0x99b2: "Tuo " +0x99b3: "Chi " +0x99b4: "Xun " +0x99b5: "Zhu " +0x99b6: "Zhi " +0x99b7: "Pei " +0x99b8: "Xin " +0x99b9: "Ri " +0x99ba: "Sa " +0x99bb: "Yin " +0x99bc: "Wen " +0x99bd: "Zhi " +0x99be: "Dan " +0x99bf: "Lu " +0x99c0: "You " +0x99c1: "Bo " +0x99c2: "Bao " +0x99c3: "Kuai " +0x99c4: "Tuo " +0x99c5: "Yi " +0x99c6: "Qu " +0x99c7: "[?] " +0x99c8: "Qu " +0x99c9: "Jiong " +0x99ca: "Bo " +0x99cb: "Zhao " +0x99cc: "Yuan " +0x99cd: "Peng " +0x99ce: "Zhou " +0x99cf: "Ju " +0x99d0: "Zhu " +0x99d1: "Nu " +0x99d2: "Ju " +0x99d3: "Pi " +0x99d4: "Zang " +0x99d5: "Jia " +0x99d6: "Ling " +0x99d7: "Zhen " +0x99d8: "Tai " +0x99d9: "Fu " +0x99da: "Yang " +0x99db: "Shi " +0x99dc: "Bi " +0x99dd: "Tuo " +0x99de: "Tuo " +0x99df: "Si " +0x99e0: "Liu " +0x99e1: "Ma " +0x99e2: "Pian " +0x99e3: "Tao " +0x99e4: "Zhi " +0x99e5: "Rong " +0x99e6: "Teng " +0x99e7: "Dong " +0x99e8: "Xun " +0x99e9: "Quan " +0x99ea: "Shen " +0x99eb: "Jiong " +0x99ec: "Er " +0x99ed: "Hai " +0x99ee: "Bo " +0x99ef: "Zhu " +0x99f0: "Yin " +0x99f1: "Luo " +0x99f2: "Shuu " +0x99f3: "Dan " +0x99f4: "Xie " +0x99f5: "Liu " +0x99f6: "Ju " +0x99f7: "Song " +0x99f8: "Qin " +0x99f9: "Mang " +0x99fa: "Liang " +0x99fb: "Han " +0x99fc: "Tu " +0x99fd: "Xuan " +0x99fe: "Tui " +0x99ff: "Jun " +/* x09a */ +0x9a00: "E " +0x9a01: "Cheng " +0x9a02: "Xin " +0x9a03: "Ai " +0x9a04: "Lu " +0x9a05: "Zhui " +0x9a06: "Zhou " +0x9a07: "She " +0x9a08: "Pian " +0x9a09: "Kun " +0x9a0a: "Tao " +0x9a0b: "Lai " +0x9a0c: "Zong " +0x9a0d: "Ke " +0x9a0e: "Qi " +0x9a0f: "Qi " +0x9a10: "Yan " +0x9a11: "Fei " +0x9a12: "Sao " +0x9a13: "Yan " +0x9a14: "Jie " +0x9a15: "Yao " +0x9a16: "Wu " +0x9a17: "Pian " +0x9a18: "Cong " +0x9a19: "Pian " +0x9a1a: "Qian " +0x9a1b: "Fei " +0x9a1c: "Huang " +0x9a1d: "Jian " +0x9a1e: "Huo " +0x9a1f: "Yu " +0x9a20: "Ti " +0x9a21: "Quan " +0x9a22: "Xia " +0x9a23: "Zong " +0x9a24: "Kui " +0x9a25: "Rou " +0x9a26: "Si " +0x9a27: "Gua " +0x9a28: "Tuo " +0x9a29: "Kui " +0x9a2a: "Sou " +0x9a2b: "Qian " +0x9a2c: "Cheng " +0x9a2d: "Zhi " +0x9a2e: "Liu " +0x9a2f: "Pang " +0x9a30: "Teng " +0x9a31: "Xi " +0x9a32: "Cao " +0x9a33: "Du " +0x9a34: "Yan " +0x9a35: "Yuan " +0x9a36: "Zou " +0x9a37: "Sao " +0x9a38: "Shan " +0x9a39: "Li " +0x9a3a: "Zhi " +0x9a3b: "Shuang " +0x9a3c: "Lu " +0x9a3d: "Xi " +0x9a3e: "Luo " +0x9a3f: "Zhang " +0x9a40: "Mo " +0x9a41: "Ao " +0x9a42: "Can " +0x9a43: "Piao " +0x9a44: "Cong " +0x9a45: "Qu " +0x9a46: "Bi " +0x9a47: "Zhi " +0x9a48: "Yu " +0x9a49: "Xu " +0x9a4a: "Hua " +0x9a4b: "Bo " +0x9a4c: "Su " +0x9a4d: "Xiao " +0x9a4e: "Lin " +0x9a4f: "Chan " +0x9a50: "Dun " +0x9a51: "Liu " +0x9a52: "Tuo " +0x9a53: "Zeng " +0x9a54: "Tan " +0x9a55: "Jiao " +0x9a56: "Tie " +0x9a57: "Yan " +0x9a58: "Luo " +0x9a59: "Zhan " +0x9a5a: "Jing " +0x9a5b: "Yi " +0x9a5c: "Ye " +0x9a5d: "Tuo " +0x9a5e: "Bin " +0x9a5f: "Zou " +0x9a60: "Yan " +0x9a61: "Peng " +0x9a62: "Lu " +0x9a63: "Teng " +0x9a64: "Xiang " +0x9a65: "Ji " +0x9a66: "Shuang " +0x9a67: "Ju " +0x9a68: "Xi " +0x9a69: "Huan " +0x9a6a: "Li " +0x9a6b: "Biao " +0x9a6c: "Ma " +0x9a6d: "Yu " +0x9a6e: "Tuo " +0x9a6f: "Xun " +0x9a70: "Chi " +0x9a71: "Qu " +0x9a72: "Ri " +0x9a73: "Bo " +0x9a74: "Lu " +0x9a75: "Zang " +0x9a76: "Shi " +0x9a77: "Si " +0x9a78: "Fu " +0x9a79: "Ju " +0x9a7a: "Zou " +0x9a7b: "Zhu " +0x9a7c: "Tuo " +0x9a7d: "Nu " +0x9a7e: "Jia " +0x9a7f: "Yi " +0x9a80: "Tai " +0x9a81: "Xiao " +0x9a82: "Ma " +0x9a83: "Yin " +0x9a84: "Jiao " +0x9a85: "Hua " +0x9a86: "Luo " +0x9a87: "Hai " +0x9a88: "Pian " +0x9a89: "Biao " +0x9a8a: "Li " +0x9a8b: "Cheng " +0x9a8c: "Yan " +0x9a8d: "Xin " +0x9a8e: "Qin " +0x9a8f: "Jun " +0x9a90: "Qi " +0x9a91: "Qi " +0x9a92: "Ke " +0x9a93: "Zhui " +0x9a94: "Zong " +0x9a95: "Su " +0x9a96: "Can " +0x9a97: "Pian " +0x9a98: "Zhi " +0x9a99: "Kui " +0x9a9a: "Sao " +0x9a9b: "Wu " +0x9a9c: "Ao " +0x9a9d: "Liu " +0x9a9e: "Qian " +0x9a9f: "Shan " +0x9aa0: "Piao " +0x9aa1: "Luo " +0x9aa2: "Cong " +0x9aa3: "Chan " +0x9aa4: "Zou " +0x9aa5: "Ji " +0x9aa6: "Shuang " +0x9aa7: "Xiang " +0x9aa8: "Gu " +0x9aa9: "Wei " +0x9aaa: "Wei " +0x9aab: "Wei " +0x9aac: "Yu " +0x9aad: "Gan " +0x9aae: "Yi " +0x9aaf: "Ang " +0x9ab0: "Tou " +0x9ab1: "Xie " +0x9ab2: "Bao " +0x9ab3: "Bi " +0x9ab4: "Chi " +0x9ab5: "Ti " +0x9ab6: "Di " +0x9ab7: "Ku " +0x9ab8: "Hai " +0x9ab9: "Qiao " +0x9aba: "Gou " +0x9abb: "Kua " +0x9abc: "Ge " +0x9abd: "Tui " +0x9abe: "Geng " +0x9abf: "Pian " +0x9ac0: "Bi " +0x9ac1: "Ke " +0x9ac2: "Ka " +0x9ac3: "Yu " +0x9ac4: "Sui " +0x9ac5: "Lou " +0x9ac6: "Bo " +0x9ac7: "Xiao " +0x9ac8: "Pang " +0x9ac9: "Bo " +0x9aca: "Ci " +0x9acb: "Kuan " +0x9acc: "Bin " +0x9acd: "Mo " +0x9ace: "Liao " +0x9acf: "Lou " +0x9ad0: "Nao " +0x9ad1: "Du " +0x9ad2: "Zang " +0x9ad3: "Sui " +0x9ad4: "Ti " +0x9ad5: "Bin " +0x9ad6: "Kuan " +0x9ad7: "Lu " +0x9ad8: "Gao " +0x9ad9: "Gao " +0x9ada: "Qiao " +0x9adb: "Kao " +0x9adc: "Qiao " +0x9add: "Lao " +0x9ade: "Zao " +0x9adf: "Biao " +0x9ae0: "Kun " +0x9ae1: "Kun " +0x9ae2: "Ti " +0x9ae3: "Fang " +0x9ae4: "Xiu " +0x9ae5: "Ran " +0x9ae6: "Mao " +0x9ae7: "Dan " +0x9ae8: "Kun " +0x9ae9: "Bin " +0x9aea: "Fa " +0x9aeb: "Tiao " +0x9aec: "Peng " +0x9aed: "Zi " +0x9aee: "Fa " +0x9aef: "Ran " +0x9af0: "Ti " +0x9af1: "Pao " +0x9af2: "Pi " +0x9af3: "Mao " +0x9af4: "Fu " +0x9af5: "Er " +0x9af6: "Rong " +0x9af7: "Qu " +0x9af8: "Gong " +0x9af9: "Xiu " +0x9afa: "Gua " +0x9afb: "Ji " +0x9afc: "Peng " +0x9afd: "Zhua " +0x9afe: "Shao " +0x9aff: "Sha " +/* x09b */ +0x9b00: "Ti " +0x9b01: "Li " +0x9b02: "Bin " +0x9b03: "Zong " +0x9b04: "Ti " +0x9b05: "Peng " +0x9b06: "Song " +0x9b07: "Zheng " +0x9b08: "Quan " +0x9b09: "Zong " +0x9b0a: "Shun " +0x9b0b: "Jian " +0x9b0c: "Duo " +0x9b0d: "Hu " +0x9b0e: "La " +0x9b0f: "Jiu " +0x9b10: "Qi " +0x9b11: "Lian " +0x9b12: "Zhen " +0x9b13: "Bin " +0x9b14: "Peng " +0x9b15: "Mo " +0x9b16: "San " +0x9b17: "Man " +0x9b18: "Man " +0x9b19: "Seng " +0x9b1a: "Xu " +0x9b1b: "Lie " +0x9b1c: "Qian " +0x9b1d: "Qian " +0x9b1e: "Nong " +0x9b1f: "Huan " +0x9b20: "Kuai " +0x9b21: "Ning " +0x9b22: "Bin " +0x9b23: "Lie " +0x9b24: "Rang " +0x9b25: "Dou " +0x9b26: "Dou " +0x9b27: "Nao " +0x9b28: "Hong " +0x9b29: "Xi " +0x9b2a: "Dou " +0x9b2b: "Han " +0x9b2c: "Dou " +0x9b2d: "Dou " +0x9b2e: "Jiu " +0x9b2f: "Chang " +0x9b30: "Yu " +0x9b31: "Yu " +0x9b32: "Li " +0x9b33: "Juan " +0x9b34: "Fu " +0x9b35: "Qian " +0x9b36: "Gui " +0x9b37: "Zong " +0x9b38: "Liu " +0x9b39: "Gui " +0x9b3a: "Shang " +0x9b3b: "Yu " +0x9b3c: "Gui " +0x9b3d: "Mei " +0x9b3e: "Ji " +0x9b3f: "Qi " +0x9b40: "Jie " +0x9b41: "Kui " +0x9b42: "Hun " +0x9b43: "Ba " +0x9b44: "Po " +0x9b45: "Mei " +0x9b46: "Xu " +0x9b47: "Yan " +0x9b48: "Xiao " +0x9b49: "Liang " +0x9b4a: "Yu " +0x9b4b: "Tui " +0x9b4c: "Qi " +0x9b4d: "Wang " +0x9b4e: "Liang " +0x9b4f: "Wei " +0x9b50: "Jian " +0x9b51: "Chi " +0x9b52: "Piao " +0x9b53: "Bi " +0x9b54: "Mo " +0x9b55: "Ji " +0x9b56: "Xu " +0x9b57: "Chou " +0x9b58: "Yan " +0x9b59: "Zhan " +0x9b5a: "Yu " +0x9b5b: "Dao " +0x9b5c: "Ren " +0x9b5d: "Ji " +0x9b5e: "Eri " +0x9b5f: "Gong " +0x9b60: "Tuo " +0x9b61: "Diao " +0x9b62: "Ji " +0x9b63: "Xu " +0x9b64: "E " +0x9b65: "E " +0x9b66: "Sha " +0x9b67: "Hang " +0x9b68: "Tun " +0x9b69: "Mo " +0x9b6a: "Jie " +0x9b6b: "Shen " +0x9b6c: "Fan " +0x9b6d: "Yuan " +0x9b6e: "Bi " +0x9b6f: "Lu " +0x9b70: "Wen " +0x9b71: "Hu " +0x9b72: "Lu " +0x9b73: "Za " +0x9b74: "Fang " +0x9b75: "Fen " +0x9b76: "Na " +0x9b77: "You " +0x9b78: "Namazu " +0x9b79: "Todo " +0x9b7a: "He " +0x9b7b: "Xia " +0x9b7c: "Qu " +0x9b7d: "Han " +0x9b7e: "Pi " +0x9b7f: "Ling " +0x9b80: "Tuo " +0x9b81: "Bo " +0x9b82: "Qiu " +0x9b83: "Ping " +0x9b84: "Fu " +0x9b85: "Bi " +0x9b86: "Ji " +0x9b87: "Wei " +0x9b88: "Ju " +0x9b89: "Diao " +0x9b8a: "Bo " +0x9b8b: "You " +0x9b8c: "Gun " +0x9b8d: "Pi " +0x9b8e: "Nian " +0x9b8f: "Xing " +0x9b90: "Tai " +0x9b91: "Bao " +0x9b92: "Fu " +0x9b93: "Zha " +0x9b94: "Ju " +0x9b95: "Gu " +0x9b96: "Kajika " +0x9b97: "Tong " +0x9b98: "[?] " +0x9b99: "Ta " +0x9b9a: "Jie " +0x9b9b: "Shu " +0x9b9c: "Hou " +0x9b9d: "Xiang " +0x9b9e: "Er " +0x9b9f: "An " +0x9ba0: "Wei " +0x9ba1: "Tiao " +0x9ba2: "Zhu " +0x9ba3: "Yin " +0x9ba4: "Lie " +0x9ba5: "Luo " +0x9ba6: "Tong " +0x9ba7: "Yi " +0x9ba8: "Qi " +0x9ba9: "Bing " +0x9baa: "Wei " +0x9bab: "Jiao " +0x9bac: "Bu " +0x9bad: "Gui " +0x9bae: "Xian " +0x9baf: "Ge " +0x9bb0: "Hui " +0x9bb1: "Bora " +0x9bb2: "Mate " +0x9bb3: "Kao " +0x9bb4: "Gori " +0x9bb5: "Duo " +0x9bb6: "Jun " +0x9bb7: "Ti " +0x9bb8: "Man " +0x9bb9: "Xiao " +0x9bba: "Za " +0x9bbb: "Sha " +0x9bbc: "Qin " +0x9bbd: "Yu " +0x9bbe: "Nei " +0x9bbf: "Zhe " +0x9bc0: "Gun " +0x9bc1: "Geng " +0x9bc2: "Su " +0x9bc3: "Wu " +0x9bc4: "Qiu " +0x9bc5: "Ting " +0x9bc6: "Fu " +0x9bc7: "Wan " +0x9bc8: "You " +0x9bc9: "Li " +0x9bca: "Sha " +0x9bcb: "Sha " +0x9bcc: "Gao " +0x9bcd: "Meng " +0x9bce: "Ugui " +0x9bcf: "Asari " +0x9bd0: "Subashiri " +0x9bd1: "Kazunoko " +0x9bd2: "Yong " +0x9bd3: "Ni " +0x9bd4: "Zi " +0x9bd5: "Qi " +0x9bd6: "Qing " +0x9bd7: "Xiang " +0x9bd8: "Nei " +0x9bd9: "Chun " +0x9bda: "Ji " +0x9bdb: "Diao " +0x9bdc: "Qie " +0x9bdd: "Gu " +0x9bde: "Zhou " +0x9bdf: "Dong " +0x9be0: "Lai " +0x9be1: "Fei " +0x9be2: "Ni " +0x9be3: "Yi " +0x9be4: "Kun " +0x9be5: "Lu " +0x9be6: "Jiu " +0x9be7: "Chang " +0x9be8: "Jing " +0x9be9: "Lun " +0x9bea: "Ling " +0x9beb: "Zou " +0x9bec: "Li " +0x9bed: "Meng " +0x9bee: "Zong " +0x9bef: "Zhi " +0x9bf0: "Nian " +0x9bf1: "Shachi " +0x9bf2: "Dojou " +0x9bf3: "Sukesou " +0x9bf4: "Shi " +0x9bf5: "Shen " +0x9bf6: "Hun " +0x9bf7: "Shi " +0x9bf8: "Hou " +0x9bf9: "Xing " +0x9bfa: "Zhu " +0x9bfb: "La " +0x9bfc: "Zong " +0x9bfd: "Ji " +0x9bfe: "Bian " +0x9bff: "Bian " +/* x09c */ +0x9c00: "Huan " +0x9c01: "Quan " +0x9c02: "Ze " +0x9c03: "Wei " +0x9c04: "Wei " +0x9c05: "Yu " +0x9c06: "Qun " +0x9c07: "Rou " +0x9c08: "Die " +0x9c09: "Huang " +0x9c0a: "Lian " +0x9c0b: "Yan " +0x9c0c: "Qiu " +0x9c0d: "Qiu " +0x9c0e: "Jian " +0x9c0f: "Bi " +0x9c10: "E " +0x9c11: "Yang " +0x9c12: "Fu " +0x9c13: "Sai " +0x9c14: "Jian " +0x9c15: "Xia " +0x9c16: "Tuo " +0x9c17: "Hu " +0x9c18: "Muroaji " +0x9c19: "Ruo " +0x9c1a: "Haraka " +0x9c1b: "Wen " +0x9c1c: "Jian " +0x9c1d: "Hao " +0x9c1e: "Wu " +0x9c1f: "Fang " +0x9c20: "Sao " +0x9c21: "Liu " +0x9c22: "Ma " +0x9c23: "Shi " +0x9c24: "Shi " +0x9c25: "Yin " +0x9c26: "Z " +0x9c27: "Teng " +0x9c28: "Ta " +0x9c29: "Yao " +0x9c2a: "Ge " +0x9c2b: "Rong " +0x9c2c: "Qian " +0x9c2d: "Qi " +0x9c2e: "Wen " +0x9c2f: "Ruo " +0x9c30: "Hatahata " +0x9c31: "Lian " +0x9c32: "Ao " +0x9c33: "Le " +0x9c34: "Hui " +0x9c35: "Min " +0x9c36: "Ji " +0x9c37: "Tiao " +0x9c38: "Qu " +0x9c39: "Jian " +0x9c3a: "Sao " +0x9c3b: "Man " +0x9c3c: "Xi " +0x9c3d: "Qiu " +0x9c3e: "Biao " +0x9c3f: "Ji " +0x9c40: "Ji " +0x9c41: "Zhu " +0x9c42: "Jiang " +0x9c43: "Qiu " +0x9c44: "Zhuan " +0x9c45: "Yong " +0x9c46: "Zhang " +0x9c47: "Kang " +0x9c48: "Xue " +0x9c49: "Bie " +0x9c4a: "Jue " +0x9c4b: "Qu " +0x9c4c: "Xiang " +0x9c4d: "Bo " +0x9c4e: "Jiao " +0x9c4f: "Xun " +0x9c50: "Su " +0x9c51: "Huang " +0x9c52: "Zun " +0x9c53: "Shan " +0x9c54: "Shan " +0x9c55: "Fan " +0x9c56: "Jue " +0x9c57: "Lin " +0x9c58: "Xun " +0x9c59: "Miao " +0x9c5a: "Xi " +0x9c5b: "Eso " +0x9c5c: "Kyou " +0x9c5d: "Fen " +0x9c5e: "Guan " +0x9c5f: "Hou " +0x9c60: "Kuai " +0x9c61: "Zei " +0x9c62: "Sao " +0x9c63: "Zhan " +0x9c64: "Gan " +0x9c65: "Gui " +0x9c66: "Sheng " +0x9c67: "Li " +0x9c68: "Chang " +0x9c69: "Hatahata " +0x9c6a: "Shiira " +0x9c6b: "Mutsu " +0x9c6c: "Ru " +0x9c6d: "Ji " +0x9c6e: "Xu " +0x9c6f: "Huo " +0x9c70: "Shiira " +0x9c71: "Li " +0x9c72: "Lie " +0x9c73: "Li " +0x9c74: "Mie " +0x9c75: "Zhen " +0x9c76: "Xiang " +0x9c77: "E " +0x9c78: "Lu " +0x9c79: "Guan " +0x9c7a: "Li " +0x9c7b: "Xian " +0x9c7c: "Yu " +0x9c7d: "Dao " +0x9c7e: "Ji " +0x9c7f: "You " +0x9c80: "Tun " +0x9c81: "Lu " +0x9c82: "Fang " +0x9c83: "Ba " +0x9c84: "He " +0x9c85: "Bo " +0x9c86: "Ping " +0x9c87: "Nian " +0x9c88: "Lu " +0x9c89: "You " +0x9c8a: "Zha " +0x9c8b: "Fu " +0x9c8c: "Bo " +0x9c8d: "Bao " +0x9c8e: "Hou " +0x9c8f: "Pi " +0x9c90: "Tai " +0x9c91: "Gui " +0x9c92: "Jie " +0x9c93: "Kao " +0x9c94: "Wei " +0x9c95: "Er " +0x9c96: "Tong " +0x9c97: "Ze " +0x9c98: "Hou " +0x9c99: "Kuai " +0x9c9a: "Ji " +0x9c9b: "Jiao " +0x9c9c: "Xian " +0x9c9d: "Za " +0x9c9e: "Xiang " +0x9c9f: "Xun " +0x9ca0: "Geng " +0x9ca1: "Li " +0x9ca2: "Lian " +0x9ca3: "Jian " +0x9ca4: "Li " +0x9ca5: "Shi " +0x9ca6: "Tiao " +0x9ca7: "Gun " +0x9ca8: "Sha " +0x9ca9: "Wan " +0x9caa: "Jun " +0x9cab: "Ji " +0x9cac: "Yong " +0x9cad: "Qing " +0x9cae: "Ling " +0x9caf: "Qi " +0x9cb0: "Zou " +0x9cb1: "Fei " +0x9cb2: "Kun " +0x9cb3: "Chang " +0x9cb4: "Gu " +0x9cb5: "Ni " +0x9cb6: "Nian " +0x9cb7: "Diao " +0x9cb8: "Jing " +0x9cb9: "Shen " +0x9cba: "Shi " +0x9cbb: "Zi " +0x9cbc: "Fen " +0x9cbd: "Die " +0x9cbe: "Bi " +0x9cbf: "Chang " +0x9cc0: "Shi " +0x9cc1: "Wen " +0x9cc2: "Wei " +0x9cc3: "Sai " +0x9cc4: "E " +0x9cc5: "Qiu " +0x9cc6: "Fu " +0x9cc7: "Huang " +0x9cc8: "Quan " +0x9cc9: "Jiang " +0x9cca: "Bian " +0x9ccb: "Sao " +0x9ccc: "Ao " +0x9ccd: "Qi " +0x9cce: "Ta " +0x9ccf: "Yin " +0x9cd0: "Yao " +0x9cd1: "Fang " +0x9cd2: "Jian " +0x9cd3: "Le " +0x9cd4: "Biao " +0x9cd5: "Xue " +0x9cd6: "Bie " +0x9cd7: "Man " +0x9cd8: "Min " +0x9cd9: "Yong " +0x9cda: "Wei " +0x9cdb: "Xi " +0x9cdc: "Jue " +0x9cdd: "Shan " +0x9cde: "Lin " +0x9cdf: "Zun " +0x9ce0: "Huo " +0x9ce1: "Gan " +0x9ce2: "Li " +0x9ce3: "Zhan " +0x9ce4: "Guan " +0x9ce5: "Niao " +0x9ce6: "Yi " +0x9ce7: "Fu " +0x9ce8: "Li " +0x9ce9: "Jiu " +0x9cea: "Bu " +0x9ceb: "Yan " +0x9cec: "Fu " +0x9ced: "Diao " +0x9cee: "Ji " +0x9cef: "Feng " +0x9cf0: "Nio " +0x9cf1: "Gan " +0x9cf2: "Shi " +0x9cf3: "Feng " +0x9cf4: "Ming " +0x9cf5: "Bao " +0x9cf6: "Yuan " +0x9cf7: "Zhi " +0x9cf8: "Hu " +0x9cf9: "Qin " +0x9cfa: "Fu " +0x9cfb: "Fen " +0x9cfc: "Wen " +0x9cfd: "Jian " +0x9cfe: "Shi " +0x9cff: "Yu " +/* x09d */ +0x9d00: "Fou " +0x9d01: "Yiao " +0x9d02: "Jue " +0x9d03: "Jue " +0x9d04: "Pi " +0x9d05: "Huan " +0x9d06: "Zhen " +0x9d07: "Bao " +0x9d08: "Yan " +0x9d09: "Ya " +0x9d0a: "Zheng " +0x9d0b: "Fang " +0x9d0c: "Feng " +0x9d0d: "Wen " +0x9d0e: "Ou " +0x9d0f: "Te " +0x9d10: "Jia " +0x9d11: "Nu " +0x9d12: "Ling " +0x9d13: "Mie " +0x9d14: "Fu " +0x9d15: "Tuo " +0x9d16: "Wen " +0x9d17: "Li " +0x9d18: "Bian " +0x9d19: "Zhi " +0x9d1a: "Ge " +0x9d1b: "Yuan " +0x9d1c: "Zi " +0x9d1d: "Qu " +0x9d1e: "Xiao " +0x9d1f: "Zhi " +0x9d20: "Dan " +0x9d21: "Ju " +0x9d22: "You " +0x9d23: "Gu " +0x9d24: "Zhong " +0x9d25: "Yu " +0x9d26: "Yang " +0x9d27: "Rong " +0x9d28: "Ya " +0x9d29: "Tie " +0x9d2a: "Yu " +0x9d2b: "Shigi " +0x9d2c: "Ying " +0x9d2d: "Zhui " +0x9d2e: "Wu " +0x9d2f: "Er " +0x9d30: "Gua " +0x9d31: "Ai " +0x9d32: "Zhi " +0x9d33: "Yan " +0x9d34: "Heng " +0x9d35: "Jiao " +0x9d36: "Ji " +0x9d37: "Lie " +0x9d38: "Zhu " +0x9d39: "Ren " +0x9d3a: "Yi " +0x9d3b: "Hong " +0x9d3c: "Luo " +0x9d3d: "Ru " +0x9d3e: "Mou " +0x9d3f: "Ge " +0x9d40: "Ren " +0x9d41: "Jiao " +0x9d42: "Xiu " +0x9d43: "Zhou " +0x9d44: "Zhi " +0x9d45: "Luo " +0x9d46: "Chidori " +0x9d47: "Toki " +0x9d48: "Ten " +0x9d49: "Luan " +0x9d4a: "Jia " +0x9d4b: "Ji " +0x9d4c: "Yu " +0x9d4d: "Huan " +0x9d4e: "Tuo " +0x9d4f: "Bu " +0x9d50: "Wu " +0x9d51: "Juan " +0x9d52: "Yu " +0x9d53: "Bo " +0x9d54: "Xun " +0x9d55: "Xun " +0x9d56: "Bi " +0x9d57: "Xi " +0x9d58: "Jun " +0x9d59: "Ju " +0x9d5a: "Tu " +0x9d5b: "Jing " +0x9d5c: "Ti " +0x9d5d: "E " +0x9d5e: "E " +0x9d5f: "Kuang " +0x9d60: "Hu " +0x9d61: "Wu " +0x9d62: "Shen " +0x9d63: "Lai " +0x9d64: "Ikaruga " +0x9d65: "Kakesu " +0x9d66: "Lu " +0x9d67: "Ping " +0x9d68: "Shu " +0x9d69: "Fu " +0x9d6a: "An " +0x9d6b: "Zhao " +0x9d6c: "Peng " +0x9d6d: "Qin " +0x9d6e: "Qian " +0x9d6f: "Bei " +0x9d70: "Diao " +0x9d71: "Lu " +0x9d72: "Que " +0x9d73: "Jian " +0x9d74: "Ju " +0x9d75: "Tu " +0x9d76: "Ya " +0x9d77: "Yuan " +0x9d78: "Qi " +0x9d79: "Li " +0x9d7a: "Ye " +0x9d7b: "Zhui " +0x9d7c: "Kong " +0x9d7d: "Zhui " +0x9d7e: "Kun " +0x9d7f: "Sheng " +0x9d80: "Qi " +0x9d81: "Jing " +0x9d82: "Yi " +0x9d83: "Yi " +0x9d84: "Jing " +0x9d85: "Zi " +0x9d86: "Lai " +0x9d87: "Dong " +0x9d88: "Qi " +0x9d89: "Chun " +0x9d8a: "Geng " +0x9d8b: "Ju " +0x9d8c: "Qu " +0x9d8d: "Isuka " +0x9d8e: "Kikuitadaki " +0x9d8f: "Ji " +0x9d90: "Shu " +0x9d91: "[?] " +0x9d92: "Chi " +0x9d93: "Miao " +0x9d94: "Rou " +0x9d95: "An " +0x9d96: "Qiu " +0x9d97: "Ti " +0x9d98: "Hu " +0x9d99: "Ti " +0x9d9a: "E " +0x9d9b: "Jie " +0x9d9c: "Mao " +0x9d9d: "Fu " +0x9d9e: "Chun " +0x9d9f: "Tu " +0x9da0: "Yan " +0x9da1: "He " +0x9da2: "Yuan " +0x9da3: "Pian " +0x9da4: "Yun " +0x9da5: "Mei " +0x9da6: "Hu " +0x9da7: "Ying " +0x9da8: "Dun " +0x9da9: "Mu " +0x9daa: "Ju " +0x9dab: "Tsugumi " +0x9dac: "Cang " +0x9dad: "Fang " +0x9dae: "Gu " +0x9daf: "Ying " +0x9db0: "Yuan " +0x9db1: "Xuan " +0x9db2: "Weng " +0x9db3: "Shi " +0x9db4: "He " +0x9db5: "Chu " +0x9db6: "Tang " +0x9db7: "Xia " +0x9db8: "Ruo " +0x9db9: "Liu " +0x9dba: "Ji " +0x9dbb: "Gu " +0x9dbc: "Jian " +0x9dbd: "Zhun " +0x9dbe: "Han " +0x9dbf: "Zi " +0x9dc0: "Zi " +0x9dc1: "Ni " +0x9dc2: "Yao " +0x9dc3: "Yan " +0x9dc4: "Ji " +0x9dc5: "Li " +0x9dc6: "Tian " +0x9dc7: "Kou " +0x9dc8: "Ti " +0x9dc9: "Ti " +0x9dca: "Ni " +0x9dcb: "Tu " +0x9dcc: "Ma " +0x9dcd: "Jiao " +0x9dce: "Gao " +0x9dcf: "Tian " +0x9dd0: "Chen " +0x9dd1: "Li " +0x9dd2: "Zhuan " +0x9dd3: "Zhe " +0x9dd4: "Ao " +0x9dd5: "Yao " +0x9dd6: "Yi " +0x9dd7: "Ou " +0x9dd8: "Chi " +0x9dd9: "Zhi " +0x9dda: "Liao " +0x9ddb: "Rong " +0x9ddc: "Lou " +0x9ddd: "Bi " +0x9dde: "Shuang " +0x9ddf: "Zhuo " +0x9de0: "Yu " +0x9de1: "Wu " +0x9de2: "Jue " +0x9de3: "Yin " +0x9de4: "Quan " +0x9de5: "Si " +0x9de6: "Jiao " +0x9de7: "Yi " +0x9de8: "Hua " +0x9de9: "Bi " +0x9dea: "Ying " +0x9deb: "Su " +0x9dec: "Huang " +0x9ded: "Fan " +0x9dee: "Jiao " +0x9def: "Liao " +0x9df0: "Yan " +0x9df1: "Kao " +0x9df2: "Jiu " +0x9df3: "Xian " +0x9df4: "Xian " +0x9df5: "Tu " +0x9df6: "Mai " +0x9df7: "Zun " +0x9df8: "Yu " +0x9df9: "Ying " +0x9dfa: "Lu " +0x9dfb: "Tuan " +0x9dfc: "Xian " +0x9dfd: "Xue " +0x9dfe: "Yi " +0x9dff: "Pi " +/* x09e */ +0x9e00: "Shu " +0x9e01: "Luo " +0x9e02: "Qi " +0x9e03: "Yi " +0x9e04: "Ji " +0x9e05: "Zhe " +0x9e06: "Yu " +0x9e07: "Zhan " +0x9e08: "Ye " +0x9e09: "Yang " +0x9e0a: "Pi " +0x9e0b: "Ning " +0x9e0c: "Huo " +0x9e0d: "Mi " +0x9e0e: "Ying " +0x9e0f: "Meng " +0x9e10: "Di " +0x9e11: "Yue " +0x9e12: "Yu " +0x9e13: "Lei " +0x9e14: "Bao " +0x9e15: "Lu " +0x9e16: "He " +0x9e17: "Long " +0x9e18: "Shuang " +0x9e19: "Yue " +0x9e1a: "Ying " +0x9e1b: "Guan " +0x9e1c: "Qu " +0x9e1d: "Li " +0x9e1e: "Luan " +0x9e1f: "Niao " +0x9e20: "Jiu " +0x9e21: "Ji " +0x9e22: "Yuan " +0x9e23: "Ming " +0x9e24: "Shi " +0x9e25: "Ou " +0x9e26: "Ya " +0x9e27: "Cang " +0x9e28: "Bao " +0x9e29: "Zhen " +0x9e2a: "Gu " +0x9e2b: "Dong " +0x9e2c: "Lu " +0x9e2d: "Ya " +0x9e2e: "Xiao " +0x9e2f: "Yang " +0x9e30: "Ling " +0x9e31: "Zhi " +0x9e32: "Qu " +0x9e33: "Yuan " +0x9e34: "Xue " +0x9e35: "Tuo " +0x9e36: "Si " +0x9e37: "Zhi " +0x9e38: "Er " +0x9e39: "Gua " +0x9e3a: "Xiu " +0x9e3b: "Heng " +0x9e3c: "Zhou " +0x9e3d: "Ge " +0x9e3e: "Luan " +0x9e3f: "Hong " +0x9e40: "Wu " +0x9e41: "Bo " +0x9e42: "Li " +0x9e43: "Juan " +0x9e44: "Hu " +0x9e45: "E " +0x9e46: "Yu " +0x9e47: "Xian " +0x9e48: "Ti " +0x9e49: "Wu " +0x9e4a: "Que " +0x9e4b: "Miao " +0x9e4c: "An " +0x9e4d: "Kun " +0x9e4e: "Bei " +0x9e4f: "Peng " +0x9e50: "Qian " +0x9e51: "Chun " +0x9e52: "Geng " +0x9e53: "Yuan " +0x9e54: "Su " +0x9e55: "Hu " +0x9e56: "He " +0x9e57: "E " +0x9e58: "Gu " +0x9e59: "Qiu " +0x9e5a: "Zi " +0x9e5b: "Mei " +0x9e5c: "Mu " +0x9e5d: "Ni " +0x9e5e: "Yao " +0x9e5f: "Weng " +0x9e60: "Liu " +0x9e61: "Ji " +0x9e62: "Ni " +0x9e63: "Jian " +0x9e64: "He " +0x9e65: "Yi " +0x9e66: "Ying " +0x9e67: "Zhe " +0x9e68: "Liao " +0x9e69: "Liao " +0x9e6a: "Jiao " +0x9e6b: "Jiu " +0x9e6c: "Yu " +0x9e6d: "Lu " +0x9e6e: "Xuan " +0x9e6f: "Zhan " +0x9e70: "Ying " +0x9e71: "Huo " +0x9e72: "Meng " +0x9e73: "Guan " +0x9e74: "Shuang " +0x9e75: "Lu " +0x9e76: "Jin " +0x9e77: "Ling " +0x9e78: "Jian " +0x9e79: "Xian " +0x9e7a: "Cuo " +0x9e7b: "Jian " +0x9e7c: "Jian " +0x9e7d: "Yan " +0x9e7e: "Cuo " +0x9e7f: "Lu " +0x9e80: "You " +0x9e81: "Cu " +0x9e82: "Ji " +0x9e83: "Biao " +0x9e84: "Cu " +0x9e85: "Biao " +0x9e86: "Zhu " +0x9e87: "Jun " +0x9e88: "Zhu " +0x9e89: "Jian " +0x9e8a: "Mi " +0x9e8b: "Mi " +0x9e8c: "Wu " +0x9e8d: "Liu " +0x9e8e: "Chen " +0x9e8f: "Jun " +0x9e90: "Lin " +0x9e91: "Ni " +0x9e92: "Qi " +0x9e93: "Lu " +0x9e94: "Jiu " +0x9e95: "Jun " +0x9e96: "Jing " +0x9e97: "Li " +0x9e98: "Xiang " +0x9e99: "Yan " +0x9e9a: "Jia " +0x9e9b: "Mi " +0x9e9c: "Li " +0x9e9d: "She " +0x9e9e: "Zhang " +0x9e9f: "Lin " +0x9ea0: "Jing " +0x9ea1: "Ji " +0x9ea2: "Ling " +0x9ea3: "Yan " +0x9ea4: "Cu " +0x9ea5: "Mai " +0x9ea6: "Mai " +0x9ea7: "Ge " +0x9ea8: "Chao " +0x9ea9: "Fu " +0x9eaa: "Mian " +0x9eab: "Mian " +0x9eac: "Fu " +0x9ead: "Pao " +0x9eae: "Qu " +0x9eaf: "Qu " +0x9eb0: "Mou " +0x9eb1: "Fu " +0x9eb2: "Xian " +0x9eb3: "Lai " +0x9eb4: "Qu " +0x9eb5: "Mian " +0x9eb6: "[?] " +0x9eb7: "Feng " +0x9eb8: "Fu " +0x9eb9: "Qu " +0x9eba: "Mian " +0x9ebb: "Ma " +0x9ebc: "Mo " +0x9ebd: "Mo " +0x9ebe: "Hui " +0x9ebf: "Ma " +0x9ec0: "Zou " +0x9ec1: "Nen " +0x9ec2: "Fen " +0x9ec3: "Huang " +0x9ec4: "Huang " +0x9ec5: "Jin " +0x9ec6: "Guang " +0x9ec7: "Tian " +0x9ec8: "Tou " +0x9ec9: "Heng " +0x9eca: "Xi " +0x9ecb: "Kuang " +0x9ecc: "Heng " +0x9ecd: "Shu " +0x9ece: "Li " +0x9ecf: "Nian " +0x9ed0: "Chi " +0x9ed1: "Hei " +0x9ed2: "Hei " +0x9ed3: "Yi " +0x9ed4: "Qian " +0x9ed5: "Dan " +0x9ed6: "Xi " +0x9ed7: "Tuan " +0x9ed8: "Mo " +0x9ed9: "Mo " +0x9eda: "Qian " +0x9edb: "Dai " +0x9edc: "Chu " +0x9edd: "You " +0x9ede: "Dian " +0x9edf: "Yi " +0x9ee0: "Xia " +0x9ee1: "Yan " +0x9ee2: "Qu " +0x9ee3: "Mei " +0x9ee4: "Yan " +0x9ee5: "Jing " +0x9ee6: "Yu " +0x9ee7: "Li " +0x9ee8: "Dang " +0x9ee9: "Du " +0x9eea: "Can " +0x9eeb: "Yin " +0x9eec: "An " +0x9eed: "Yan " +0x9eee: "Tan " +0x9eef: "An " +0x9ef0: "Zhen " +0x9ef1: "Dai " +0x9ef2: "Can " +0x9ef3: "Yi " +0x9ef4: "Mei " +0x9ef5: "Dan " +0x9ef6: "Yan " +0x9ef7: "Du " +0x9ef8: "Lu " +0x9ef9: "Zhi " +0x9efa: "Fen " +0x9efb: "Fu " +0x9efc: "Fu " +0x9efd: "Min " +0x9efe: "Min " +0x9eff: "Yuan " +/* x09f */ +0x9f00: "Cu " +0x9f01: "Qu " +0x9f02: "Chao " +0x9f03: "Wa " +0x9f04: "Zhu " +0x9f05: "Zhi " +0x9f06: "Mang " +0x9f07: "Ao " +0x9f08: "Bie " +0x9f09: "Tuo " +0x9f0a: "Bi " +0x9f0b: "Yuan " +0x9f0c: "Chao " +0x9f0d: "Tuo " +0x9f0e: "Ding " +0x9f0f: "Mi " +0x9f10: "Nai " +0x9f11: "Ding " +0x9f12: "Zi " +0x9f13: "Gu " +0x9f14: "Gu " +0x9f15: "Dong " +0x9f16: "Fen " +0x9f17: "Tao " +0x9f18: "Yuan " +0x9f19: "Pi " +0x9f1a: "Chang " +0x9f1b: "Gao " +0x9f1c: "Qi " +0x9f1d: "Yuan " +0x9f1e: "Tang " +0x9f1f: "Teng " +0x9f20: "Shu " +0x9f21: "Shu " +0x9f22: "Fen " +0x9f23: "Fei " +0x9f24: "Wen " +0x9f25: "Ba " +0x9f26: "Diao " +0x9f27: "Tuo " +0x9f28: "Tong " +0x9f29: "Qu " +0x9f2a: "Sheng " +0x9f2b: "Shi " +0x9f2c: "You " +0x9f2d: "Shi " +0x9f2e: "Ting " +0x9f2f: "Wu " +0x9f30: "Nian " +0x9f31: "Jing " +0x9f32: "Hun " +0x9f33: "Ju " +0x9f34: "Yan " +0x9f35: "Tu " +0x9f36: "Ti " +0x9f37: "Xi " +0x9f38: "Xian " +0x9f39: "Yan " +0x9f3a: "Lei " +0x9f3b: "Bi " +0x9f3c: "Yao " +0x9f3d: "Qiu " +0x9f3e: "Han " +0x9f3f: "Wu " +0x9f40: "Wu " +0x9f41: "Hou " +0x9f42: "Xi " +0x9f43: "Ge " +0x9f44: "Zha " +0x9f45: "Xiu " +0x9f46: "Weng " +0x9f47: "Zha " +0x9f48: "Nong " +0x9f49: "Nang " +0x9f4a: "Qi " +0x9f4b: "Zhai " +0x9f4c: "Ji " +0x9f4d: "Zi " +0x9f4e: "Ji " +0x9f4f: "Ji " +0x9f50: "Qi " +0x9f51: "Ji " +0x9f52: "Chi " +0x9f53: "Chen " +0x9f54: "Chen " +0x9f55: "He " +0x9f56: "Ya " +0x9f57: "Ken " +0x9f58: "Xie " +0x9f59: "Pao " +0x9f5a: "Cuo " +0x9f5b: "Shi " +0x9f5c: "Zi " +0x9f5d: "Chi " +0x9f5e: "Nian " +0x9f5f: "Ju " +0x9f60: "Tiao " +0x9f61: "Ling " +0x9f62: "Ling " +0x9f63: "Chu " +0x9f64: "Quan " +0x9f65: "Xie " +0x9f66: "Ken " +0x9f67: "Nie " +0x9f68: "Jiu " +0x9f69: "Yao " +0x9f6a: "Chuo " +0x9f6b: "Kun " +0x9f6c: "Yu " +0x9f6d: "Chu " +0x9f6e: "Yi " +0x9f6f: "Ni " +0x9f70: "Cuo " +0x9f71: "Zou " +0x9f72: "Qu " +0x9f73: "Nen " +0x9f74: "Xian " +0x9f75: "Ou " +0x9f76: "E " +0x9f77: "Wo " +0x9f78: "Yi " +0x9f79: "Chuo " +0x9f7a: "Zou " +0x9f7b: "Dian " +0x9f7c: "Chu " +0x9f7d: "Jin " +0x9f7e: "Ya " +0x9f7f: "Chi " +0x9f80: "Chen " +0x9f81: "He " +0x9f82: "Ken " +0x9f83: "Ju " +0x9f84: "Ling " +0x9f85: "Pao " +0x9f86: "Tiao " +0x9f87: "Zi " +0x9f88: "Ken " +0x9f89: "Yu " +0x9f8a: "Chuo " +0x9f8b: "Qu " +0x9f8c: "Wo " +0x9f8d: "Long " +0x9f8e: "Pang " +0x9f8f: "Gong " +0x9f90: "Pang " +0x9f91: "Yan " +0x9f92: "Long " +0x9f93: "Long " +0x9f94: "Gong " +0x9f95: "Kan " +0x9f96: "Ta " +0x9f97: "Ling " +0x9f98: "Ta " +0x9f99: "Long " +0x9f9a: "Gong " +0x9f9b: "Kan " +0x9f9c: "Gui " +0x9f9d: "Qiu " +0x9f9e: "Bie " +0x9f9f: "Gui " +0x9fa0: "Yue " +0x9fa1: "Chui " +0x9fa2: "He " +0x9fa3: "Jue " +0x9fa4: "Xie " +0x9fa5: "Yu " +0x9fa6: "[?]" +0x9fa7: "[?]" +0x9fa8: "[?]" +0x9fa9: "[?]" +0x9faa: "[?]" +0x9fab: "[?]" +0x9fac: "[?]" +0x9fad: "[?]" +0x9fae: "[?]" +0x9faf: "[?]" +0x9fb0: "[?]" +0x9fb1: "[?]" +0x9fb2: "[?]" +0x9fb3: "[?]" +0x9fb4: "[?]" +0x9fb5: "[?]" +0x9fb6: "[?]" +0x9fb7: "[?]" +0x9fb8: "[?]" +0x9fb9: "[?]" +0x9fba: "[?]" +0x9fbb: "[?]" +0x9fbc: "[?]" +0x9fbd: "[?]" +0x9fbe: "[?]" +0x9fbf: "[?]" +0x9fc0: "[?]" +0x9fc1: "[?]" +0x9fc2: "[?]" +0x9fc3: "[?]" +0x9fc4: "[?]" +0x9fc5: "[?]" +0x9fc6: "[?]" +0x9fc7: "[?]" +0x9fc8: "[?]" +0x9fc9: "[?]" +0x9fca: "[?]" +0x9fcb: "[?]" +0x9fcc: "[?]" +0x9fcd: "[?]" +0x9fce: "[?]" +0x9fcf: "[?]" +0x9fd0: "[?]" +0x9fd1: "[?]" +0x9fd2: "[?]" +0x9fd3: "[?]" +0x9fd4: "[?]" +0x9fd5: "[?]" +0x9fd6: "[?]" +0x9fd7: "[?]" +0x9fd8: "[?]" +0x9fd9: "[?]" +0x9fda: "[?]" +0x9fdb: "[?]" +0x9fdc: "[?]" +0x9fdd: "[?]" +0x9fde: "[?]" +0x9fdf: "[?]" +0x9fe0: "[?]" +0x9fe1: "[?]" +0x9fe2: "[?]" +0x9fe3: "[?]" +0x9fe4: "[?]" +0x9fe5: "[?]" +0x9fe6: "[?]" +0x9fe7: "[?]" +0x9fe8: "[?]" +0x9fe9: "[?]" +0x9fea: "[?]" +0x9feb: "[?]" +0x9fec: "[?]" +0x9fed: "[?]" +0x9fee: "[?]" +0x9fef: "[?]" +0x9ff0: "[?]" +0x9ff1: "[?]" +0x9ff2: "[?]" +0x9ff3: "[?]" +0x9ff4: "[?]" +0x9ff5: "[?]" +0x9ff6: "[?]" +0x9ff7: "[?]" +0x9ff8: "[?]" +0x9ff9: "[?]" +0x9ffa: "[?]" +0x9ffb: "[?]" +0x9ffc: "[?]" +0x9ffd: "[?]" +0x9ffe: "[?]" +/* x0a0 */ +0xa000: "it" +0xa001: "ix" +0xa002: "i" +0xa003: "ip" +0xa004: "iet" +0xa005: "iex" +0xa006: "ie" +0xa007: "iep" +0xa008: "at" +0xa009: "ax" +0xa00a: "a" +0xa00b: "ap" +0xa00c: "uox" +0xa00d: "uo" +0xa00e: "uop" +0xa00f: "ot" +0xa010: "ox" +0xa011: "o" +0xa012: "op" +0xa013: "ex" +0xa014: "e" +0xa015: "wu" +0xa016: "bit" +0xa017: "bix" +0xa018: "bi" +0xa019: "bip" +0xa01a: "biet" +0xa01b: "biex" +0xa01c: "bie" +0xa01d: "biep" +0xa01e: "bat" +0xa01f: "bax" +0xa020: "ba" +0xa021: "bap" +0xa022: "buox" +0xa023: "buo" +0xa024: "buop" +0xa025: "bot" +0xa026: "box" +0xa027: "bo" +0xa028: "bop" +0xa029: "bex" +0xa02a: "be" +0xa02b: "bep" +0xa02c: "but" +0xa02d: "bux" +0xa02e: "bu" +0xa02f: "bup" +0xa030: "burx" +0xa031: "bur" +0xa032: "byt" +0xa033: "byx" +0xa034: "by" +0xa035: "byp" +0xa036: "byrx" +0xa037: "byr" +0xa038: "pit" +0xa039: "pix" +0xa03a: "pi" +0xa03b: "pip" +0xa03c: "piex" +0xa03d: "pie" +0xa03e: "piep" +0xa03f: "pat" +0xa040: "pax" +0xa041: "pa" +0xa042: "pap" +0xa043: "puox" +0xa044: "puo" +0xa045: "puop" +0xa046: "pot" +0xa047: "pox" +0xa048: "po" +0xa049: "pop" +0xa04a: "put" +0xa04b: "pux" +0xa04c: "pu" +0xa04d: "pup" +0xa04e: "purx" +0xa04f: "pur" +0xa050: "pyt" +0xa051: "pyx" +0xa052: "py" +0xa053: "pyp" +0xa054: "pyrx" +0xa055: "pyr" +0xa056: "bbit" +0xa057: "bbix" +0xa058: "bbi" +0xa059: "bbip" +0xa05a: "bbiet" +0xa05b: "bbiex" +0xa05c: "bbie" +0xa05d: "bbiep" +0xa05e: "bbat" +0xa05f: "bbax" +0xa060: "bba" +0xa061: "bbap" +0xa062: "bbuox" +0xa063: "bbuo" +0xa064: "bbuop" +0xa065: "bbot" +0xa066: "bbox" +0xa067: "bbo" +0xa068: "bbop" +0xa069: "bbex" +0xa06a: "bbe" +0xa06b: "bbep" +0xa06c: "bbut" +0xa06d: "bbux" +0xa06e: "bbu" +0xa06f: "bbup" +0xa070: "bburx" +0xa071: "bbur" +0xa072: "bbyt" +0xa073: "bbyx" +0xa074: "bby" +0xa075: "bbyp" +0xa076: "nbit" +0xa077: "nbix" +0xa078: "nbi" +0xa079: "nbip" +0xa07a: "nbiex" +0xa07b: "nbie" +0xa07c: "nbiep" +0xa07d: "nbat" +0xa07e: "nbax" +0xa07f: "nba" +0xa080: "nbap" +0xa081: "nbot" +0xa082: "nbox" +0xa083: "nbo" +0xa084: "nbop" +0xa085: "nbut" +0xa086: "nbux" +0xa087: "nbu" +0xa088: "nbup" +0xa089: "nburx" +0xa08a: "nbur" +0xa08b: "nbyt" +0xa08c: "nbyx" +0xa08d: "nby" +0xa08e: "nbyp" +0xa08f: "nbyrx" +0xa090: "nbyr" +0xa091: "hmit" +0xa092: "hmix" +0xa093: "hmi" +0xa094: "hmip" +0xa095: "hmiex" +0xa096: "hmie" +0xa097: "hmiep" +0xa098: "hmat" +0xa099: "hmax" +0xa09a: "hma" +0xa09b: "hmap" +0xa09c: "hmuox" +0xa09d: "hmuo" +0xa09e: "hmuop" +0xa09f: "hmot" +0xa0a0: "hmox" +0xa0a1: "hmo" +0xa0a2: "hmop" +0xa0a3: "hmut" +0xa0a4: "hmux" +0xa0a5: "hmu" +0xa0a6: "hmup" +0xa0a7: "hmurx" +0xa0a8: "hmur" +0xa0a9: "hmyx" +0xa0aa: "hmy" +0xa0ab: "hmyp" +0xa0ac: "hmyrx" +0xa0ad: "hmyr" +0xa0ae: "mit" +0xa0af: "mix" +0xa0b0: "mi" +0xa0b1: "mip" +0xa0b2: "miex" +0xa0b3: "mie" +0xa0b4: "miep" +0xa0b5: "mat" +0xa0b6: "max" +0xa0b7: "ma" +0xa0b8: "map" +0xa0b9: "muot" +0xa0ba: "muox" +0xa0bb: "muo" +0xa0bc: "muop" +0xa0bd: "mot" +0xa0be: "mox" +0xa0bf: "mo" +0xa0c0: "mop" +0xa0c1: "mex" +0xa0c2: "me" +0xa0c3: "mut" +0xa0c4: "mux" +0xa0c5: "mu" +0xa0c6: "mup" +0xa0c7: "murx" +0xa0c8: "mur" +0xa0c9: "myt" +0xa0ca: "myx" +0xa0cb: "my" +0xa0cc: "myp" +0xa0cd: "fit" +0xa0ce: "fix" +0xa0cf: "fi" +0xa0d0: "fip" +0xa0d1: "fat" +0xa0d2: "fax" +0xa0d3: "fa" +0xa0d4: "fap" +0xa0d5: "fox" +0xa0d6: "fo" +0xa0d7: "fop" +0xa0d8: "fut" +0xa0d9: "fux" +0xa0da: "fu" +0xa0db: "fup" +0xa0dc: "furx" +0xa0dd: "fur" +0xa0de: "fyt" +0xa0df: "fyx" +0xa0e0: "fy" +0xa0e1: "fyp" +0xa0e2: "vit" +0xa0e3: "vix" +0xa0e4: "vi" +0xa0e5: "vip" +0xa0e6: "viet" +0xa0e7: "viex" +0xa0e8: "vie" +0xa0e9: "viep" +0xa0ea: "vat" +0xa0eb: "vax" +0xa0ec: "va" +0xa0ed: "vap" +0xa0ee: "vot" +0xa0ef: "vox" +0xa0f0: "vo" +0xa0f1: "vop" +0xa0f2: "vex" +0xa0f3: "vep" +0xa0f4: "vut" +0xa0f5: "vux" +0xa0f6: "vu" +0xa0f7: "vup" +0xa0f8: "vurx" +0xa0f9: "vur" +0xa0fa: "vyt" +0xa0fb: "vyx" +0xa0fc: "vy" +0xa0fd: "vyp" +0xa0fe: "vyrx" +0xa0ff: "vyr" +/* x0a1 */ +0xa100: "dit" +0xa101: "dix" +0xa102: "di" +0xa103: "dip" +0xa104: "diex" +0xa105: "die" +0xa106: "diep" +0xa107: "dat" +0xa108: "dax" +0xa109: "da" +0xa10a: "dap" +0xa10b: "duox" +0xa10c: "duo" +0xa10d: "dot" +0xa10e: "dox" +0xa10f: "do" +0xa110: "dop" +0xa111: "dex" +0xa112: "de" +0xa113: "dep" +0xa114: "dut" +0xa115: "dux" +0xa116: "du" +0xa117: "dup" +0xa118: "durx" +0xa119: "dur" +0xa11a: "tit" +0xa11b: "tix" +0xa11c: "ti" +0xa11d: "tip" +0xa11e: "tiex" +0xa11f: "tie" +0xa120: "tiep" +0xa121: "tat" +0xa122: "tax" +0xa123: "ta" +0xa124: "tap" +0xa125: "tuot" +0xa126: "tuox" +0xa127: "tuo" +0xa128: "tuop" +0xa129: "tot" +0xa12a: "tox" +0xa12b: "to" +0xa12c: "top" +0xa12d: "tex" +0xa12e: "te" +0xa12f: "tep" +0xa130: "tut" +0xa131: "tux" +0xa132: "tu" +0xa133: "tup" +0xa134: "turx" +0xa135: "tur" +0xa136: "ddit" +0xa137: "ddix" +0xa138: "ddi" +0xa139: "ddip" +0xa13a: "ddiex" +0xa13b: "ddie" +0xa13c: "ddiep" +0xa13d: "ddat" +0xa13e: "ddax" +0xa13f: "dda" +0xa140: "ddap" +0xa141: "dduox" +0xa142: "dduo" +0xa143: "dduop" +0xa144: "ddot" +0xa145: "ddox" +0xa146: "ddo" +0xa147: "ddop" +0xa148: "ddex" +0xa149: "dde" +0xa14a: "ddep" +0xa14b: "ddut" +0xa14c: "ddux" +0xa14d: "ddu" +0xa14e: "ddup" +0xa14f: "ddurx" +0xa150: "ddur" +0xa151: "ndit" +0xa152: "ndix" +0xa153: "ndi" +0xa154: "ndip" +0xa155: "ndiex" +0xa156: "ndie" +0xa157: "ndat" +0xa158: "ndax" +0xa159: "nda" +0xa15a: "ndap" +0xa15b: "ndot" +0xa15c: "ndox" +0xa15d: "ndo" +0xa15e: "ndop" +0xa15f: "ndex" +0xa160: "nde" +0xa161: "ndep" +0xa162: "ndut" +0xa163: "ndux" +0xa164: "ndu" +0xa165: "ndup" +0xa166: "ndurx" +0xa167: "ndur" +0xa168: "hnit" +0xa169: "hnix" +0xa16a: "hni" +0xa16b: "hnip" +0xa16c: "hniet" +0xa16d: "hniex" +0xa16e: "hnie" +0xa16f: "hniep" +0xa170: "hnat" +0xa171: "hnax" +0xa172: "hna" +0xa173: "hnap" +0xa174: "hnuox" +0xa175: "hnuo" +0xa176: "hnot" +0xa177: "hnox" +0xa178: "hnop" +0xa179: "hnex" +0xa17a: "hne" +0xa17b: "hnep" +0xa17c: "hnut" +0xa17d: "nit" +0xa17e: "nix" +0xa17f: "ni" +0xa180: "nip" +0xa181: "niex" +0xa182: "nie" +0xa183: "niep" +0xa184: "nax" +0xa185: "na" +0xa186: "nap" +0xa187: "nuox" +0xa188: "nuo" +0xa189: "nuop" +0xa18a: "not" +0xa18b: "nox" +0xa18c: "no" +0xa18d: "nop" +0xa18e: "nex" +0xa18f: "ne" +0xa190: "nep" +0xa191: "nut" +0xa192: "nux" +0xa193: "nu" +0xa194: "nup" +0xa195: "nurx" +0xa196: "nur" +0xa197: "hlit" +0xa198: "hlix" +0xa199: "hli" +0xa19a: "hlip" +0xa19b: "hliex" +0xa19c: "hlie" +0xa19d: "hliep" +0xa19e: "hlat" +0xa19f: "hlax" +0xa1a0: "hla" +0xa1a1: "hlap" +0xa1a2: "hluox" +0xa1a3: "hluo" +0xa1a4: "hluop" +0xa1a5: "hlox" +0xa1a6: "hlo" +0xa1a7: "hlop" +0xa1a8: "hlex" +0xa1a9: "hle" +0xa1aa: "hlep" +0xa1ab: "hlut" +0xa1ac: "hlux" +0xa1ad: "hlu" +0xa1ae: "hlup" +0xa1af: "hlurx" +0xa1b0: "hlur" +0xa1b1: "hlyt" +0xa1b2: "hlyx" +0xa1b3: "hly" +0xa1b4: "hlyp" +0xa1b5: "hlyrx" +0xa1b6: "hlyr" +0xa1b7: "lit" +0xa1b8: "lix" +0xa1b9: "li" +0xa1ba: "lip" +0xa1bb: "liet" +0xa1bc: "liex" +0xa1bd: "lie" +0xa1be: "liep" +0xa1bf: "lat" +0xa1c0: "lax" +0xa1c1: "la" +0xa1c2: "lap" +0xa1c3: "luot" +0xa1c4: "luox" +0xa1c5: "luo" +0xa1c6: "luop" +0xa1c7: "lot" +0xa1c8: "lox" +0xa1c9: "lo" +0xa1ca: "lop" +0xa1cb: "lex" +0xa1cc: "le" +0xa1cd: "lep" +0xa1ce: "lut" +0xa1cf: "lux" +0xa1d0: "lu" +0xa1d1: "lup" +0xa1d2: "lurx" +0xa1d3: "lur" +0xa1d4: "lyt" +0xa1d5: "lyx" +0xa1d6: "ly" +0xa1d7: "lyp" +0xa1d8: "lyrx" +0xa1d9: "lyr" +0xa1da: "git" +0xa1db: "gix" +0xa1dc: "gi" +0xa1dd: "gip" +0xa1de: "giet" +0xa1df: "giex" +0xa1e0: "gie" +0xa1e1: "giep" +0xa1e2: "gat" +0xa1e3: "gax" +0xa1e4: "ga" +0xa1e5: "gap" +0xa1e6: "guot" +0xa1e7: "guox" +0xa1e8: "guo" +0xa1e9: "guop" +0xa1ea: "got" +0xa1eb: "gox" +0xa1ec: "go" +0xa1ed: "gop" +0xa1ee: "get" +0xa1ef: "gex" +0xa1f0: "ge" +0xa1f1: "gep" +0xa1f2: "gut" +0xa1f3: "gux" +0xa1f4: "gu" +0xa1f5: "gup" +0xa1f6: "gurx" +0xa1f7: "gur" +0xa1f8: "kit" +0xa1f9: "kix" +0xa1fa: "ki" +0xa1fb: "kip" +0xa1fc: "kiex" +0xa1fd: "kie" +0xa1fe: "kiep" +0xa1ff: "kat" +/* x0a2 */ +0xa200: "kax" +0xa201: "ka" +0xa202: "kap" +0xa203: "kuox" +0xa204: "kuo" +0xa205: "kuop" +0xa206: "kot" +0xa207: "kox" +0xa208: "ko" +0xa209: "kop" +0xa20a: "ket" +0xa20b: "kex" +0xa20c: "ke" +0xa20d: "kep" +0xa20e: "kut" +0xa20f: "kux" +0xa210: "ku" +0xa211: "kup" +0xa212: "kurx" +0xa213: "kur" +0xa214: "ggit" +0xa215: "ggix" +0xa216: "ggi" +0xa217: "ggiex" +0xa218: "ggie" +0xa219: "ggiep" +0xa21a: "ggat" +0xa21b: "ggax" +0xa21c: "gga" +0xa21d: "ggap" +0xa21e: "gguot" +0xa21f: "gguox" +0xa220: "gguo" +0xa221: "gguop" +0xa222: "ggot" +0xa223: "ggox" +0xa224: "ggo" +0xa225: "ggop" +0xa226: "gget" +0xa227: "ggex" +0xa228: "gge" +0xa229: "ggep" +0xa22a: "ggut" +0xa22b: "ggux" +0xa22c: "ggu" +0xa22d: "ggup" +0xa22e: "ggurx" +0xa22f: "ggur" +0xa230: "mgiex" +0xa231: "mgie" +0xa232: "mgat" +0xa233: "mgax" +0xa234: "mga" +0xa235: "mgap" +0xa236: "mguox" +0xa237: "mguo" +0xa238: "mguop" +0xa239: "mgot" +0xa23a: "mgox" +0xa23b: "mgo" +0xa23c: "mgop" +0xa23d: "mgex" +0xa23e: "mge" +0xa23f: "mgep" +0xa240: "mgut" +0xa241: "mgux" +0xa242: "mgu" +0xa243: "mgup" +0xa244: "mgurx" +0xa245: "mgur" +0xa246: "hxit" +0xa247: "hxix" +0xa248: "hxi" +0xa249: "hxip" +0xa24a: "hxiet" +0xa24b: "hxiex" +0xa24c: "hxie" +0xa24d: "hxiep" +0xa24e: "hxat" +0xa24f: "hxax" +0xa250: "hxa" +0xa251: "hxap" +0xa252: "hxuot" +0xa253: "hxuox" +0xa254: "hxuo" +0xa255: "hxuop" +0xa256: "hxot" +0xa257: "hxox" +0xa258: "hxo" +0xa259: "hxop" +0xa25a: "hxex" +0xa25b: "hxe" +0xa25c: "hxep" +0xa25d: "ngiex" +0xa25e: "ngie" +0xa25f: "ngiep" +0xa260: "ngat" +0xa261: "ngax" +0xa262: "nga" +0xa263: "ngap" +0xa264: "nguot" +0xa265: "nguox" +0xa266: "nguo" +0xa267: "ngot" +0xa268: "ngox" +0xa269: "ngo" +0xa26a: "ngop" +0xa26b: "ngex" +0xa26c: "nge" +0xa26d: "ngep" +0xa26e: "hit" +0xa26f: "hiex" +0xa270: "hie" +0xa271: "hat" +0xa272: "hax" +0xa273: "ha" +0xa274: "hap" +0xa275: "huot" +0xa276: "huox" +0xa277: "huo" +0xa278: "huop" +0xa279: "hot" +0xa27a: "hox" +0xa27b: "ho" +0xa27c: "hop" +0xa27d: "hex" +0xa27e: "he" +0xa27f: "hep" +0xa280: "wat" +0xa281: "wax" +0xa282: "wa" +0xa283: "wap" +0xa284: "wuox" +0xa285: "wuo" +0xa286: "wuop" +0xa287: "wox" +0xa288: "wo" +0xa289: "wop" +0xa28a: "wex" +0xa28b: "we" +0xa28c: "wep" +0xa28d: "zit" +0xa28e: "zix" +0xa28f: "zi" +0xa290: "zip" +0xa291: "ziex" +0xa292: "zie" +0xa293: "ziep" +0xa294: "zat" +0xa295: "zax" +0xa296: "za" +0xa297: "zap" +0xa298: "zuox" +0xa299: "zuo" +0xa29a: "zuop" +0xa29b: "zot" +0xa29c: "zox" +0xa29d: "zo" +0xa29e: "zop" +0xa29f: "zex" +0xa2a0: "ze" +0xa2a1: "zep" +0xa2a2: "zut" +0xa2a3: "zux" +0xa2a4: "zu" +0xa2a5: "zup" +0xa2a6: "zurx" +0xa2a7: "zur" +0xa2a8: "zyt" +0xa2a9: "zyx" +0xa2aa: "zy" +0xa2ab: "zyp" +0xa2ac: "zyrx" +0xa2ad: "zyr" +0xa2ae: "cit" +0xa2af: "cix" +0xa2b0: "ci" +0xa2b1: "cip" +0xa2b2: "ciet" +0xa2b3: "ciex" +0xa2b4: "cie" +0xa2b5: "ciep" +0xa2b6: "cat" +0xa2b7: "cax" +0xa2b8: "ca" +0xa2b9: "cap" +0xa2ba: "cuox" +0xa2bb: "cuo" +0xa2bc: "cuop" +0xa2bd: "cot" +0xa2be: "cox" +0xa2bf: "co" +0xa2c0: "cop" +0xa2c1: "cex" +0xa2c2: "ce" +0xa2c3: "cep" +0xa2c4: "cut" +0xa2c5: "cux" +0xa2c6: "cu" +0xa2c7: "cup" +0xa2c8: "curx" +0xa2c9: "cur" +0xa2ca: "cyt" +0xa2cb: "cyx" +0xa2cc: "cy" +0xa2cd: "cyp" +0xa2ce: "cyrx" +0xa2cf: "cyr" +0xa2d0: "zzit" +0xa2d1: "zzix" +0xa2d2: "zzi" +0xa2d3: "zzip" +0xa2d4: "zziet" +0xa2d5: "zziex" +0xa2d6: "zzie" +0xa2d7: "zziep" +0xa2d8: "zzat" +0xa2d9: "zzax" +0xa2da: "zza" +0xa2db: "zzap" +0xa2dc: "zzox" +0xa2dd: "zzo" +0xa2de: "zzop" +0xa2df: "zzex" +0xa2e0: "zze" +0xa2e1: "zzep" +0xa2e2: "zzux" +0xa2e3: "zzu" +0xa2e4: "zzup" +0xa2e5: "zzurx" +0xa2e6: "zzur" +0xa2e7: "zzyt" +0xa2e8: "zzyx" +0xa2e9: "zzy" +0xa2ea: "zzyp" +0xa2eb: "zzyrx" +0xa2ec: "zzyr" +0xa2ed: "nzit" +0xa2ee: "nzix" +0xa2ef: "nzi" +0xa2f0: "nzip" +0xa2f1: "nziex" +0xa2f2: "nzie" +0xa2f3: "nziep" +0xa2f4: "nzat" +0xa2f5: "nzax" +0xa2f6: "nza" +0xa2f7: "nzap" +0xa2f8: "nzuox" +0xa2f9: "nzuo" +0xa2fa: "nzox" +0xa2fb: "nzop" +0xa2fc: "nzex" +0xa2fd: "nze" +0xa2fe: "nzux" +0xa2ff: "nzu" +/* x0a3 */ +0xa300: "nzup" +0xa301: "nzurx" +0xa302: "nzur" +0xa303: "nzyt" +0xa304: "nzyx" +0xa305: "nzy" +0xa306: "nzyp" +0xa307: "nzyrx" +0xa308: "nzyr" +0xa309: "sit" +0xa30a: "six" +0xa30b: "si" +0xa30c: "sip" +0xa30d: "siex" +0xa30e: "sie" +0xa30f: "siep" +0xa310: "sat" +0xa311: "sax" +0xa312: "sa" +0xa313: "sap" +0xa314: "suox" +0xa315: "suo" +0xa316: "suop" +0xa317: "sot" +0xa318: "sox" +0xa319: "so" +0xa31a: "sop" +0xa31b: "sex" +0xa31c: "se" +0xa31d: "sep" +0xa31e: "sut" +0xa31f: "sux" +0xa320: "su" +0xa321: "sup" +0xa322: "surx" +0xa323: "sur" +0xa324: "syt" +0xa325: "syx" +0xa326: "sy" +0xa327: "syp" +0xa328: "syrx" +0xa329: "syr" +0xa32a: "ssit" +0xa32b: "ssix" +0xa32c: "ssi" +0xa32d: "ssip" +0xa32e: "ssiex" +0xa32f: "ssie" +0xa330: "ssiep" +0xa331: "ssat" +0xa332: "ssax" +0xa333: "ssa" +0xa334: "ssap" +0xa335: "ssot" +0xa336: "ssox" +0xa337: "sso" +0xa338: "ssop" +0xa339: "ssex" +0xa33a: "sse" +0xa33b: "ssep" +0xa33c: "ssut" +0xa33d: "ssux" +0xa33e: "ssu" +0xa33f: "ssup" +0xa340: "ssyt" +0xa341: "ssyx" +0xa342: "ssy" +0xa343: "ssyp" +0xa344: "ssyrx" +0xa345: "ssyr" +0xa346: "zhat" +0xa347: "zhax" +0xa348: "zha" +0xa349: "zhap" +0xa34a: "zhuox" +0xa34b: "zhuo" +0xa34c: "zhuop" +0xa34d: "zhot" +0xa34e: "zhox" +0xa34f: "zho" +0xa350: "zhop" +0xa351: "zhet" +0xa352: "zhex" +0xa353: "zhe" +0xa354: "zhep" +0xa355: "zhut" +0xa356: "zhux" +0xa357: "zhu" +0xa358: "zhup" +0xa359: "zhurx" +0xa35a: "zhur" +0xa35b: "zhyt" +0xa35c: "zhyx" +0xa35d: "zhy" +0xa35e: "zhyp" +0xa35f: "zhyrx" +0xa360: "zhyr" +0xa361: "chat" +0xa362: "chax" +0xa363: "cha" +0xa364: "chap" +0xa365: "chuot" +0xa366: "chuox" +0xa367: "chuo" +0xa368: "chuop" +0xa369: "chot" +0xa36a: "chox" +0xa36b: "cho" +0xa36c: "chop" +0xa36d: "chet" +0xa36e: "chex" +0xa36f: "che" +0xa370: "chep" +0xa371: "chux" +0xa372: "chu" +0xa373: "chup" +0xa374: "churx" +0xa375: "chur" +0xa376: "chyt" +0xa377: "chyx" +0xa378: "chy" +0xa379: "chyp" +0xa37a: "chyrx" +0xa37b: "chyr" +0xa37c: "rrax" +0xa37d: "rra" +0xa37e: "rruox" +0xa37f: "rruo" +0xa380: "rrot" +0xa381: "rrox" +0xa382: "rro" +0xa383: "rrop" +0xa384: "rret" +0xa385: "rrex" +0xa386: "rre" +0xa387: "rrep" +0xa388: "rrut" +0xa389: "rrux" +0xa38a: "rru" +0xa38b: "rrup" +0xa38c: "rrurx" +0xa38d: "rrur" +0xa38e: "rryt" +0xa38f: "rryx" +0xa390: "rry" +0xa391: "rryp" +0xa392: "rryrx" +0xa393: "rryr" +0xa394: "nrat" +0xa395: "nrax" +0xa396: "nra" +0xa397: "nrap" +0xa398: "nrox" +0xa399: "nro" +0xa39a: "nrop" +0xa39b: "nret" +0xa39c: "nrex" +0xa39d: "nre" +0xa39e: "nrep" +0xa39f: "nrut" +0xa3a0: "nrux" +0xa3a1: "nru" +0xa3a2: "nrup" +0xa3a3: "nrurx" +0xa3a4: "nrur" +0xa3a5: "nryt" +0xa3a6: "nryx" +0xa3a7: "nry" +0xa3a8: "nryp" +0xa3a9: "nryrx" +0xa3aa: "nryr" +0xa3ab: "shat" +0xa3ac: "shax" +0xa3ad: "sha" +0xa3ae: "shap" +0xa3af: "shuox" +0xa3b0: "shuo" +0xa3b1: "shuop" +0xa3b2: "shot" +0xa3b3: "shox" +0xa3b4: "sho" +0xa3b5: "shop" +0xa3b6: "shet" +0xa3b7: "shex" +0xa3b8: "she" +0xa3b9: "shep" +0xa3ba: "shut" +0xa3bb: "shux" +0xa3bc: "shu" +0xa3bd: "shup" +0xa3be: "shurx" +0xa3bf: "shur" +0xa3c0: "shyt" +0xa3c1: "shyx" +0xa3c2: "shy" +0xa3c3: "shyp" +0xa3c4: "shyrx" +0xa3c5: "shyr" +0xa3c6: "rat" +0xa3c7: "rax" +0xa3c8: "ra" +0xa3c9: "rap" +0xa3ca: "ruox" +0xa3cb: "ruo" +0xa3cc: "ruop" +0xa3cd: "rot" +0xa3ce: "rox" +0xa3cf: "ro" +0xa3d0: "rop" +0xa3d1: "rex" +0xa3d2: "re" +0xa3d3: "rep" +0xa3d4: "rut" +0xa3d5: "rux" +0xa3d6: "ru" +0xa3d7: "rup" +0xa3d8: "rurx" +0xa3d9: "rur" +0xa3da: "ryt" +0xa3db: "ryx" +0xa3dc: "ry" +0xa3dd: "ryp" +0xa3de: "ryrx" +0xa3df: "ryr" +0xa3e0: "jit" +0xa3e1: "jix" +0xa3e2: "ji" +0xa3e3: "jip" +0xa3e4: "jiet" +0xa3e5: "jiex" +0xa3e6: "jie" +0xa3e7: "jiep" +0xa3e8: "juot" +0xa3e9: "juox" +0xa3ea: "juo" +0xa3eb: "juop" +0xa3ec: "jot" +0xa3ed: "jox" +0xa3ee: "jo" +0xa3ef: "jop" +0xa3f0: "jut" +0xa3f1: "jux" +0xa3f2: "ju" +0xa3f3: "jup" +0xa3f4: "jurx" +0xa3f5: "jur" +0xa3f6: "jyt" +0xa3f7: "jyx" +0xa3f8: "jy" +0xa3f9: "jyp" +0xa3fa: "jyrx" +0xa3fb: "jyr" +0xa3fc: "qit" +0xa3fd: "qix" +0xa3fe: "qi" +0xa3ff: "qip" +/* x0a4 */ +0xa400: "qiet" +0xa401: "qiex" +0xa402: "qie" +0xa403: "qiep" +0xa404: "quot" +0xa405: "quox" +0xa406: "quo" +0xa407: "quop" +0xa408: "qot" +0xa409: "qox" +0xa40a: "qo" +0xa40b: "qop" +0xa40c: "qut" +0xa40d: "qux" +0xa40e: "qu" +0xa40f: "qup" +0xa410: "qurx" +0xa411: "qur" +0xa412: "qyt" +0xa413: "qyx" +0xa414: "qy" +0xa415: "qyp" +0xa416: "qyrx" +0xa417: "qyr" +0xa418: "jjit" +0xa419: "jjix" +0xa41a: "jji" +0xa41b: "jjip" +0xa41c: "jjiet" +0xa41d: "jjiex" +0xa41e: "jjie" +0xa41f: "jjiep" +0xa420: "jjuox" +0xa421: "jjuo" +0xa422: "jjuop" +0xa423: "jjot" +0xa424: "jjox" +0xa425: "jjo" +0xa426: "jjop" +0xa427: "jjut" +0xa428: "jjux" +0xa429: "jju" +0xa42a: "jjup" +0xa42b: "jjurx" +0xa42c: "jjur" +0xa42d: "jjyt" +0xa42e: "jjyx" +0xa42f: "jjy" +0xa430: "jjyp" +0xa431: "njit" +0xa432: "njix" +0xa433: "nji" +0xa434: "njip" +0xa435: "njiet" +0xa436: "njiex" +0xa437: "njie" +0xa438: "njiep" +0xa439: "njuox" +0xa43a: "njuo" +0xa43b: "njot" +0xa43c: "njox" +0xa43d: "njo" +0xa43e: "njop" +0xa43f: "njux" +0xa440: "nju" +0xa441: "njup" +0xa442: "njurx" +0xa443: "njur" +0xa444: "njyt" +0xa445: "njyx" +0xa446: "njy" +0xa447: "njyp" +0xa448: "njyrx" +0xa449: "njyr" +0xa44a: "nyit" +0xa44b: "nyix" +0xa44c: "nyi" +0xa44d: "nyip" +0xa44e: "nyiet" +0xa44f: "nyiex" +0xa450: "nyie" +0xa451: "nyiep" +0xa452: "nyuox" +0xa453: "nyuo" +0xa454: "nyuop" +0xa455: "nyot" +0xa456: "nyox" +0xa457: "nyo" +0xa458: "nyop" +0xa459: "nyut" +0xa45a: "nyux" +0xa45b: "nyu" +0xa45c: "nyup" +0xa45d: "xit" +0xa45e: "xix" +0xa45f: "xi" +0xa460: "xip" +0xa461: "xiet" +0xa462: "xiex" +0xa463: "xie" +0xa464: "xiep" +0xa465: "xuox" +0xa466: "xuo" +0xa467: "xot" +0xa468: "xox" +0xa469: "xo" +0xa46a: "xop" +0xa46b: "xyt" +0xa46c: "xyx" +0xa46d: "xy" +0xa46e: "xyp" +0xa46f: "xyrx" +0xa470: "xyr" +0xa471: "yit" +0xa472: "yix" +0xa473: "yi" +0xa474: "yip" +0xa475: "yiet" +0xa476: "yiex" +0xa477: "yie" +0xa478: "yiep" +0xa479: "yuot" +0xa47a: "yuox" +0xa47b: "yuo" +0xa47c: "yuop" +0xa47d: "yot" +0xa47e: "yox" +0xa47f: "yo" +0xa480: "yop" +0xa481: "yut" +0xa482: "yux" +0xa483: "yu" +0xa484: "yup" +0xa485: "yurx" +0xa486: "yur" +0xa487: "yyt" +0xa488: "yyx" +0xa489: "yy" +0xa48a: "yyp" +0xa48b: "yyrx" +0xa48c: "yyr" +0xa48d: "[?]" +0xa48e: "[?]" +0xa48f: "[?]" +0xa490: "Qot" +0xa491: "Li" +0xa492: "Kit" +0xa493: "Nyip" +0xa494: "Cyp" +0xa495: "Ssi" +0xa496: "Ggop" +0xa497: "Gep" +0xa498: "Mi" +0xa499: "Hxit" +0xa49a: "Lyr" +0xa49b: "Bbut" +0xa49c: "Mop" +0xa49d: "Yo" +0xa49e: "Put" +0xa49f: "Hxuo" +0xa4a0: "Tat" +0xa4a1: "Ga" +0xa4a2: "[?]" +0xa4a3: "[?]" +0xa4a4: "Ddur" +0xa4a5: "Bur" +0xa4a6: "Gguo" +0xa4a7: "Nyop" +0xa4a8: "Tu" +0xa4a9: "Op" +0xa4aa: "Jjut" +0xa4ab: "Zot" +0xa4ac: "Pyt" +0xa4ad: "Hmo" +0xa4ae: "Yit" +0xa4af: "Vur" +0xa4b0: "Shy" +0xa4b1: "Vep" +0xa4b2: "Za" +0xa4b3: "Jo" +0xa4b4: "[?]" +0xa4b5: "Jjy" +0xa4b6: "Got" +0xa4b7: "Jjie" +0xa4b8: "Wo" +0xa4b9: "Du" +0xa4ba: "Shur" +0xa4bb: "Lie" +0xa4bc: "Cy" +0xa4bd: "Cuop" +0xa4be: "Cip" +0xa4bf: "Hxop" +0xa4c0: "Shat" +0xa4c1: "[?]" +0xa4c2: "Shop" +0xa4c3: "Che" +0xa4c4: "Zziet" +0xa4c5: "[?]" +0xa4c6: "Ke" +0xa4c7: "[?]" +0xa4c8: "[?]" +0xa4c9: "[?]" +0xa4ca: "[?]" +0xa4cb: "[?]" +0xa4cc: "[?]" +0xa4cd: "[?]" +0xa4ce: "[?]" +0xa4cf: "[?]" +0xa4d0: "[?]" +0xa4d1: "[?]" +0xa4d2: "[?]" +0xa4d3: "[?]" +0xa4d4: "[?]" +0xa4d5: "[?]" +0xa4d6: "[?]" +0xa4d7: "[?]" +0xa4d8: "[?]" +0xa4d9: "[?]" +0xa4da: "[?]" +0xa4db: "[?]" +0xa4dc: "[?]" +0xa4dd: "[?]" +0xa4de: "[?]" +0xa4df: "[?]" +0xa4e0: "[?]" +0xa4e1: "[?]" +0xa4e2: "[?]" +0xa4e3: "[?]" +0xa4e4: "[?]" +0xa4e5: "[?]" +0xa4e6: "[?]" +0xa4e7: "[?]" +0xa4e8: "[?]" +0xa4e9: "[?]" +0xa4ea: "[?]" +0xa4eb: "[?]" +0xa4ec: "[?]" +0xa4ed: "[?]" +0xa4ee: "[?]" +0xa4ef: "[?]" +0xa4f0: "[?]" +0xa4f1: "[?]" +0xa4f2: "[?]" +0xa4f3: "[?]" +0xa4f4: "[?]" +0xa4f5: "[?]" +0xa4f6: "[?]" +0xa4f7: "[?]" +0xa4f8: "[?]" +0xa4f9: "[?]" +0xa4fa: "[?]" +0xa4fb: "[?]" +0xa4fc: "[?]" +0xa4fd: "[?]" +0xa4fe: "[?]" +/* x0ac */ +0xac00: "ga" +0xac01: "gag" +0xac02: "gagg" +0xac03: "gags" +0xac04: "gan" +0xac05: "ganj" +0xac06: "ganh" +0xac07: "gad" +0xac08: "gal" +0xac09: "galg" +0xac0a: "galm" +0xac0b: "galb" +0xac0c: "gals" +0xac0d: "galt" +0xac0e: "galp" +0xac0f: "galh" +0xac10: "gam" +0xac11: "gab" +0xac12: "gabs" +0xac13: "gas" +0xac14: "gass" +0xac15: "gang" +0xac16: "gaj" +0xac17: "gac" +0xac18: "gak" +0xac19: "gat" +0xac1a: "gap" +0xac1b: "gah" +0xac1c: "gae" +0xac1d: "gaeg" +0xac1e: "gaegg" +0xac1f: "gaegs" +0xac20: "gaen" +0xac21: "gaenj" +0xac22: "gaenh" +0xac23: "gaed" +0xac24: "gael" +0xac25: "gaelg" +0xac26: "gaelm" +0xac27: "gaelb" +0xac28: "gaels" +0xac29: "gaelt" +0xac2a: "gaelp" +0xac2b: "gaelh" +0xac2c: "gaem" +0xac2d: "gaeb" +0xac2e: "gaebs" +0xac2f: "gaes" +0xac30: "gaess" +0xac31: "gaeng" +0xac32: "gaej" +0xac33: "gaec" +0xac34: "gaek" +0xac35: "gaet" +0xac36: "gaep" +0xac37: "gaeh" +0xac38: "gya" +0xac39: "gyag" +0xac3a: "gyagg" +0xac3b: "gyags" +0xac3c: "gyan" +0xac3d: "gyanj" +0xac3e: "gyanh" +0xac3f: "gyad" +0xac40: "gyal" +0xac41: "gyalg" +0xac42: "gyalm" +0xac43: "gyalb" +0xac44: "gyals" +0xac45: "gyalt" +0xac46: "gyalp" +0xac47: "gyalh" +0xac48: "gyam" +0xac49: "gyab" +0xac4a: "gyabs" +0xac4b: "gyas" +0xac4c: "gyass" +0xac4d: "gyang" +0xac4e: "gyaj" +0xac4f: "gyac" +0xac50: "gyak" +0xac51: "gyat" +0xac52: "gyap" +0xac53: "gyah" +0xac54: "gyae" +0xac55: "gyaeg" +0xac56: "gyaegg" +0xac57: "gyaegs" +0xac58: "gyaen" +0xac59: "gyaenj" +0xac5a: "gyaenh" +0xac5b: "gyaed" +0xac5c: "gyael" +0xac5d: "gyaelg" +0xac5e: "gyaelm" +0xac5f: "gyaelb" +0xac60: "gyaels" +0xac61: "gyaelt" +0xac62: "gyaelp" +0xac63: "gyaelh" +0xac64: "gyaem" +0xac65: "gyaeb" +0xac66: "gyaebs" +0xac67: "gyaes" +0xac68: "gyaess" +0xac69: "gyaeng" +0xac6a: "gyaej" +0xac6b: "gyaec" +0xac6c: "gyaek" +0xac6d: "gyaet" +0xac6e: "gyaep" +0xac6f: "gyaeh" +0xac70: "geo" +0xac71: "geog" +0xac72: "geogg" +0xac73: "geogs" +0xac74: "geon" +0xac75: "geonj" +0xac76: "geonh" +0xac77: "geod" +0xac78: "geol" +0xac79: "geolg" +0xac7a: "geolm" +0xac7b: "geolb" +0xac7c: "geols" +0xac7d: "geolt" +0xac7e: "geolp" +0xac7f: "geolh" +0xac80: "geom" +0xac81: "geob" +0xac82: "geobs" +0xac83: "geos" +0xac84: "geoss" +0xac85: "geong" +0xac86: "geoj" +0xac87: "geoc" +0xac88: "geok" +0xac89: "geot" +0xac8a: "geop" +0xac8b: "geoh" +0xac8c: "ge" +0xac8d: "geg" +0xac8e: "gegg" +0xac8f: "gegs" +0xac90: "gen" +0xac91: "genj" +0xac92: "genh" +0xac93: "ged" +0xac94: "gel" +0xac95: "gelg" +0xac96: "gelm" +0xac97: "gelb" +0xac98: "gels" +0xac99: "gelt" +0xac9a: "gelp" +0xac9b: "gelh" +0xac9c: "gem" +0xac9d: "geb" +0xac9e: "gebs" +0xac9f: "ges" +0xaca0: "gess" +0xaca1: "geng" +0xaca2: "gej" +0xaca3: "gec" +0xaca4: "gek" +0xaca5: "get" +0xaca6: "gep" +0xaca7: "geh" +0xaca8: "gyeo" +0xaca9: "gyeog" +0xacaa: "gyeogg" +0xacab: "gyeogs" +0xacac: "gyeon" +0xacad: "gyeonj" +0xacae: "gyeonh" +0xacaf: "gyeod" +0xacb0: "gyeol" +0xacb1: "gyeolg" +0xacb2: "gyeolm" +0xacb3: "gyeolb" +0xacb4: "gyeols" +0xacb5: "gyeolt" +0xacb6: "gyeolp" +0xacb7: "gyeolh" +0xacb8: "gyeom" +0xacb9: "gyeob" +0xacba: "gyeobs" +0xacbb: "gyeos" +0xacbc: "gyeoss" +0xacbd: "gyeong" +0xacbe: "gyeoj" +0xacbf: "gyeoc" +0xacc0: "gyeok" +0xacc1: "gyeot" +0xacc2: "gyeop" +0xacc3: "gyeoh" +0xacc4: "gye" +0xacc5: "gyeg" +0xacc6: "gyegg" +0xacc7: "gyegs" +0xacc8: "gyen" +0xacc9: "gyenj" +0xacca: "gyenh" +0xaccb: "gyed" +0xaccc: "gyel" +0xaccd: "gyelg" +0xacce: "gyelm" +0xaccf: "gyelb" +0xacd0: "gyels" +0xacd1: "gyelt" +0xacd2: "gyelp" +0xacd3: "gyelh" +0xacd4: "gyem" +0xacd5: "gyeb" +0xacd6: "gyebs" +0xacd7: "gyes" +0xacd8: "gyess" +0xacd9: "gyeng" +0xacda: "gyej" +0xacdb: "gyec" +0xacdc: "gyek" +0xacdd: "gyet" +0xacde: "gyep" +0xacdf: "gyeh" +0xace0: "go" +0xace1: "gog" +0xace2: "gogg" +0xace3: "gogs" +0xace4: "gon" +0xace5: "gonj" +0xace6: "gonh" +0xace7: "god" +0xace8: "gol" +0xace9: "golg" +0xacea: "golm" +0xaceb: "golb" +0xacec: "gols" +0xaced: "golt" +0xacee: "golp" +0xacef: "golh" +0xacf0: "gom" +0xacf1: "gob" +0xacf2: "gobs" +0xacf3: "gos" +0xacf4: "goss" +0xacf5: "gong" +0xacf6: "goj" +0xacf7: "goc" +0xacf8: "gok" +0xacf9: "got" +0xacfa: "gop" +0xacfb: "goh" +0xacfc: "gwa" +0xacfd: "gwag" +0xacfe: "gwagg" +0xacff: "gwags" +/* x0ad */ +0xad00: "gwan" +0xad01: "gwanj" +0xad02: "gwanh" +0xad03: "gwad" +0xad04: "gwal" +0xad05: "gwalg" +0xad06: "gwalm" +0xad07: "gwalb" +0xad08: "gwals" +0xad09: "gwalt" +0xad0a: "gwalp" +0xad0b: "gwalh" +0xad0c: "gwam" +0xad0d: "gwab" +0xad0e: "gwabs" +0xad0f: "gwas" +0xad10: "gwass" +0xad11: "gwang" +0xad12: "gwaj" +0xad13: "gwac" +0xad14: "gwak" +0xad15: "gwat" +0xad16: "gwap" +0xad17: "gwah" +0xad18: "gwae" +0xad19: "gwaeg" +0xad1a: "gwaegg" +0xad1b: "gwaegs" +0xad1c: "gwaen" +0xad1d: "gwaenj" +0xad1e: "gwaenh" +0xad1f: "gwaed" +0xad20: "gwael" +0xad21: "gwaelg" +0xad22: "gwaelm" +0xad23: "gwaelb" +0xad24: "gwaels" +0xad25: "gwaelt" +0xad26: "gwaelp" +0xad27: "gwaelh" +0xad28: "gwaem" +0xad29: "gwaeb" +0xad2a: "gwaebs" +0xad2b: "gwaes" +0xad2c: "gwaess" +0xad2d: "gwaeng" +0xad2e: "gwaej" +0xad2f: "gwaec" +0xad30: "gwaek" +0xad31: "gwaet" +0xad32: "gwaep" +0xad33: "gwaeh" +0xad34: "goe" +0xad35: "goeg" +0xad36: "goegg" +0xad37: "goegs" +0xad38: "goen" +0xad39: "goenj" +0xad3a: "goenh" +0xad3b: "goed" +0xad3c: "goel" +0xad3d: "goelg" +0xad3e: "goelm" +0xad3f: "goelb" +0xad40: "goels" +0xad41: "goelt" +0xad42: "goelp" +0xad43: "goelh" +0xad44: "goem" +0xad45: "goeb" +0xad46: "goebs" +0xad47: "goes" +0xad48: "goess" +0xad49: "goeng" +0xad4a: "goej" +0xad4b: "goec" +0xad4c: "goek" +0xad4d: "goet" +0xad4e: "goep" +0xad4f: "goeh" +0xad50: "gyo" +0xad51: "gyog" +0xad52: "gyogg" +0xad53: "gyogs" +0xad54: "gyon" +0xad55: "gyonj" +0xad56: "gyonh" +0xad57: "gyod" +0xad58: "gyol" +0xad59: "gyolg" +0xad5a: "gyolm" +0xad5b: "gyolb" +0xad5c: "gyols" +0xad5d: "gyolt" +0xad5e: "gyolp" +0xad5f: "gyolh" +0xad60: "gyom" +0xad61: "gyob" +0xad62: "gyobs" +0xad63: "gyos" +0xad64: "gyoss" +0xad65: "gyong" +0xad66: "gyoj" +0xad67: "gyoc" +0xad68: "gyok" +0xad69: "gyot" +0xad6a: "gyop" +0xad6b: "gyoh" +0xad6c: "gu" +0xad6d: "gug" +0xad6e: "gugg" +0xad6f: "gugs" +0xad70: "gun" +0xad71: "gunj" +0xad72: "gunh" +0xad73: "gud" +0xad74: "gul" +0xad75: "gulg" +0xad76: "gulm" +0xad77: "gulb" +0xad78: "guls" +0xad79: "gult" +0xad7a: "gulp" +0xad7b: "gulh" +0xad7c: "gum" +0xad7d: "gub" +0xad7e: "gubs" +0xad7f: "gus" +0xad80: "guss" +0xad81: "gung" +0xad82: "guj" +0xad83: "guc" +0xad84: "guk" +0xad85: "gut" +0xad86: "gup" +0xad87: "guh" +0xad88: "gweo" +0xad89: "gweog" +0xad8a: "gweogg" +0xad8b: "gweogs" +0xad8c: "gweon" +0xad8d: "gweonj" +0xad8e: "gweonh" +0xad8f: "gweod" +0xad90: "gweol" +0xad91: "gweolg" +0xad92: "gweolm" +0xad93: "gweolb" +0xad94: "gweols" +0xad95: "gweolt" +0xad96: "gweolp" +0xad97: "gweolh" +0xad98: "gweom" +0xad99: "gweob" +0xad9a: "gweobs" +0xad9b: "gweos" +0xad9c: "gweoss" +0xad9d: "gweong" +0xad9e: "gweoj" +0xad9f: "gweoc" +0xada0: "gweok" +0xada1: "gweot" +0xada2: "gweop" +0xada3: "gweoh" +0xada4: "gwe" +0xada5: "gweg" +0xada6: "gwegg" +0xada7: "gwegs" +0xada8: "gwen" +0xada9: "gwenj" +0xadaa: "gwenh" +0xadab: "gwed" +0xadac: "gwel" +0xadad: "gwelg" +0xadae: "gwelm" +0xadaf: "gwelb" +0xadb0: "gwels" +0xadb1: "gwelt" +0xadb2: "gwelp" +0xadb3: "gwelh" +0xadb4: "gwem" +0xadb5: "gweb" +0xadb6: "gwebs" +0xadb7: "gwes" +0xadb8: "gwess" +0xadb9: "gweng" +0xadba: "gwej" +0xadbb: "gwec" +0xadbc: "gwek" +0xadbd: "gwet" +0xadbe: "gwep" +0xadbf: "gweh" +0xadc0: "gwi" +0xadc1: "gwig" +0xadc2: "gwigg" +0xadc3: "gwigs" +0xadc4: "gwin" +0xadc5: "gwinj" +0xadc6: "gwinh" +0xadc7: "gwid" +0xadc8: "gwil" +0xadc9: "gwilg" +0xadca: "gwilm" +0xadcb: "gwilb" +0xadcc: "gwils" +0xadcd: "gwilt" +0xadce: "gwilp" +0xadcf: "gwilh" +0xadd0: "gwim" +0xadd1: "gwib" +0xadd2: "gwibs" +0xadd3: "gwis" +0xadd4: "gwiss" +0xadd5: "gwing" +0xadd6: "gwij" +0xadd7: "gwic" +0xadd8: "gwik" +0xadd9: "gwit" +0xadda: "gwip" +0xaddb: "gwih" +0xaddc: "gyu" +0xaddd: "gyug" +0xadde: "gyugg" +0xaddf: "gyugs" +0xade0: "gyun" +0xade1: "gyunj" +0xade2: "gyunh" +0xade3: "gyud" +0xade4: "gyul" +0xade5: "gyulg" +0xade6: "gyulm" +0xade7: "gyulb" +0xade8: "gyuls" +0xade9: "gyult" +0xadea: "gyulp" +0xadeb: "gyulh" +0xadec: "gyum" +0xaded: "gyub" +0xadee: "gyubs" +0xadef: "gyus" +0xadf0: "gyuss" +0xadf1: "gyung" +0xadf2: "gyuj" +0xadf3: "gyuc" +0xadf4: "gyuk" +0xadf5: "gyut" +0xadf6: "gyup" +0xadf7: "gyuh" +0xadf8: "geu" +0xadf9: "geug" +0xadfa: "geugg" +0xadfb: "geugs" +0xadfc: "geun" +0xadfd: "geunj" +0xadfe: "geunh" +0xadff: "geud" +/* x0ae */ +0xae00: "geul" +0xae01: "geulg" +0xae02: "geulm" +0xae03: "geulb" +0xae04: "geuls" +0xae05: "geult" +0xae06: "geulp" +0xae07: "geulh" +0xae08: "geum" +0xae09: "geub" +0xae0a: "geubs" +0xae0b: "geus" +0xae0c: "geuss" +0xae0d: "geung" +0xae0e: "geuj" +0xae0f: "geuc" +0xae10: "geuk" +0xae11: "geut" +0xae12: "geup" +0xae13: "geuh" +0xae14: "gyi" +0xae15: "gyig" +0xae16: "gyigg" +0xae17: "gyigs" +0xae18: "gyin" +0xae19: "gyinj" +0xae1a: "gyinh" +0xae1b: "gyid" +0xae1c: "gyil" +0xae1d: "gyilg" +0xae1e: "gyilm" +0xae1f: "gyilb" +0xae20: "gyils" +0xae21: "gyilt" +0xae22: "gyilp" +0xae23: "gyilh" +0xae24: "gyim" +0xae25: "gyib" +0xae26: "gyibs" +0xae27: "gyis" +0xae28: "gyiss" +0xae29: "gying" +0xae2a: "gyij" +0xae2b: "gyic" +0xae2c: "gyik" +0xae2d: "gyit" +0xae2e: "gyip" +0xae2f: "gyih" +0xae30: "gi" +0xae31: "gig" +0xae32: "gigg" +0xae33: "gigs" +0xae34: "gin" +0xae35: "ginj" +0xae36: "ginh" +0xae37: "gid" +0xae38: "gil" +0xae39: "gilg" +0xae3a: "gilm" +0xae3b: "gilb" +0xae3c: "gils" +0xae3d: "gilt" +0xae3e: "gilp" +0xae3f: "gilh" +0xae40: "gim" +0xae41: "gib" +0xae42: "gibs" +0xae43: "gis" +0xae44: "giss" +0xae45: "ging" +0xae46: "gij" +0xae47: "gic" +0xae48: "gik" +0xae49: "git" +0xae4a: "gip" +0xae4b: "gih" +0xae4c: "gga" +0xae4d: "ggag" +0xae4e: "ggagg" +0xae4f: "ggags" +0xae50: "ggan" +0xae51: "gganj" +0xae52: "gganh" +0xae53: "ggad" +0xae54: "ggal" +0xae55: "ggalg" +0xae56: "ggalm" +0xae57: "ggalb" +0xae58: "ggals" +0xae59: "ggalt" +0xae5a: "ggalp" +0xae5b: "ggalh" +0xae5c: "ggam" +0xae5d: "ggab" +0xae5e: "ggabs" +0xae5f: "ggas" +0xae60: "ggass" +0xae61: "ggang" +0xae62: "ggaj" +0xae63: "ggac" +0xae64: "ggak" +0xae65: "ggat" +0xae66: "ggap" +0xae67: "ggah" +0xae68: "ggae" +0xae69: "ggaeg" +0xae6a: "ggaegg" +0xae6b: "ggaegs" +0xae6c: "ggaen" +0xae6d: "ggaenj" +0xae6e: "ggaenh" +0xae6f: "ggaed" +0xae70: "ggael" +0xae71: "ggaelg" +0xae72: "ggaelm" +0xae73: "ggaelb" +0xae74: "ggaels" +0xae75: "ggaelt" +0xae76: "ggaelp" +0xae77: "ggaelh" +0xae78: "ggaem" +0xae79: "ggaeb" +0xae7a: "ggaebs" +0xae7b: "ggaes" +0xae7c: "ggaess" +0xae7d: "ggaeng" +0xae7e: "ggaej" +0xae7f: "ggaec" +0xae80: "ggaek" +0xae81: "ggaet" +0xae82: "ggaep" +0xae83: "ggaeh" +0xae84: "ggya" +0xae85: "ggyag" +0xae86: "ggyagg" +0xae87: "ggyags" +0xae88: "ggyan" +0xae89: "ggyanj" +0xae8a: "ggyanh" +0xae8b: "ggyad" +0xae8c: "ggyal" +0xae8d: "ggyalg" +0xae8e: "ggyalm" +0xae8f: "ggyalb" +0xae90: "ggyals" +0xae91: "ggyalt" +0xae92: "ggyalp" +0xae93: "ggyalh" +0xae94: "ggyam" +0xae95: "ggyab" +0xae96: "ggyabs" +0xae97: "ggyas" +0xae98: "ggyass" +0xae99: "ggyang" +0xae9a: "ggyaj" +0xae9b: "ggyac" +0xae9c: "ggyak" +0xae9d: "ggyat" +0xae9e: "ggyap" +0xae9f: "ggyah" +0xaea0: "ggyae" +0xaea1: "ggyaeg" +0xaea2: "ggyaegg" +0xaea3: "ggyaegs" +0xaea4: "ggyaen" +0xaea5: "ggyaenj" +0xaea6: "ggyaenh" +0xaea7: "ggyaed" +0xaea8: "ggyael" +0xaea9: "ggyaelg" +0xaeaa: "ggyaelm" +0xaeab: "ggyaelb" +0xaeac: "ggyaels" +0xaead: "ggyaelt" +0xaeae: "ggyaelp" +0xaeaf: "ggyaelh" +0xaeb0: "ggyaem" +0xaeb1: "ggyaeb" +0xaeb2: "ggyaebs" +0xaeb3: "ggyaes" +0xaeb4: "ggyaess" +0xaeb5: "ggyaeng" +0xaeb6: "ggyaej" +0xaeb7: "ggyaec" +0xaeb8: "ggyaek" +0xaeb9: "ggyaet" +0xaeba: "ggyaep" +0xaebb: "ggyaeh" +0xaebc: "ggeo" +0xaebd: "ggeog" +0xaebe: "ggeogg" +0xaebf: "ggeogs" +0xaec0: "ggeon" +0xaec1: "ggeonj" +0xaec2: "ggeonh" +0xaec3: "ggeod" +0xaec4: "ggeol" +0xaec5: "ggeolg" +0xaec6: "ggeolm" +0xaec7: "ggeolb" +0xaec8: "ggeols" +0xaec9: "ggeolt" +0xaeca: "ggeolp" +0xaecb: "ggeolh" +0xaecc: "ggeom" +0xaecd: "ggeob" +0xaece: "ggeobs" +0xaecf: "ggeos" +0xaed0: "ggeoss" +0xaed1: "ggeong" +0xaed2: "ggeoj" +0xaed3: "ggeoc" +0xaed4: "ggeok" +0xaed5: "ggeot" +0xaed6: "ggeop" +0xaed7: "ggeoh" +0xaed8: "gge" +0xaed9: "ggeg" +0xaeda: "ggegg" +0xaedb: "ggegs" +0xaedc: "ggen" +0xaedd: "ggenj" +0xaede: "ggenh" +0xaedf: "gged" +0xaee0: "ggel" +0xaee1: "ggelg" +0xaee2: "ggelm" +0xaee3: "ggelb" +0xaee4: "ggels" +0xaee5: "ggelt" +0xaee6: "ggelp" +0xaee7: "ggelh" +0xaee8: "ggem" +0xaee9: "ggeb" +0xaeea: "ggebs" +0xaeeb: "gges" +0xaeec: "ggess" +0xaeed: "ggeng" +0xaeee: "ggej" +0xaeef: "ggec" +0xaef0: "ggek" +0xaef1: "gget" +0xaef2: "ggep" +0xaef3: "ggeh" +0xaef4: "ggyeo" +0xaef5: "ggyeog" +0xaef6: "ggyeogg" +0xaef7: "ggyeogs" +0xaef8: "ggyeon" +0xaef9: "ggyeonj" +0xaefa: "ggyeonh" +0xaefb: "ggyeod" +0xaefc: "ggyeol" +0xaefd: "ggyeolg" +0xaefe: "ggyeolm" +0xaeff: "ggyeolb" +/* x0af */ +0xaf00: "ggyeols" +0xaf01: "ggyeolt" +0xaf02: "ggyeolp" +0xaf03: "ggyeolh" +0xaf04: "ggyeom" +0xaf05: "ggyeob" +0xaf06: "ggyeobs" +0xaf07: "ggyeos" +0xaf08: "ggyeoss" +0xaf09: "ggyeong" +0xaf0a: "ggyeoj" +0xaf0b: "ggyeoc" +0xaf0c: "ggyeok" +0xaf0d: "ggyeot" +0xaf0e: "ggyeop" +0xaf0f: "ggyeoh" +0xaf10: "ggye" +0xaf11: "ggyeg" +0xaf12: "ggyegg" +0xaf13: "ggyegs" +0xaf14: "ggyen" +0xaf15: "ggyenj" +0xaf16: "ggyenh" +0xaf17: "ggyed" +0xaf18: "ggyel" +0xaf19: "ggyelg" +0xaf1a: "ggyelm" +0xaf1b: "ggyelb" +0xaf1c: "ggyels" +0xaf1d: "ggyelt" +0xaf1e: "ggyelp" +0xaf1f: "ggyelh" +0xaf20: "ggyem" +0xaf21: "ggyeb" +0xaf22: "ggyebs" +0xaf23: "ggyes" +0xaf24: "ggyess" +0xaf25: "ggyeng" +0xaf26: "ggyej" +0xaf27: "ggyec" +0xaf28: "ggyek" +0xaf29: "ggyet" +0xaf2a: "ggyep" +0xaf2b: "ggyeh" +0xaf2c: "ggo" +0xaf2d: "ggog" +0xaf2e: "ggogg" +0xaf2f: "ggogs" +0xaf30: "ggon" +0xaf31: "ggonj" +0xaf32: "ggonh" +0xaf33: "ggod" +0xaf34: "ggol" +0xaf35: "ggolg" +0xaf36: "ggolm" +0xaf37: "ggolb" +0xaf38: "ggols" +0xaf39: "ggolt" +0xaf3a: "ggolp" +0xaf3b: "ggolh" +0xaf3c: "ggom" +0xaf3d: "ggob" +0xaf3e: "ggobs" +0xaf3f: "ggos" +0xaf40: "ggoss" +0xaf41: "ggong" +0xaf42: "ggoj" +0xaf43: "ggoc" +0xaf44: "ggok" +0xaf45: "ggot" +0xaf46: "ggop" +0xaf47: "ggoh" +0xaf48: "ggwa" +0xaf49: "ggwag" +0xaf4a: "ggwagg" +0xaf4b: "ggwags" +0xaf4c: "ggwan" +0xaf4d: "ggwanj" +0xaf4e: "ggwanh" +0xaf4f: "ggwad" +0xaf50: "ggwal" +0xaf51: "ggwalg" +0xaf52: "ggwalm" +0xaf53: "ggwalb" +0xaf54: "ggwals" +0xaf55: "ggwalt" +0xaf56: "ggwalp" +0xaf57: "ggwalh" +0xaf58: "ggwam" +0xaf59: "ggwab" +0xaf5a: "ggwabs" +0xaf5b: "ggwas" +0xaf5c: "ggwass" +0xaf5d: "ggwang" +0xaf5e: "ggwaj" +0xaf5f: "ggwac" +0xaf60: "ggwak" +0xaf61: "ggwat" +0xaf62: "ggwap" +0xaf63: "ggwah" +0xaf64: "ggwae" +0xaf65: "ggwaeg" +0xaf66: "ggwaegg" +0xaf67: "ggwaegs" +0xaf68: "ggwaen" +0xaf69: "ggwaenj" +0xaf6a: "ggwaenh" +0xaf6b: "ggwaed" +0xaf6c: "ggwael" +0xaf6d: "ggwaelg" +0xaf6e: "ggwaelm" +0xaf6f: "ggwaelb" +0xaf70: "ggwaels" +0xaf71: "ggwaelt" +0xaf72: "ggwaelp" +0xaf73: "ggwaelh" +0xaf74: "ggwaem" +0xaf75: "ggwaeb" +0xaf76: "ggwaebs" +0xaf77: "ggwaes" +0xaf78: "ggwaess" +0xaf79: "ggwaeng" +0xaf7a: "ggwaej" +0xaf7b: "ggwaec" +0xaf7c: "ggwaek" +0xaf7d: "ggwaet" +0xaf7e: "ggwaep" +0xaf7f: "ggwaeh" +0xaf80: "ggoe" +0xaf81: "ggoeg" +0xaf82: "ggoegg" +0xaf83: "ggoegs" +0xaf84: "ggoen" +0xaf85: "ggoenj" +0xaf86: "ggoenh" +0xaf87: "ggoed" +0xaf88: "ggoel" +0xaf89: "ggoelg" +0xaf8a: "ggoelm" +0xaf8b: "ggoelb" +0xaf8c: "ggoels" +0xaf8d: "ggoelt" +0xaf8e: "ggoelp" +0xaf8f: "ggoelh" +0xaf90: "ggoem" +0xaf91: "ggoeb" +0xaf92: "ggoebs" +0xaf93: "ggoes" +0xaf94: "ggoess" +0xaf95: "ggoeng" +0xaf96: "ggoej" +0xaf97: "ggoec" +0xaf98: "ggoek" +0xaf99: "ggoet" +0xaf9a: "ggoep" +0xaf9b: "ggoeh" +0xaf9c: "ggyo" +0xaf9d: "ggyog" +0xaf9e: "ggyogg" +0xaf9f: "ggyogs" +0xafa0: "ggyon" +0xafa1: "ggyonj" +0xafa2: "ggyonh" +0xafa3: "ggyod" +0xafa4: "ggyol" +0xafa5: "ggyolg" +0xafa6: "ggyolm" +0xafa7: "ggyolb" +0xafa8: "ggyols" +0xafa9: "ggyolt" +0xafaa: "ggyolp" +0xafab: "ggyolh" +0xafac: "ggyom" +0xafad: "ggyob" +0xafae: "ggyobs" +0xafaf: "ggyos" +0xafb0: "ggyoss" +0xafb1: "ggyong" +0xafb2: "ggyoj" +0xafb3: "ggyoc" +0xafb4: "ggyok" +0xafb5: "ggyot" +0xafb6: "ggyop" +0xafb7: "ggyoh" +0xafb8: "ggu" +0xafb9: "ggug" +0xafba: "ggugg" +0xafbb: "ggugs" +0xafbc: "ggun" +0xafbd: "ggunj" +0xafbe: "ggunh" +0xafbf: "ggud" +0xafc0: "ggul" +0xafc1: "ggulg" +0xafc2: "ggulm" +0xafc3: "ggulb" +0xafc4: "gguls" +0xafc5: "ggult" +0xafc6: "ggulp" +0xafc7: "ggulh" +0xafc8: "ggum" +0xafc9: "ggub" +0xafca: "ggubs" +0xafcb: "ggus" +0xafcc: "gguss" +0xafcd: "ggung" +0xafce: "gguj" +0xafcf: "gguc" +0xafd0: "gguk" +0xafd1: "ggut" +0xafd2: "ggup" +0xafd3: "gguh" +0xafd4: "ggweo" +0xafd5: "ggweog" +0xafd6: "ggweogg" +0xafd7: "ggweogs" +0xafd8: "ggweon" +0xafd9: "ggweonj" +0xafda: "ggweonh" +0xafdb: "ggweod" +0xafdc: "ggweol" +0xafdd: "ggweolg" +0xafde: "ggweolm" +0xafdf: "ggweolb" +0xafe0: "ggweols" +0xafe1: "ggweolt" +0xafe2: "ggweolp" +0xafe3: "ggweolh" +0xafe4: "ggweom" +0xafe5: "ggweob" +0xafe6: "ggweobs" +0xafe7: "ggweos" +0xafe8: "ggweoss" +0xafe9: "ggweong" +0xafea: "ggweoj" +0xafeb: "ggweoc" +0xafec: "ggweok" +0xafed: "ggweot" +0xafee: "ggweop" +0xafef: "ggweoh" +0xaff0: "ggwe" +0xaff1: "ggweg" +0xaff2: "ggwegg" +0xaff3: "ggwegs" +0xaff4: "ggwen" +0xaff5: "ggwenj" +0xaff6: "ggwenh" +0xaff7: "ggwed" +0xaff8: "ggwel" +0xaff9: "ggwelg" +0xaffa: "ggwelm" +0xaffb: "ggwelb" +0xaffc: "ggwels" +0xaffd: "ggwelt" +0xaffe: "ggwelp" +0xafff: "ggwelh" +/* x0b0 */ +0xb000: "ggwem" +0xb001: "ggweb" +0xb002: "ggwebs" +0xb003: "ggwes" +0xb004: "ggwess" +0xb005: "ggweng" +0xb006: "ggwej" +0xb007: "ggwec" +0xb008: "ggwek" +0xb009: "ggwet" +0xb00a: "ggwep" +0xb00b: "ggweh" +0xb00c: "ggwi" +0xb00d: "ggwig" +0xb00e: "ggwigg" +0xb00f: "ggwigs" +0xb010: "ggwin" +0xb011: "ggwinj" +0xb012: "ggwinh" +0xb013: "ggwid" +0xb014: "ggwil" +0xb015: "ggwilg" +0xb016: "ggwilm" +0xb017: "ggwilb" +0xb018: "ggwils" +0xb019: "ggwilt" +0xb01a: "ggwilp" +0xb01b: "ggwilh" +0xb01c: "ggwim" +0xb01d: "ggwib" +0xb01e: "ggwibs" +0xb01f: "ggwis" +0xb020: "ggwiss" +0xb021: "ggwing" +0xb022: "ggwij" +0xb023: "ggwic" +0xb024: "ggwik" +0xb025: "ggwit" +0xb026: "ggwip" +0xb027: "ggwih" +0xb028: "ggyu" +0xb029: "ggyug" +0xb02a: "ggyugg" +0xb02b: "ggyugs" +0xb02c: "ggyun" +0xb02d: "ggyunj" +0xb02e: "ggyunh" +0xb02f: "ggyud" +0xb030: "ggyul" +0xb031: "ggyulg" +0xb032: "ggyulm" +0xb033: "ggyulb" +0xb034: "ggyuls" +0xb035: "ggyult" +0xb036: "ggyulp" +0xb037: "ggyulh" +0xb038: "ggyum" +0xb039: "ggyub" +0xb03a: "ggyubs" +0xb03b: "ggyus" +0xb03c: "ggyuss" +0xb03d: "ggyung" +0xb03e: "ggyuj" +0xb03f: "ggyuc" +0xb040: "ggyuk" +0xb041: "ggyut" +0xb042: "ggyup" +0xb043: "ggyuh" +0xb044: "ggeu" +0xb045: "ggeug" +0xb046: "ggeugg" +0xb047: "ggeugs" +0xb048: "ggeun" +0xb049: "ggeunj" +0xb04a: "ggeunh" +0xb04b: "ggeud" +0xb04c: "ggeul" +0xb04d: "ggeulg" +0xb04e: "ggeulm" +0xb04f: "ggeulb" +0xb050: "ggeuls" +0xb051: "ggeult" +0xb052: "ggeulp" +0xb053: "ggeulh" +0xb054: "ggeum" +0xb055: "ggeub" +0xb056: "ggeubs" +0xb057: "ggeus" +0xb058: "ggeuss" +0xb059: "ggeung" +0xb05a: "ggeuj" +0xb05b: "ggeuc" +0xb05c: "ggeuk" +0xb05d: "ggeut" +0xb05e: "ggeup" +0xb05f: "ggeuh" +0xb060: "ggyi" +0xb061: "ggyig" +0xb062: "ggyigg" +0xb063: "ggyigs" +0xb064: "ggyin" +0xb065: "ggyinj" +0xb066: "ggyinh" +0xb067: "ggyid" +0xb068: "ggyil" +0xb069: "ggyilg" +0xb06a: "ggyilm" +0xb06b: "ggyilb" +0xb06c: "ggyils" +0xb06d: "ggyilt" +0xb06e: "ggyilp" +0xb06f: "ggyilh" +0xb070: "ggyim" +0xb071: "ggyib" +0xb072: "ggyibs" +0xb073: "ggyis" +0xb074: "ggyiss" +0xb075: "ggying" +0xb076: "ggyij" +0xb077: "ggyic" +0xb078: "ggyik" +0xb079: "ggyit" +0xb07a: "ggyip" +0xb07b: "ggyih" +0xb07c: "ggi" +0xb07d: "ggig" +0xb07e: "ggigg" +0xb07f: "ggigs" +0xb080: "ggin" +0xb081: "gginj" +0xb082: "gginh" +0xb083: "ggid" +0xb084: "ggil" +0xb085: "ggilg" +0xb086: "ggilm" +0xb087: "ggilb" +0xb088: "ggils" +0xb089: "ggilt" +0xb08a: "ggilp" +0xb08b: "ggilh" +0xb08c: "ggim" +0xb08d: "ggib" +0xb08e: "ggibs" +0xb08f: "ggis" +0xb090: "ggiss" +0xb091: "gging" +0xb092: "ggij" +0xb093: "ggic" +0xb094: "ggik" +0xb095: "ggit" +0xb096: "ggip" +0xb097: "ggih" +0xb098: "na" +0xb099: "nag" +0xb09a: "nagg" +0xb09b: "nags" +0xb09c: "nan" +0xb09d: "nanj" +0xb09e: "nanh" +0xb09f: "nad" +0xb0a0: "nal" +0xb0a1: "nalg" +0xb0a2: "nalm" +0xb0a3: "nalb" +0xb0a4: "nals" +0xb0a5: "nalt" +0xb0a6: "nalp" +0xb0a7: "nalh" +0xb0a8: "nam" +0xb0a9: "nab" +0xb0aa: "nabs" +0xb0ab: "nas" +0xb0ac: "nass" +0xb0ad: "nang" +0xb0ae: "naj" +0xb0af: "nac" +0xb0b0: "nak" +0xb0b1: "nat" +0xb0b2: "nap" +0xb0b3: "nah" +0xb0b4: "nae" +0xb0b5: "naeg" +0xb0b6: "naegg" +0xb0b7: "naegs" +0xb0b8: "naen" +0xb0b9: "naenj" +0xb0ba: "naenh" +0xb0bb: "naed" +0xb0bc: "nael" +0xb0bd: "naelg" +0xb0be: "naelm" +0xb0bf: "naelb" +0xb0c0: "naels" +0xb0c1: "naelt" +0xb0c2: "naelp" +0xb0c3: "naelh" +0xb0c4: "naem" +0xb0c5: "naeb" +0xb0c6: "naebs" +0xb0c7: "naes" +0xb0c8: "naess" +0xb0c9: "naeng" +0xb0ca: "naej" +0xb0cb: "naec" +0xb0cc: "naek" +0xb0cd: "naet" +0xb0ce: "naep" +0xb0cf: "naeh" +0xb0d0: "nya" +0xb0d1: "nyag" +0xb0d2: "nyagg" +0xb0d3: "nyags" +0xb0d4: "nyan" +0xb0d5: "nyanj" +0xb0d6: "nyanh" +0xb0d7: "nyad" +0xb0d8: "nyal" +0xb0d9: "nyalg" +0xb0da: "nyalm" +0xb0db: "nyalb" +0xb0dc: "nyals" +0xb0dd: "nyalt" +0xb0de: "nyalp" +0xb0df: "nyalh" +0xb0e0: "nyam" +0xb0e1: "nyab" +0xb0e2: "nyabs" +0xb0e3: "nyas" +0xb0e4: "nyass" +0xb0e5: "nyang" +0xb0e6: "nyaj" +0xb0e7: "nyac" +0xb0e8: "nyak" +0xb0e9: "nyat" +0xb0ea: "nyap" +0xb0eb: "nyah" +0xb0ec: "nyae" +0xb0ed: "nyaeg" +0xb0ee: "nyaegg" +0xb0ef: "nyaegs" +0xb0f0: "nyaen" +0xb0f1: "nyaenj" +0xb0f2: "nyaenh" +0xb0f3: "nyaed" +0xb0f4: "nyael" +0xb0f5: "nyaelg" +0xb0f6: "nyaelm" +0xb0f7: "nyaelb" +0xb0f8: "nyaels" +0xb0f9: "nyaelt" +0xb0fa: "nyaelp" +0xb0fb: "nyaelh" +0xb0fc: "nyaem" +0xb0fd: "nyaeb" +0xb0fe: "nyaebs" +0xb0ff: "nyaes" +/* x0b1 */ +0xb100: "nyaess" +0xb101: "nyaeng" +0xb102: "nyaej" +0xb103: "nyaec" +0xb104: "nyaek" +0xb105: "nyaet" +0xb106: "nyaep" +0xb107: "nyaeh" +0xb108: "neo" +0xb109: "neog" +0xb10a: "neogg" +0xb10b: "neogs" +0xb10c: "neon" +0xb10d: "neonj" +0xb10e: "neonh" +0xb10f: "neod" +0xb110: "neol" +0xb111: "neolg" +0xb112: "neolm" +0xb113: "neolb" +0xb114: "neols" +0xb115: "neolt" +0xb116: "neolp" +0xb117: "neolh" +0xb118: "neom" +0xb119: "neob" +0xb11a: "neobs" +0xb11b: "neos" +0xb11c: "neoss" +0xb11d: "neong" +0xb11e: "neoj" +0xb11f: "neoc" +0xb120: "neok" +0xb121: "neot" +0xb122: "neop" +0xb123: "neoh" +0xb124: "ne" +0xb125: "neg" +0xb126: "negg" +0xb127: "negs" +0xb128: "nen" +0xb129: "nenj" +0xb12a: "nenh" +0xb12b: "ned" +0xb12c: "nel" +0xb12d: "nelg" +0xb12e: "nelm" +0xb12f: "nelb" +0xb130: "nels" +0xb131: "nelt" +0xb132: "nelp" +0xb133: "nelh" +0xb134: "nem" +0xb135: "neb" +0xb136: "nebs" +0xb137: "nes" +0xb138: "ness" +0xb139: "neng" +0xb13a: "nej" +0xb13b: "nec" +0xb13c: "nek" +0xb13d: "net" +0xb13e: "nep" +0xb13f: "neh" +0xb140: "nyeo" +0xb141: "nyeog" +0xb142: "nyeogg" +0xb143: "nyeogs" +0xb144: "nyeon" +0xb145: "nyeonj" +0xb146: "nyeonh" +0xb147: "nyeod" +0xb148: "nyeol" +0xb149: "nyeolg" +0xb14a: "nyeolm" +0xb14b: "nyeolb" +0xb14c: "nyeols" +0xb14d: "nyeolt" +0xb14e: "nyeolp" +0xb14f: "nyeolh" +0xb150: "nyeom" +0xb151: "nyeob" +0xb152: "nyeobs" +0xb153: "nyeos" +0xb154: "nyeoss" +0xb155: "nyeong" +0xb156: "nyeoj" +0xb157: "nyeoc" +0xb158: "nyeok" +0xb159: "nyeot" +0xb15a: "nyeop" +0xb15b: "nyeoh" +0xb15c: "nye" +0xb15d: "nyeg" +0xb15e: "nyegg" +0xb15f: "nyegs" +0xb160: "nyen" +0xb161: "nyenj" +0xb162: "nyenh" +0xb163: "nyed" +0xb164: "nyel" +0xb165: "nyelg" +0xb166: "nyelm" +0xb167: "nyelb" +0xb168: "nyels" +0xb169: "nyelt" +0xb16a: "nyelp" +0xb16b: "nyelh" +0xb16c: "nyem" +0xb16d: "nyeb" +0xb16e: "nyebs" +0xb16f: "nyes" +0xb170: "nyess" +0xb171: "nyeng" +0xb172: "nyej" +0xb173: "nyec" +0xb174: "nyek" +0xb175: "nyet" +0xb176: "nyep" +0xb177: "nyeh" +0xb178: "no" +0xb179: "nog" +0xb17a: "nogg" +0xb17b: "nogs" +0xb17c: "non" +0xb17d: "nonj" +0xb17e: "nonh" +0xb17f: "nod" +0xb180: "nol" +0xb181: "nolg" +0xb182: "nolm" +0xb183: "nolb" +0xb184: "nols" +0xb185: "nolt" +0xb186: "nolp" +0xb187: "nolh" +0xb188: "nom" +0xb189: "nob" +0xb18a: "nobs" +0xb18b: "nos" +0xb18c: "noss" +0xb18d: "nong" +0xb18e: "noj" +0xb18f: "noc" +0xb190: "nok" +0xb191: "not" +0xb192: "nop" +0xb193: "noh" +0xb194: "nwa" +0xb195: "nwag" +0xb196: "nwagg" +0xb197: "nwags" +0xb198: "nwan" +0xb199: "nwanj" +0xb19a: "nwanh" +0xb19b: "nwad" +0xb19c: "nwal" +0xb19d: "nwalg" +0xb19e: "nwalm" +0xb19f: "nwalb" +0xb1a0: "nwals" +0xb1a1: "nwalt" +0xb1a2: "nwalp" +0xb1a3: "nwalh" +0xb1a4: "nwam" +0xb1a5: "nwab" +0xb1a6: "nwabs" +0xb1a7: "nwas" +0xb1a8: "nwass" +0xb1a9: "nwang" +0xb1aa: "nwaj" +0xb1ab: "nwac" +0xb1ac: "nwak" +0xb1ad: "nwat" +0xb1ae: "nwap" +0xb1af: "nwah" +0xb1b0: "nwae" +0xb1b1: "nwaeg" +0xb1b2: "nwaegg" +0xb1b3: "nwaegs" +0xb1b4: "nwaen" +0xb1b5: "nwaenj" +0xb1b6: "nwaenh" +0xb1b7: "nwaed" +0xb1b8: "nwael" +0xb1b9: "nwaelg" +0xb1ba: "nwaelm" +0xb1bb: "nwaelb" +0xb1bc: "nwaels" +0xb1bd: "nwaelt" +0xb1be: "nwaelp" +0xb1bf: "nwaelh" +0xb1c0: "nwaem" +0xb1c1: "nwaeb" +0xb1c2: "nwaebs" +0xb1c3: "nwaes" +0xb1c4: "nwaess" +0xb1c5: "nwaeng" +0xb1c6: "nwaej" +0xb1c7: "nwaec" +0xb1c8: "nwaek" +0xb1c9: "nwaet" +0xb1ca: "nwaep" +0xb1cb: "nwaeh" +0xb1cc: "noe" +0xb1cd: "noeg" +0xb1ce: "noegg" +0xb1cf: "noegs" +0xb1d0: "noen" +0xb1d1: "noenj" +0xb1d2: "noenh" +0xb1d3: "noed" +0xb1d4: "noel" +0xb1d5: "noelg" +0xb1d6: "noelm" +0xb1d7: "noelb" +0xb1d8: "noels" +0xb1d9: "noelt" +0xb1da: "noelp" +0xb1db: "noelh" +0xb1dc: "noem" +0xb1dd: "noeb" +0xb1de: "noebs" +0xb1df: "noes" +0xb1e0: "noess" +0xb1e1: "noeng" +0xb1e2: "noej" +0xb1e3: "noec" +0xb1e4: "noek" +0xb1e5: "noet" +0xb1e6: "noep" +0xb1e7: "noeh" +0xb1e8: "nyo" +0xb1e9: "nyog" +0xb1ea: "nyogg" +0xb1eb: "nyogs" +0xb1ec: "nyon" +0xb1ed: "nyonj" +0xb1ee: "nyonh" +0xb1ef: "nyod" +0xb1f0: "nyol" +0xb1f1: "nyolg" +0xb1f2: "nyolm" +0xb1f3: "nyolb" +0xb1f4: "nyols" +0xb1f5: "nyolt" +0xb1f6: "nyolp" +0xb1f7: "nyolh" +0xb1f8: "nyom" +0xb1f9: "nyob" +0xb1fa: "nyobs" +0xb1fb: "nyos" +0xb1fc: "nyoss" +0xb1fd: "nyong" +0xb1fe: "nyoj" +0xb1ff: "nyoc" +/* x0b2 */ +0xb200: "nyok" +0xb201: "nyot" +0xb202: "nyop" +0xb203: "nyoh" +0xb204: "nu" +0xb205: "nug" +0xb206: "nugg" +0xb207: "nugs" +0xb208: "nun" +0xb209: "nunj" +0xb20a: "nunh" +0xb20b: "nud" +0xb20c: "nul" +0xb20d: "nulg" +0xb20e: "nulm" +0xb20f: "nulb" +0xb210: "nuls" +0xb211: "nult" +0xb212: "nulp" +0xb213: "nulh" +0xb214: "num" +0xb215: "nub" +0xb216: "nubs" +0xb217: "nus" +0xb218: "nuss" +0xb219: "nung" +0xb21a: "nuj" +0xb21b: "nuc" +0xb21c: "nuk" +0xb21d: "nut" +0xb21e: "nup" +0xb21f: "nuh" +0xb220: "nweo" +0xb221: "nweog" +0xb222: "nweogg" +0xb223: "nweogs" +0xb224: "nweon" +0xb225: "nweonj" +0xb226: "nweonh" +0xb227: "nweod" +0xb228: "nweol" +0xb229: "nweolg" +0xb22a: "nweolm" +0xb22b: "nweolb" +0xb22c: "nweols" +0xb22d: "nweolt" +0xb22e: "nweolp" +0xb22f: "nweolh" +0xb230: "nweom" +0xb231: "nweob" +0xb232: "nweobs" +0xb233: "nweos" +0xb234: "nweoss" +0xb235: "nweong" +0xb236: "nweoj" +0xb237: "nweoc" +0xb238: "nweok" +0xb239: "nweot" +0xb23a: "nweop" +0xb23b: "nweoh" +0xb23c: "nwe" +0xb23d: "nweg" +0xb23e: "nwegg" +0xb23f: "nwegs" +0xb240: "nwen" +0xb241: "nwenj" +0xb242: "nwenh" +0xb243: "nwed" +0xb244: "nwel" +0xb245: "nwelg" +0xb246: "nwelm" +0xb247: "nwelb" +0xb248: "nwels" +0xb249: "nwelt" +0xb24a: "nwelp" +0xb24b: "nwelh" +0xb24c: "nwem" +0xb24d: "nweb" +0xb24e: "nwebs" +0xb24f: "nwes" +0xb250: "nwess" +0xb251: "nweng" +0xb252: "nwej" +0xb253: "nwec" +0xb254: "nwek" +0xb255: "nwet" +0xb256: "nwep" +0xb257: "nweh" +0xb258: "nwi" +0xb259: "nwig" +0xb25a: "nwigg" +0xb25b: "nwigs" +0xb25c: "nwin" +0xb25d: "nwinj" +0xb25e: "nwinh" +0xb25f: "nwid" +0xb260: "nwil" +0xb261: "nwilg" +0xb262: "nwilm" +0xb263: "nwilb" +0xb264: "nwils" +0xb265: "nwilt" +0xb266: "nwilp" +0xb267: "nwilh" +0xb268: "nwim" +0xb269: "nwib" +0xb26a: "nwibs" +0xb26b: "nwis" +0xb26c: "nwiss" +0xb26d: "nwing" +0xb26e: "nwij" +0xb26f: "nwic" +0xb270: "nwik" +0xb271: "nwit" +0xb272: "nwip" +0xb273: "nwih" +0xb274: "nyu" +0xb275: "nyug" +0xb276: "nyugg" +0xb277: "nyugs" +0xb278: "nyun" +0xb279: "nyunj" +0xb27a: "nyunh" +0xb27b: "nyud" +0xb27c: "nyul" +0xb27d: "nyulg" +0xb27e: "nyulm" +0xb27f: "nyulb" +0xb280: "nyuls" +0xb281: "nyult" +0xb282: "nyulp" +0xb283: "nyulh" +0xb284: "nyum" +0xb285: "nyub" +0xb286: "nyubs" +0xb287: "nyus" +0xb288: "nyuss" +0xb289: "nyung" +0xb28a: "nyuj" +0xb28b: "nyuc" +0xb28c: "nyuk" +0xb28d: "nyut" +0xb28e: "nyup" +0xb28f: "nyuh" +0xb290: "neu" +0xb291: "neug" +0xb292: "neugg" +0xb293: "neugs" +0xb294: "neun" +0xb295: "neunj" +0xb296: "neunh" +0xb297: "neud" +0xb298: "neul" +0xb299: "neulg" +0xb29a: "neulm" +0xb29b: "neulb" +0xb29c: "neuls" +0xb29d: "neult" +0xb29e: "neulp" +0xb29f: "neulh" +0xb2a0: "neum" +0xb2a1: "neub" +0xb2a2: "neubs" +0xb2a3: "neus" +0xb2a4: "neuss" +0xb2a5: "neung" +0xb2a6: "neuj" +0xb2a7: "neuc" +0xb2a8: "neuk" +0xb2a9: "neut" +0xb2aa: "neup" +0xb2ab: "neuh" +0xb2ac: "nyi" +0xb2ad: "nyig" +0xb2ae: "nyigg" +0xb2af: "nyigs" +0xb2b0: "nyin" +0xb2b1: "nyinj" +0xb2b2: "nyinh" +0xb2b3: "nyid" +0xb2b4: "nyil" +0xb2b5: "nyilg" +0xb2b6: "nyilm" +0xb2b7: "nyilb" +0xb2b8: "nyils" +0xb2b9: "nyilt" +0xb2ba: "nyilp" +0xb2bb: "nyilh" +0xb2bc: "nyim" +0xb2bd: "nyib" +0xb2be: "nyibs" +0xb2bf: "nyis" +0xb2c0: "nyiss" +0xb2c1: "nying" +0xb2c2: "nyij" +0xb2c3: "nyic" +0xb2c4: "nyik" +0xb2c5: "nyit" +0xb2c6: "nyip" +0xb2c7: "nyih" +0xb2c8: "ni" +0xb2c9: "nig" +0xb2ca: "nigg" +0xb2cb: "nigs" +0xb2cc: "nin" +0xb2cd: "ninj" +0xb2ce: "ninh" +0xb2cf: "nid" +0xb2d0: "nil" +0xb2d1: "nilg" +0xb2d2: "nilm" +0xb2d3: "nilb" +0xb2d4: "nils" +0xb2d5: "nilt" +0xb2d6: "nilp" +0xb2d7: "nilh" +0xb2d8: "nim" +0xb2d9: "nib" +0xb2da: "nibs" +0xb2db: "nis" +0xb2dc: "niss" +0xb2dd: "ning" +0xb2de: "nij" +0xb2df: "nic" +0xb2e0: "nik" +0xb2e1: "nit" +0xb2e2: "nip" +0xb2e3: "nih" +0xb2e4: "da" +0xb2e5: "dag" +0xb2e6: "dagg" +0xb2e7: "dags" +0xb2e8: "dan" +0xb2e9: "danj" +0xb2ea: "danh" +0xb2eb: "dad" +0xb2ec: "dal" +0xb2ed: "dalg" +0xb2ee: "dalm" +0xb2ef: "dalb" +0xb2f0: "dals" +0xb2f1: "dalt" +0xb2f2: "dalp" +0xb2f3: "dalh" +0xb2f4: "dam" +0xb2f5: "dab" +0xb2f6: "dabs" +0xb2f7: "das" +0xb2f8: "dass" +0xb2f9: "dang" +0xb2fa: "daj" +0xb2fb: "dac" +0xb2fc: "dak" +0xb2fd: "dat" +0xb2fe: "dap" +0xb2ff: "dah" +/* x0b3 */ +0xb300: "dae" +0xb301: "daeg" +0xb302: "daegg" +0xb303: "daegs" +0xb304: "daen" +0xb305: "daenj" +0xb306: "daenh" +0xb307: "daed" +0xb308: "dael" +0xb309: "daelg" +0xb30a: "daelm" +0xb30b: "daelb" +0xb30c: "daels" +0xb30d: "daelt" +0xb30e: "daelp" +0xb30f: "daelh" +0xb310: "daem" +0xb311: "daeb" +0xb312: "daebs" +0xb313: "daes" +0xb314: "daess" +0xb315: "daeng" +0xb316: "daej" +0xb317: "daec" +0xb318: "daek" +0xb319: "daet" +0xb31a: "daep" +0xb31b: "daeh" +0xb31c: "dya" +0xb31d: "dyag" +0xb31e: "dyagg" +0xb31f: "dyags" +0xb320: "dyan" +0xb321: "dyanj" +0xb322: "dyanh" +0xb323: "dyad" +0xb324: "dyal" +0xb325: "dyalg" +0xb326: "dyalm" +0xb327: "dyalb" +0xb328: "dyals" +0xb329: "dyalt" +0xb32a: "dyalp" +0xb32b: "dyalh" +0xb32c: "dyam" +0xb32d: "dyab" +0xb32e: "dyabs" +0xb32f: "dyas" +0xb330: "dyass" +0xb331: "dyang" +0xb332: "dyaj" +0xb333: "dyac" +0xb334: "dyak" +0xb335: "dyat" +0xb336: "dyap" +0xb337: "dyah" +0xb338: "dyae" +0xb339: "dyaeg" +0xb33a: "dyaegg" +0xb33b: "dyaegs" +0xb33c: "dyaen" +0xb33d: "dyaenj" +0xb33e: "dyaenh" +0xb33f: "dyaed" +0xb340: "dyael" +0xb341: "dyaelg" +0xb342: "dyaelm" +0xb343: "dyaelb" +0xb344: "dyaels" +0xb345: "dyaelt" +0xb346: "dyaelp" +0xb347: "dyaelh" +0xb348: "dyaem" +0xb349: "dyaeb" +0xb34a: "dyaebs" +0xb34b: "dyaes" +0xb34c: "dyaess" +0xb34d: "dyaeng" +0xb34e: "dyaej" +0xb34f: "dyaec" +0xb350: "dyaek" +0xb351: "dyaet" +0xb352: "dyaep" +0xb353: "dyaeh" +0xb354: "deo" +0xb355: "deog" +0xb356: "deogg" +0xb357: "deogs" +0xb358: "deon" +0xb359: "deonj" +0xb35a: "deonh" +0xb35b: "deod" +0xb35c: "deol" +0xb35d: "deolg" +0xb35e: "deolm" +0xb35f: "deolb" +0xb360: "deols" +0xb361: "deolt" +0xb362: "deolp" +0xb363: "deolh" +0xb364: "deom" +0xb365: "deob" +0xb366: "deobs" +0xb367: "deos" +0xb368: "deoss" +0xb369: "deong" +0xb36a: "deoj" +0xb36b: "deoc" +0xb36c: "deok" +0xb36d: "deot" +0xb36e: "deop" +0xb36f: "deoh" +0xb370: "de" +0xb371: "deg" +0xb372: "degg" +0xb373: "degs" +0xb374: "den" +0xb375: "denj" +0xb376: "denh" +0xb377: "ded" +0xb378: "del" +0xb379: "delg" +0xb37a: "delm" +0xb37b: "delb" +0xb37c: "dels" +0xb37d: "delt" +0xb37e: "delp" +0xb37f: "delh" +0xb380: "dem" +0xb381: "deb" +0xb382: "debs" +0xb383: "des" +0xb384: "dess" +0xb385: "deng" +0xb386: "dej" +0xb387: "dec" +0xb388: "dek" +0xb389: "det" +0xb38a: "dep" +0xb38b: "deh" +0xb38c: "dyeo" +0xb38d: "dyeog" +0xb38e: "dyeogg" +0xb38f: "dyeogs" +0xb390: "dyeon" +0xb391: "dyeonj" +0xb392: "dyeonh" +0xb393: "dyeod" +0xb394: "dyeol" +0xb395: "dyeolg" +0xb396: "dyeolm" +0xb397: "dyeolb" +0xb398: "dyeols" +0xb399: "dyeolt" +0xb39a: "dyeolp" +0xb39b: "dyeolh" +0xb39c: "dyeom" +0xb39d: "dyeob" +0xb39e: "dyeobs" +0xb39f: "dyeos" +0xb3a0: "dyeoss" +0xb3a1: "dyeong" +0xb3a2: "dyeoj" +0xb3a3: "dyeoc" +0xb3a4: "dyeok" +0xb3a5: "dyeot" +0xb3a6: "dyeop" +0xb3a7: "dyeoh" +0xb3a8: "dye" +0xb3a9: "dyeg" +0xb3aa: "dyegg" +0xb3ab: "dyegs" +0xb3ac: "dyen" +0xb3ad: "dyenj" +0xb3ae: "dyenh" +0xb3af: "dyed" +0xb3b0: "dyel" +0xb3b1: "dyelg" +0xb3b2: "dyelm" +0xb3b3: "dyelb" +0xb3b4: "dyels" +0xb3b5: "dyelt" +0xb3b6: "dyelp" +0xb3b7: "dyelh" +0xb3b8: "dyem" +0xb3b9: "dyeb" +0xb3ba: "dyebs" +0xb3bb: "dyes" +0xb3bc: "dyess" +0xb3bd: "dyeng" +0xb3be: "dyej" +0xb3bf: "dyec" +0xb3c0: "dyek" +0xb3c1: "dyet" +0xb3c2: "dyep" +0xb3c3: "dyeh" +0xb3c4: "do" +0xb3c5: "dog" +0xb3c6: "dogg" +0xb3c7: "dogs" +0xb3c8: "don" +0xb3c9: "donj" +0xb3ca: "donh" +0xb3cb: "dod" +0xb3cc: "dol" +0xb3cd: "dolg" +0xb3ce: "dolm" +0xb3cf: "dolb" +0xb3d0: "dols" +0xb3d1: "dolt" +0xb3d2: "dolp" +0xb3d3: "dolh" +0xb3d4: "dom" +0xb3d5: "dob" +0xb3d6: "dobs" +0xb3d7: "dos" +0xb3d8: "doss" +0xb3d9: "dong" +0xb3da: "doj" +0xb3db: "doc" +0xb3dc: "dok" +0xb3dd: "dot" +0xb3de: "dop" +0xb3df: "doh" +0xb3e0: "dwa" +0xb3e1: "dwag" +0xb3e2: "dwagg" +0xb3e3: "dwags" +0xb3e4: "dwan" +0xb3e5: "dwanj" +0xb3e6: "dwanh" +0xb3e7: "dwad" +0xb3e8: "dwal" +0xb3e9: "dwalg" +0xb3ea: "dwalm" +0xb3eb: "dwalb" +0xb3ec: "dwals" +0xb3ed: "dwalt" +0xb3ee: "dwalp" +0xb3ef: "dwalh" +0xb3f0: "dwam" +0xb3f1: "dwab" +0xb3f2: "dwabs" +0xb3f3: "dwas" +0xb3f4: "dwass" +0xb3f5: "dwang" +0xb3f6: "dwaj" +0xb3f7: "dwac" +0xb3f8: "dwak" +0xb3f9: "dwat" +0xb3fa: "dwap" +0xb3fb: "dwah" +0xb3fc: "dwae" +0xb3fd: "dwaeg" +0xb3fe: "dwaegg" +0xb3ff: "dwaegs" +/* x0b4 */ +0xb400: "dwaen" +0xb401: "dwaenj" +0xb402: "dwaenh" +0xb403: "dwaed" +0xb404: "dwael" +0xb405: "dwaelg" +0xb406: "dwaelm" +0xb407: "dwaelb" +0xb408: "dwaels" +0xb409: "dwaelt" +0xb40a: "dwaelp" +0xb40b: "dwaelh" +0xb40c: "dwaem" +0xb40d: "dwaeb" +0xb40e: "dwaebs" +0xb40f: "dwaes" +0xb410: "dwaess" +0xb411: "dwaeng" +0xb412: "dwaej" +0xb413: "dwaec" +0xb414: "dwaek" +0xb415: "dwaet" +0xb416: "dwaep" +0xb417: "dwaeh" +0xb418: "doe" +0xb419: "doeg" +0xb41a: "doegg" +0xb41b: "doegs" +0xb41c: "doen" +0xb41d: "doenj" +0xb41e: "doenh" +0xb41f: "doed" +0xb420: "doel" +0xb421: "doelg" +0xb422: "doelm" +0xb423: "doelb" +0xb424: "doels" +0xb425: "doelt" +0xb426: "doelp" +0xb427: "doelh" +0xb428: "doem" +0xb429: "doeb" +0xb42a: "doebs" +0xb42b: "does" +0xb42c: "doess" +0xb42d: "doeng" +0xb42e: "doej" +0xb42f: "doec" +0xb430: "doek" +0xb431: "doet" +0xb432: "doep" +0xb433: "doeh" +0xb434: "dyo" +0xb435: "dyog" +0xb436: "dyogg" +0xb437: "dyogs" +0xb438: "dyon" +0xb439: "dyonj" +0xb43a: "dyonh" +0xb43b: "dyod" +0xb43c: "dyol" +0xb43d: "dyolg" +0xb43e: "dyolm" +0xb43f: "dyolb" +0xb440: "dyols" +0xb441: "dyolt" +0xb442: "dyolp" +0xb443: "dyolh" +0xb444: "dyom" +0xb445: "dyob" +0xb446: "dyobs" +0xb447: "dyos" +0xb448: "dyoss" +0xb449: "dyong" +0xb44a: "dyoj" +0xb44b: "dyoc" +0xb44c: "dyok" +0xb44d: "dyot" +0xb44e: "dyop" +0xb44f: "dyoh" +0xb450: "du" +0xb451: "dug" +0xb452: "dugg" +0xb453: "dugs" +0xb454: "dun" +0xb455: "dunj" +0xb456: "dunh" +0xb457: "dud" +0xb458: "dul" +0xb459: "dulg" +0xb45a: "dulm" +0xb45b: "dulb" +0xb45c: "duls" +0xb45d: "dult" +0xb45e: "dulp" +0xb45f: "dulh" +0xb460: "dum" +0xb461: "dub" +0xb462: "dubs" +0xb463: "dus" +0xb464: "duss" +0xb465: "dung" +0xb466: "duj" +0xb467: "duc" +0xb468: "duk" +0xb469: "dut" +0xb46a: "dup" +0xb46b: "duh" +0xb46c: "dweo" +0xb46d: "dweog" +0xb46e: "dweogg" +0xb46f: "dweogs" +0xb470: "dweon" +0xb471: "dweonj" +0xb472: "dweonh" +0xb473: "dweod" +0xb474: "dweol" +0xb475: "dweolg" +0xb476: "dweolm" +0xb477: "dweolb" +0xb478: "dweols" +0xb479: "dweolt" +0xb47a: "dweolp" +0xb47b: "dweolh" +0xb47c: "dweom" +0xb47d: "dweob" +0xb47e: "dweobs" +0xb47f: "dweos" +0xb480: "dweoss" +0xb481: "dweong" +0xb482: "dweoj" +0xb483: "dweoc" +0xb484: "dweok" +0xb485: "dweot" +0xb486: "dweop" +0xb487: "dweoh" +0xb488: "dwe" +0xb489: "dweg" +0xb48a: "dwegg" +0xb48b: "dwegs" +0xb48c: "dwen" +0xb48d: "dwenj" +0xb48e: "dwenh" +0xb48f: "dwed" +0xb490: "dwel" +0xb491: "dwelg" +0xb492: "dwelm" +0xb493: "dwelb" +0xb494: "dwels" +0xb495: "dwelt" +0xb496: "dwelp" +0xb497: "dwelh" +0xb498: "dwem" +0xb499: "dweb" +0xb49a: "dwebs" +0xb49b: "dwes" +0xb49c: "dwess" +0xb49d: "dweng" +0xb49e: "dwej" +0xb49f: "dwec" +0xb4a0: "dwek" +0xb4a1: "dwet" +0xb4a2: "dwep" +0xb4a3: "dweh" +0xb4a4: "dwi" +0xb4a5: "dwig" +0xb4a6: "dwigg" +0xb4a7: "dwigs" +0xb4a8: "dwin" +0xb4a9: "dwinj" +0xb4aa: "dwinh" +0xb4ab: "dwid" +0xb4ac: "dwil" +0xb4ad: "dwilg" +0xb4ae: "dwilm" +0xb4af: "dwilb" +0xb4b0: "dwils" +0xb4b1: "dwilt" +0xb4b2: "dwilp" +0xb4b3: "dwilh" +0xb4b4: "dwim" +0xb4b5: "dwib" +0xb4b6: "dwibs" +0xb4b7: "dwis" +0xb4b8: "dwiss" +0xb4b9: "dwing" +0xb4ba: "dwij" +0xb4bb: "dwic" +0xb4bc: "dwik" +0xb4bd: "dwit" +0xb4be: "dwip" +0xb4bf: "dwih" +0xb4c0: "dyu" +0xb4c1: "dyug" +0xb4c2: "dyugg" +0xb4c3: "dyugs" +0xb4c4: "dyun" +0xb4c5: "dyunj" +0xb4c6: "dyunh" +0xb4c7: "dyud" +0xb4c8: "dyul" +0xb4c9: "dyulg" +0xb4ca: "dyulm" +0xb4cb: "dyulb" +0xb4cc: "dyuls" +0xb4cd: "dyult" +0xb4ce: "dyulp" +0xb4cf: "dyulh" +0xb4d0: "dyum" +0xb4d1: "dyub" +0xb4d2: "dyubs" +0xb4d3: "dyus" +0xb4d4: "dyuss" +0xb4d5: "dyung" +0xb4d6: "dyuj" +0xb4d7: "dyuc" +0xb4d8: "dyuk" +0xb4d9: "dyut" +0xb4da: "dyup" +0xb4db: "dyuh" +0xb4dc: "deu" +0xb4dd: "deug" +0xb4de: "deugg" +0xb4df: "deugs" +0xb4e0: "deun" +0xb4e1: "deunj" +0xb4e2: "deunh" +0xb4e3: "deud" +0xb4e4: "deul" +0xb4e5: "deulg" +0xb4e6: "deulm" +0xb4e7: "deulb" +0xb4e8: "deuls" +0xb4e9: "deult" +0xb4ea: "deulp" +0xb4eb: "deulh" +0xb4ec: "deum" +0xb4ed: "deub" +0xb4ee: "deubs" +0xb4ef: "deus" +0xb4f0: "deuss" +0xb4f1: "deung" +0xb4f2: "deuj" +0xb4f3: "deuc" +0xb4f4: "deuk" +0xb4f5: "deut" +0xb4f6: "deup" +0xb4f7: "deuh" +0xb4f8: "dyi" +0xb4f9: "dyig" +0xb4fa: "dyigg" +0xb4fb: "dyigs" +0xb4fc: "dyin" +0xb4fd: "dyinj" +0xb4fe: "dyinh" +0xb4ff: "dyid" +/* x0b5 */ +0xb500: "dyil" +0xb501: "dyilg" +0xb502: "dyilm" +0xb503: "dyilb" +0xb504: "dyils" +0xb505: "dyilt" +0xb506: "dyilp" +0xb507: "dyilh" +0xb508: "dyim" +0xb509: "dyib" +0xb50a: "dyibs" +0xb50b: "dyis" +0xb50c: "dyiss" +0xb50d: "dying" +0xb50e: "dyij" +0xb50f: "dyic" +0xb510: "dyik" +0xb511: "dyit" +0xb512: "dyip" +0xb513: "dyih" +0xb514: "di" +0xb515: "dig" +0xb516: "digg" +0xb517: "digs" +0xb518: "din" +0xb519: "dinj" +0xb51a: "dinh" +0xb51b: "did" +0xb51c: "dil" +0xb51d: "dilg" +0xb51e: "dilm" +0xb51f: "dilb" +0xb520: "dils" +0xb521: "dilt" +0xb522: "dilp" +0xb523: "dilh" +0xb524: "dim" +0xb525: "dib" +0xb526: "dibs" +0xb527: "dis" +0xb528: "diss" +0xb529: "ding" +0xb52a: "dij" +0xb52b: "dic" +0xb52c: "dik" +0xb52d: "dit" +0xb52e: "dip" +0xb52f: "dih" +0xb530: "dda" +0xb531: "ddag" +0xb532: "ddagg" +0xb533: "ddags" +0xb534: "ddan" +0xb535: "ddanj" +0xb536: "ddanh" +0xb537: "ddad" +0xb538: "ddal" +0xb539: "ddalg" +0xb53a: "ddalm" +0xb53b: "ddalb" +0xb53c: "ddals" +0xb53d: "ddalt" +0xb53e: "ddalp" +0xb53f: "ddalh" +0xb540: "ddam" +0xb541: "ddab" +0xb542: "ddabs" +0xb543: "ddas" +0xb544: "ddass" +0xb545: "ddang" +0xb546: "ddaj" +0xb547: "ddac" +0xb548: "ddak" +0xb549: "ddat" +0xb54a: "ddap" +0xb54b: "ddah" +0xb54c: "ddae" +0xb54d: "ddaeg" +0xb54e: "ddaegg" +0xb54f: "ddaegs" +0xb550: "ddaen" +0xb551: "ddaenj" +0xb552: "ddaenh" +0xb553: "ddaed" +0xb554: "ddael" +0xb555: "ddaelg" +0xb556: "ddaelm" +0xb557: "ddaelb" +0xb558: "ddaels" +0xb559: "ddaelt" +0xb55a: "ddaelp" +0xb55b: "ddaelh" +0xb55c: "ddaem" +0xb55d: "ddaeb" +0xb55e: "ddaebs" +0xb55f: "ddaes" +0xb560: "ddaess" +0xb561: "ddaeng" +0xb562: "ddaej" +0xb563: "ddaec" +0xb564: "ddaek" +0xb565: "ddaet" +0xb566: "ddaep" +0xb567: "ddaeh" +0xb568: "ddya" +0xb569: "ddyag" +0xb56a: "ddyagg" +0xb56b: "ddyags" +0xb56c: "ddyan" +0xb56d: "ddyanj" +0xb56e: "ddyanh" +0xb56f: "ddyad" +0xb570: "ddyal" +0xb571: "ddyalg" +0xb572: "ddyalm" +0xb573: "ddyalb" +0xb574: "ddyals" +0xb575: "ddyalt" +0xb576: "ddyalp" +0xb577: "ddyalh" +0xb578: "ddyam" +0xb579: "ddyab" +0xb57a: "ddyabs" +0xb57b: "ddyas" +0xb57c: "ddyass" +0xb57d: "ddyang" +0xb57e: "ddyaj" +0xb57f: "ddyac" +0xb580: "ddyak" +0xb581: "ddyat" +0xb582: "ddyap" +0xb583: "ddyah" +0xb584: "ddyae" +0xb585: "ddyaeg" +0xb586: "ddyaegg" +0xb587: "ddyaegs" +0xb588: "ddyaen" +0xb589: "ddyaenj" +0xb58a: "ddyaenh" +0xb58b: "ddyaed" +0xb58c: "ddyael" +0xb58d: "ddyaelg" +0xb58e: "ddyaelm" +0xb58f: "ddyaelb" +0xb590: "ddyaels" +0xb591: "ddyaelt" +0xb592: "ddyaelp" +0xb593: "ddyaelh" +0xb594: "ddyaem" +0xb595: "ddyaeb" +0xb596: "ddyaebs" +0xb597: "ddyaes" +0xb598: "ddyaess" +0xb599: "ddyaeng" +0xb59a: "ddyaej" +0xb59b: "ddyaec" +0xb59c: "ddyaek" +0xb59d: "ddyaet" +0xb59e: "ddyaep" +0xb59f: "ddyaeh" +0xb5a0: "ddeo" +0xb5a1: "ddeog" +0xb5a2: "ddeogg" +0xb5a3: "ddeogs" +0xb5a4: "ddeon" +0xb5a5: "ddeonj" +0xb5a6: "ddeonh" +0xb5a7: "ddeod" +0xb5a8: "ddeol" +0xb5a9: "ddeolg" +0xb5aa: "ddeolm" +0xb5ab: "ddeolb" +0xb5ac: "ddeols" +0xb5ad: "ddeolt" +0xb5ae: "ddeolp" +0xb5af: "ddeolh" +0xb5b0: "ddeom" +0xb5b1: "ddeob" +0xb5b2: "ddeobs" +0xb5b3: "ddeos" +0xb5b4: "ddeoss" +0xb5b5: "ddeong" +0xb5b6: "ddeoj" +0xb5b7: "ddeoc" +0xb5b8: "ddeok" +0xb5b9: "ddeot" +0xb5ba: "ddeop" +0xb5bb: "ddeoh" +0xb5bc: "dde" +0xb5bd: "ddeg" +0xb5be: "ddegg" +0xb5bf: "ddegs" +0xb5c0: "dden" +0xb5c1: "ddenj" +0xb5c2: "ddenh" +0xb5c3: "dded" +0xb5c4: "ddel" +0xb5c5: "ddelg" +0xb5c6: "ddelm" +0xb5c7: "ddelb" +0xb5c8: "ddels" +0xb5c9: "ddelt" +0xb5ca: "ddelp" +0xb5cb: "ddelh" +0xb5cc: "ddem" +0xb5cd: "ddeb" +0xb5ce: "ddebs" +0xb5cf: "ddes" +0xb5d0: "ddess" +0xb5d1: "ddeng" +0xb5d2: "ddej" +0xb5d3: "ddec" +0xb5d4: "ddek" +0xb5d5: "ddet" +0xb5d6: "ddep" +0xb5d7: "ddeh" +0xb5d8: "ddyeo" +0xb5d9: "ddyeog" +0xb5da: "ddyeogg" +0xb5db: "ddyeogs" +0xb5dc: "ddyeon" +0xb5dd: "ddyeonj" +0xb5de: "ddyeonh" +0xb5df: "ddyeod" +0xb5e0: "ddyeol" +0xb5e1: "ddyeolg" +0xb5e2: "ddyeolm" +0xb5e3: "ddyeolb" +0xb5e4: "ddyeols" +0xb5e5: "ddyeolt" +0xb5e6: "ddyeolp" +0xb5e7: "ddyeolh" +0xb5e8: "ddyeom" +0xb5e9: "ddyeob" +0xb5ea: "ddyeobs" +0xb5eb: "ddyeos" +0xb5ec: "ddyeoss" +0xb5ed: "ddyeong" +0xb5ee: "ddyeoj" +0xb5ef: "ddyeoc" +0xb5f0: "ddyeok" +0xb5f1: "ddyeot" +0xb5f2: "ddyeop" +0xb5f3: "ddyeoh" +0xb5f4: "ddye" +0xb5f5: "ddyeg" +0xb5f6: "ddyegg" +0xb5f7: "ddyegs" +0xb5f8: "ddyen" +0xb5f9: "ddyenj" +0xb5fa: "ddyenh" +0xb5fb: "ddyed" +0xb5fc: "ddyel" +0xb5fd: "ddyelg" +0xb5fe: "ddyelm" +0xb5ff: "ddyelb" +/* x0b6 */ +0xb600: "ddyels" +0xb601: "ddyelt" +0xb602: "ddyelp" +0xb603: "ddyelh" +0xb604: "ddyem" +0xb605: "ddyeb" +0xb606: "ddyebs" +0xb607: "ddyes" +0xb608: "ddyess" +0xb609: "ddyeng" +0xb60a: "ddyej" +0xb60b: "ddyec" +0xb60c: "ddyek" +0xb60d: "ddyet" +0xb60e: "ddyep" +0xb60f: "ddyeh" +0xb610: "ddo" +0xb611: "ddog" +0xb612: "ddogg" +0xb613: "ddogs" +0xb614: "ddon" +0xb615: "ddonj" +0xb616: "ddonh" +0xb617: "ddod" +0xb618: "ddol" +0xb619: "ddolg" +0xb61a: "ddolm" +0xb61b: "ddolb" +0xb61c: "ddols" +0xb61d: "ddolt" +0xb61e: "ddolp" +0xb61f: "ddolh" +0xb620: "ddom" +0xb621: "ddob" +0xb622: "ddobs" +0xb623: "ddos" +0xb624: "ddoss" +0xb625: "ddong" +0xb626: "ddoj" +0xb627: "ddoc" +0xb628: "ddok" +0xb629: "ddot" +0xb62a: "ddop" +0xb62b: "ddoh" +0xb62c: "ddwa" +0xb62d: "ddwag" +0xb62e: "ddwagg" +0xb62f: "ddwags" +0xb630: "ddwan" +0xb631: "ddwanj" +0xb632: "ddwanh" +0xb633: "ddwad" +0xb634: "ddwal" +0xb635: "ddwalg" +0xb636: "ddwalm" +0xb637: "ddwalb" +0xb638: "ddwals" +0xb639: "ddwalt" +0xb63a: "ddwalp" +0xb63b: "ddwalh" +0xb63c: "ddwam" +0xb63d: "ddwab" +0xb63e: "ddwabs" +0xb63f: "ddwas" +0xb640: "ddwass" +0xb641: "ddwang" +0xb642: "ddwaj" +0xb643: "ddwac" +0xb644: "ddwak" +0xb645: "ddwat" +0xb646: "ddwap" +0xb647: "ddwah" +0xb648: "ddwae" +0xb649: "ddwaeg" +0xb64a: "ddwaegg" +0xb64b: "ddwaegs" +0xb64c: "ddwaen" +0xb64d: "ddwaenj" +0xb64e: "ddwaenh" +0xb64f: "ddwaed" +0xb650: "ddwael" +0xb651: "ddwaelg" +0xb652: "ddwaelm" +0xb653: "ddwaelb" +0xb654: "ddwaels" +0xb655: "ddwaelt" +0xb656: "ddwaelp" +0xb657: "ddwaelh" +0xb658: "ddwaem" +0xb659: "ddwaeb" +0xb65a: "ddwaebs" +0xb65b: "ddwaes" +0xb65c: "ddwaess" +0xb65d: "ddwaeng" +0xb65e: "ddwaej" +0xb65f: "ddwaec" +0xb660: "ddwaek" +0xb661: "ddwaet" +0xb662: "ddwaep" +0xb663: "ddwaeh" +0xb664: "ddoe" +0xb665: "ddoeg" +0xb666: "ddoegg" +0xb667: "ddoegs" +0xb668: "ddoen" +0xb669: "ddoenj" +0xb66a: "ddoenh" +0xb66b: "ddoed" +0xb66c: "ddoel" +0xb66d: "ddoelg" +0xb66e: "ddoelm" +0xb66f: "ddoelb" +0xb670: "ddoels" +0xb671: "ddoelt" +0xb672: "ddoelp" +0xb673: "ddoelh" +0xb674: "ddoem" +0xb675: "ddoeb" +0xb676: "ddoebs" +0xb677: "ddoes" +0xb678: "ddoess" +0xb679: "ddoeng" +0xb67a: "ddoej" +0xb67b: "ddoec" +0xb67c: "ddoek" +0xb67d: "ddoet" +0xb67e: "ddoep" +0xb67f: "ddoeh" +0xb680: "ddyo" +0xb681: "ddyog" +0xb682: "ddyogg" +0xb683: "ddyogs" +0xb684: "ddyon" +0xb685: "ddyonj" +0xb686: "ddyonh" +0xb687: "ddyod" +0xb688: "ddyol" +0xb689: "ddyolg" +0xb68a: "ddyolm" +0xb68b: "ddyolb" +0xb68c: "ddyols" +0xb68d: "ddyolt" +0xb68e: "ddyolp" +0xb68f: "ddyolh" +0xb690: "ddyom" +0xb691: "ddyob" +0xb692: "ddyobs" +0xb693: "ddyos" +0xb694: "ddyoss" +0xb695: "ddyong" +0xb696: "ddyoj" +0xb697: "ddyoc" +0xb698: "ddyok" +0xb699: "ddyot" +0xb69a: "ddyop" +0xb69b: "ddyoh" +0xb69c: "ddu" +0xb69d: "ddug" +0xb69e: "ddugg" +0xb69f: "ddugs" +0xb6a0: "ddun" +0xb6a1: "ddunj" +0xb6a2: "ddunh" +0xb6a3: "ddud" +0xb6a4: "ddul" +0xb6a5: "ddulg" +0xb6a6: "ddulm" +0xb6a7: "ddulb" +0xb6a8: "dduls" +0xb6a9: "ddult" +0xb6aa: "ddulp" +0xb6ab: "ddulh" +0xb6ac: "ddum" +0xb6ad: "ddub" +0xb6ae: "ddubs" +0xb6af: "ddus" +0xb6b0: "dduss" +0xb6b1: "ddung" +0xb6b2: "dduj" +0xb6b3: "dduc" +0xb6b4: "dduk" +0xb6b5: "ddut" +0xb6b6: "ddup" +0xb6b7: "dduh" +0xb6b8: "ddweo" +0xb6b9: "ddweog" +0xb6ba: "ddweogg" +0xb6bb: "ddweogs" +0xb6bc: "ddweon" +0xb6bd: "ddweonj" +0xb6be: "ddweonh" +0xb6bf: "ddweod" +0xb6c0: "ddweol" +0xb6c1: "ddweolg" +0xb6c2: "ddweolm" +0xb6c3: "ddweolb" +0xb6c4: "ddweols" +0xb6c5: "ddweolt" +0xb6c6: "ddweolp" +0xb6c7: "ddweolh" +0xb6c8: "ddweom" +0xb6c9: "ddweob" +0xb6ca: "ddweobs" +0xb6cb: "ddweos" +0xb6cc: "ddweoss" +0xb6cd: "ddweong" +0xb6ce: "ddweoj" +0xb6cf: "ddweoc" +0xb6d0: "ddweok" +0xb6d1: "ddweot" +0xb6d2: "ddweop" +0xb6d3: "ddweoh" +0xb6d4: "ddwe" +0xb6d5: "ddweg" +0xb6d6: "ddwegg" +0xb6d7: "ddwegs" +0xb6d8: "ddwen" +0xb6d9: "ddwenj" +0xb6da: "ddwenh" +0xb6db: "ddwed" +0xb6dc: "ddwel" +0xb6dd: "ddwelg" +0xb6de: "ddwelm" +0xb6df: "ddwelb" +0xb6e0: "ddwels" +0xb6e1: "ddwelt" +0xb6e2: "ddwelp" +0xb6e3: "ddwelh" +0xb6e4: "ddwem" +0xb6e5: "ddweb" +0xb6e6: "ddwebs" +0xb6e7: "ddwes" +0xb6e8: "ddwess" +0xb6e9: "ddweng" +0xb6ea: "ddwej" +0xb6eb: "ddwec" +0xb6ec: "ddwek" +0xb6ed: "ddwet" +0xb6ee: "ddwep" +0xb6ef: "ddweh" +0xb6f0: "ddwi" +0xb6f1: "ddwig" +0xb6f2: "ddwigg" +0xb6f3: "ddwigs" +0xb6f4: "ddwin" +0xb6f5: "ddwinj" +0xb6f6: "ddwinh" +0xb6f7: "ddwid" +0xb6f8: "ddwil" +0xb6f9: "ddwilg" +0xb6fa: "ddwilm" +0xb6fb: "ddwilb" +0xb6fc: "ddwils" +0xb6fd: "ddwilt" +0xb6fe: "ddwilp" +0xb6ff: "ddwilh" +/* x0b7 */ +0xb700: "ddwim" +0xb701: "ddwib" +0xb702: "ddwibs" +0xb703: "ddwis" +0xb704: "ddwiss" +0xb705: "ddwing" +0xb706: "ddwij" +0xb707: "ddwic" +0xb708: "ddwik" +0xb709: "ddwit" +0xb70a: "ddwip" +0xb70b: "ddwih" +0xb70c: "ddyu" +0xb70d: "ddyug" +0xb70e: "ddyugg" +0xb70f: "ddyugs" +0xb710: "ddyun" +0xb711: "ddyunj" +0xb712: "ddyunh" +0xb713: "ddyud" +0xb714: "ddyul" +0xb715: "ddyulg" +0xb716: "ddyulm" +0xb717: "ddyulb" +0xb718: "ddyuls" +0xb719: "ddyult" +0xb71a: "ddyulp" +0xb71b: "ddyulh" +0xb71c: "ddyum" +0xb71d: "ddyub" +0xb71e: "ddyubs" +0xb71f: "ddyus" +0xb720: "ddyuss" +0xb721: "ddyung" +0xb722: "ddyuj" +0xb723: "ddyuc" +0xb724: "ddyuk" +0xb725: "ddyut" +0xb726: "ddyup" +0xb727: "ddyuh" +0xb728: "ddeu" +0xb729: "ddeug" +0xb72a: "ddeugg" +0xb72b: "ddeugs" +0xb72c: "ddeun" +0xb72d: "ddeunj" +0xb72e: "ddeunh" +0xb72f: "ddeud" +0xb730: "ddeul" +0xb731: "ddeulg" +0xb732: "ddeulm" +0xb733: "ddeulb" +0xb734: "ddeuls" +0xb735: "ddeult" +0xb736: "ddeulp" +0xb737: "ddeulh" +0xb738: "ddeum" +0xb739: "ddeub" +0xb73a: "ddeubs" +0xb73b: "ddeus" +0xb73c: "ddeuss" +0xb73d: "ddeung" +0xb73e: "ddeuj" +0xb73f: "ddeuc" +0xb740: "ddeuk" +0xb741: "ddeut" +0xb742: "ddeup" +0xb743: "ddeuh" +0xb744: "ddyi" +0xb745: "ddyig" +0xb746: "ddyigg" +0xb747: "ddyigs" +0xb748: "ddyin" +0xb749: "ddyinj" +0xb74a: "ddyinh" +0xb74b: "ddyid" +0xb74c: "ddyil" +0xb74d: "ddyilg" +0xb74e: "ddyilm" +0xb74f: "ddyilb" +0xb750: "ddyils" +0xb751: "ddyilt" +0xb752: "ddyilp" +0xb753: "ddyilh" +0xb754: "ddyim" +0xb755: "ddyib" +0xb756: "ddyibs" +0xb757: "ddyis" +0xb758: "ddyiss" +0xb759: "ddying" +0xb75a: "ddyij" +0xb75b: "ddyic" +0xb75c: "ddyik" +0xb75d: "ddyit" +0xb75e: "ddyip" +0xb75f: "ddyih" +0xb760: "ddi" +0xb761: "ddig" +0xb762: "ddigg" +0xb763: "ddigs" +0xb764: "ddin" +0xb765: "ddinj" +0xb766: "ddinh" +0xb767: "ddid" +0xb768: "ddil" +0xb769: "ddilg" +0xb76a: "ddilm" +0xb76b: "ddilb" +0xb76c: "ddils" +0xb76d: "ddilt" +0xb76e: "ddilp" +0xb76f: "ddilh" +0xb770: "ddim" +0xb771: "ddib" +0xb772: "ddibs" +0xb773: "ddis" +0xb774: "ddiss" +0xb775: "dding" +0xb776: "ddij" +0xb777: "ddic" +0xb778: "ddik" +0xb779: "ddit" +0xb77a: "ddip" +0xb77b: "ddih" +0xb77c: "ra" +0xb77d: "rag" +0xb77e: "ragg" +0xb77f: "rags" +0xb780: "ran" +0xb781: "ranj" +0xb782: "ranh" +0xb783: "rad" +0xb784: "ral" +0xb785: "ralg" +0xb786: "ralm" +0xb787: "ralb" +0xb788: "rals" +0xb789: "ralt" +0xb78a: "ralp" +0xb78b: "ralh" +0xb78c: "ram" +0xb78d: "rab" +0xb78e: "rabs" +0xb78f: "ras" +0xb790: "rass" +0xb791: "rang" +0xb792: "raj" +0xb793: "rac" +0xb794: "rak" +0xb795: "rat" +0xb796: "rap" +0xb797: "rah" +0xb798: "rae" +0xb799: "raeg" +0xb79a: "raegg" +0xb79b: "raegs" +0xb79c: "raen" +0xb79d: "raenj" +0xb79e: "raenh" +0xb79f: "raed" +0xb7a0: "rael" +0xb7a1: "raelg" +0xb7a2: "raelm" +0xb7a3: "raelb" +0xb7a4: "raels" +0xb7a5: "raelt" +0xb7a6: "raelp" +0xb7a7: "raelh" +0xb7a8: "raem" +0xb7a9: "raeb" +0xb7aa: "raebs" +0xb7ab: "raes" +0xb7ac: "raess" +0xb7ad: "raeng" +0xb7ae: "raej" +0xb7af: "raec" +0xb7b0: "raek" +0xb7b1: "raet" +0xb7b2: "raep" +0xb7b3: "raeh" +0xb7b4: "rya" +0xb7b5: "ryag" +0xb7b6: "ryagg" +0xb7b7: "ryags" +0xb7b8: "ryan" +0xb7b9: "ryanj" +0xb7ba: "ryanh" +0xb7bb: "ryad" +0xb7bc: "ryal" +0xb7bd: "ryalg" +0xb7be: "ryalm" +0xb7bf: "ryalb" +0xb7c0: "ryals" +0xb7c1: "ryalt" +0xb7c2: "ryalp" +0xb7c3: "ryalh" +0xb7c4: "ryam" +0xb7c5: "ryab" +0xb7c6: "ryabs" +0xb7c7: "ryas" +0xb7c8: "ryass" +0xb7c9: "ryang" +0xb7ca: "ryaj" +0xb7cb: "ryac" +0xb7cc: "ryak" +0xb7cd: "ryat" +0xb7ce: "ryap" +0xb7cf: "ryah" +0xb7d0: "ryae" +0xb7d1: "ryaeg" +0xb7d2: "ryaegg" +0xb7d3: "ryaegs" +0xb7d4: "ryaen" +0xb7d5: "ryaenj" +0xb7d6: "ryaenh" +0xb7d7: "ryaed" +0xb7d8: "ryael" +0xb7d9: "ryaelg" +0xb7da: "ryaelm" +0xb7db: "ryaelb" +0xb7dc: "ryaels" +0xb7dd: "ryaelt" +0xb7de: "ryaelp" +0xb7df: "ryaelh" +0xb7e0: "ryaem" +0xb7e1: "ryaeb" +0xb7e2: "ryaebs" +0xb7e3: "ryaes" +0xb7e4: "ryaess" +0xb7e5: "ryaeng" +0xb7e6: "ryaej" +0xb7e7: "ryaec" +0xb7e8: "ryaek" +0xb7e9: "ryaet" +0xb7ea: "ryaep" +0xb7eb: "ryaeh" +0xb7ec: "reo" +0xb7ed: "reog" +0xb7ee: "reogg" +0xb7ef: "reogs" +0xb7f0: "reon" +0xb7f1: "reonj" +0xb7f2: "reonh" +0xb7f3: "reod" +0xb7f4: "reol" +0xb7f5: "reolg" +0xb7f6: "reolm" +0xb7f7: "reolb" +0xb7f8: "reols" +0xb7f9: "reolt" +0xb7fa: "reolp" +0xb7fb: "reolh" +0xb7fc: "reom" +0xb7fd: "reob" +0xb7fe: "reobs" +0xb7ff: "reos" +/* x0b8 */ +0xb800: "reoss" +0xb801: "reong" +0xb802: "reoj" +0xb803: "reoc" +0xb804: "reok" +0xb805: "reot" +0xb806: "reop" +0xb807: "reoh" +0xb808: "re" +0xb809: "reg" +0xb80a: "regg" +0xb80b: "regs" +0xb80c: "ren" +0xb80d: "renj" +0xb80e: "renh" +0xb80f: "red" +0xb810: "rel" +0xb811: "relg" +0xb812: "relm" +0xb813: "relb" +0xb814: "rels" +0xb815: "relt" +0xb816: "relp" +0xb817: "relh" +0xb818: "rem" +0xb819: "reb" +0xb81a: "rebs" +0xb81b: "res" +0xb81c: "ress" +0xb81d: "reng" +0xb81e: "rej" +0xb81f: "rec" +0xb820: "rek" +0xb821: "ret" +0xb822: "rep" +0xb823: "reh" +0xb824: "ryeo" +0xb825: "ryeog" +0xb826: "ryeogg" +0xb827: "ryeogs" +0xb828: "ryeon" +0xb829: "ryeonj" +0xb82a: "ryeonh" +0xb82b: "ryeod" +0xb82c: "ryeol" +0xb82d: "ryeolg" +0xb82e: "ryeolm" +0xb82f: "ryeolb" +0xb830: "ryeols" +0xb831: "ryeolt" +0xb832: "ryeolp" +0xb833: "ryeolh" +0xb834: "ryeom" +0xb835: "ryeob" +0xb836: "ryeobs" +0xb837: "ryeos" +0xb838: "ryeoss" +0xb839: "ryeong" +0xb83a: "ryeoj" +0xb83b: "ryeoc" +0xb83c: "ryeok" +0xb83d: "ryeot" +0xb83e: "ryeop" +0xb83f: "ryeoh" +0xb840: "rye" +0xb841: "ryeg" +0xb842: "ryegg" +0xb843: "ryegs" +0xb844: "ryen" +0xb845: "ryenj" +0xb846: "ryenh" +0xb847: "ryed" +0xb848: "ryel" +0xb849: "ryelg" +0xb84a: "ryelm" +0xb84b: "ryelb" +0xb84c: "ryels" +0xb84d: "ryelt" +0xb84e: "ryelp" +0xb84f: "ryelh" +0xb850: "ryem" +0xb851: "ryeb" +0xb852: "ryebs" +0xb853: "ryes" +0xb854: "ryess" +0xb855: "ryeng" +0xb856: "ryej" +0xb857: "ryec" +0xb858: "ryek" +0xb859: "ryet" +0xb85a: "ryep" +0xb85b: "ryeh" +0xb85c: "ro" +0xb85d: "rog" +0xb85e: "rogg" +0xb85f: "rogs" +0xb860: "ron" +0xb861: "ronj" +0xb862: "ronh" +0xb863: "rod" +0xb864: "rol" +0xb865: "rolg" +0xb866: "rolm" +0xb867: "rolb" +0xb868: "rols" +0xb869: "rolt" +0xb86a: "rolp" +0xb86b: "rolh" +0xb86c: "rom" +0xb86d: "rob" +0xb86e: "robs" +0xb86f: "ros" +0xb870: "ross" +0xb871: "rong" +0xb872: "roj" +0xb873: "roc" +0xb874: "rok" +0xb875: "rot" +0xb876: "rop" +0xb877: "roh" +0xb878: "rwa" +0xb879: "rwag" +0xb87a: "rwagg" +0xb87b: "rwags" +0xb87c: "rwan" +0xb87d: "rwanj" +0xb87e: "rwanh" +0xb87f: "rwad" +0xb880: "rwal" +0xb881: "rwalg" +0xb882: "rwalm" +0xb883: "rwalb" +0xb884: "rwals" +0xb885: "rwalt" +0xb886: "rwalp" +0xb887: "rwalh" +0xb888: "rwam" +0xb889: "rwab" +0xb88a: "rwabs" +0xb88b: "rwas" +0xb88c: "rwass" +0xb88d: "rwang" +0xb88e: "rwaj" +0xb88f: "rwac" +0xb890: "rwak" +0xb891: "rwat" +0xb892: "rwap" +0xb893: "rwah" +0xb894: "rwae" +0xb895: "rwaeg" +0xb896: "rwaegg" +0xb897: "rwaegs" +0xb898: "rwaen" +0xb899: "rwaenj" +0xb89a: "rwaenh" +0xb89b: "rwaed" +0xb89c: "rwael" +0xb89d: "rwaelg" +0xb89e: "rwaelm" +0xb89f: "rwaelb" +0xb8a0: "rwaels" +0xb8a1: "rwaelt" +0xb8a2: "rwaelp" +0xb8a3: "rwaelh" +0xb8a4: "rwaem" +0xb8a5: "rwaeb" +0xb8a6: "rwaebs" +0xb8a7: "rwaes" +0xb8a8: "rwaess" +0xb8a9: "rwaeng" +0xb8aa: "rwaej" +0xb8ab: "rwaec" +0xb8ac: "rwaek" +0xb8ad: "rwaet" +0xb8ae: "rwaep" +0xb8af: "rwaeh" +0xb8b0: "roe" +0xb8b1: "roeg" +0xb8b2: "roegg" +0xb8b3: "roegs" +0xb8b4: "roen" +0xb8b5: "roenj" +0xb8b6: "roenh" +0xb8b7: "roed" +0xb8b8: "roel" +0xb8b9: "roelg" +0xb8ba: "roelm" +0xb8bb: "roelb" +0xb8bc: "roels" +0xb8bd: "roelt" +0xb8be: "roelp" +0xb8bf: "roelh" +0xb8c0: "roem" +0xb8c1: "roeb" +0xb8c2: "roebs" +0xb8c3: "roes" +0xb8c4: "roess" +0xb8c5: "roeng" +0xb8c6: "roej" +0xb8c7: "roec" +0xb8c8: "roek" +0xb8c9: "roet" +0xb8ca: "roep" +0xb8cb: "roeh" +0xb8cc: "ryo" +0xb8cd: "ryog" +0xb8ce: "ryogg" +0xb8cf: "ryogs" +0xb8d0: "ryon" +0xb8d1: "ryonj" +0xb8d2: "ryonh" +0xb8d3: "ryod" +0xb8d4: "ryol" +0xb8d5: "ryolg" +0xb8d6: "ryolm" +0xb8d7: "ryolb" +0xb8d8: "ryols" +0xb8d9: "ryolt" +0xb8da: "ryolp" +0xb8db: "ryolh" +0xb8dc: "ryom" +0xb8dd: "ryob" +0xb8de: "ryobs" +0xb8df: "ryos" +0xb8e0: "ryoss" +0xb8e1: "ryong" +0xb8e2: "ryoj" +0xb8e3: "ryoc" +0xb8e4: "ryok" +0xb8e5: "ryot" +0xb8e6: "ryop" +0xb8e7: "ryoh" +0xb8e8: "ru" +0xb8e9: "rug" +0xb8ea: "rugg" +0xb8eb: "rugs" +0xb8ec: "run" +0xb8ed: "runj" +0xb8ee: "runh" +0xb8ef: "rud" +0xb8f0: "rul" +0xb8f1: "rulg" +0xb8f2: "rulm" +0xb8f3: "rulb" +0xb8f4: "ruls" +0xb8f5: "rult" +0xb8f6: "rulp" +0xb8f7: "rulh" +0xb8f8: "rum" +0xb8f9: "rub" +0xb8fa: "rubs" +0xb8fb: "rus" +0xb8fc: "russ" +0xb8fd: "rung" +0xb8fe: "ruj" +0xb8ff: "ruc" +/* x0b9 */ +0xb900: "ruk" +0xb901: "rut" +0xb902: "rup" +0xb903: "ruh" +0xb904: "rweo" +0xb905: "rweog" +0xb906: "rweogg" +0xb907: "rweogs" +0xb908: "rweon" +0xb909: "rweonj" +0xb90a: "rweonh" +0xb90b: "rweod" +0xb90c: "rweol" +0xb90d: "rweolg" +0xb90e: "rweolm" +0xb90f: "rweolb" +0xb910: "rweols" +0xb911: "rweolt" +0xb912: "rweolp" +0xb913: "rweolh" +0xb914: "rweom" +0xb915: "rweob" +0xb916: "rweobs" +0xb917: "rweos" +0xb918: "rweoss" +0xb919: "rweong" +0xb91a: "rweoj" +0xb91b: "rweoc" +0xb91c: "rweok" +0xb91d: "rweot" +0xb91e: "rweop" +0xb91f: "rweoh" +0xb920: "rwe" +0xb921: "rweg" +0xb922: "rwegg" +0xb923: "rwegs" +0xb924: "rwen" +0xb925: "rwenj" +0xb926: "rwenh" +0xb927: "rwed" +0xb928: "rwel" +0xb929: "rwelg" +0xb92a: "rwelm" +0xb92b: "rwelb" +0xb92c: "rwels" +0xb92d: "rwelt" +0xb92e: "rwelp" +0xb92f: "rwelh" +0xb930: "rwem" +0xb931: "rweb" +0xb932: "rwebs" +0xb933: "rwes" +0xb934: "rwess" +0xb935: "rweng" +0xb936: "rwej" +0xb937: "rwec" +0xb938: "rwek" +0xb939: "rwet" +0xb93a: "rwep" +0xb93b: "rweh" +0xb93c: "rwi" +0xb93d: "rwig" +0xb93e: "rwigg" +0xb93f: "rwigs" +0xb940: "rwin" +0xb941: "rwinj" +0xb942: "rwinh" +0xb943: "rwid" +0xb944: "rwil" +0xb945: "rwilg" +0xb946: "rwilm" +0xb947: "rwilb" +0xb948: "rwils" +0xb949: "rwilt" +0xb94a: "rwilp" +0xb94b: "rwilh" +0xb94c: "rwim" +0xb94d: "rwib" +0xb94e: "rwibs" +0xb94f: "rwis" +0xb950: "rwiss" +0xb951: "rwing" +0xb952: "rwij" +0xb953: "rwic" +0xb954: "rwik" +0xb955: "rwit" +0xb956: "rwip" +0xb957: "rwih" +0xb958: "ryu" +0xb959: "ryug" +0xb95a: "ryugg" +0xb95b: "ryugs" +0xb95c: "ryun" +0xb95d: "ryunj" +0xb95e: "ryunh" +0xb95f: "ryud" +0xb960: "ryul" +0xb961: "ryulg" +0xb962: "ryulm" +0xb963: "ryulb" +0xb964: "ryuls" +0xb965: "ryult" +0xb966: "ryulp" +0xb967: "ryulh" +0xb968: "ryum" +0xb969: "ryub" +0xb96a: "ryubs" +0xb96b: "ryus" +0xb96c: "ryuss" +0xb96d: "ryung" +0xb96e: "ryuj" +0xb96f: "ryuc" +0xb970: "ryuk" +0xb971: "ryut" +0xb972: "ryup" +0xb973: "ryuh" +0xb974: "reu" +0xb975: "reug" +0xb976: "reugg" +0xb977: "reugs" +0xb978: "reun" +0xb979: "reunj" +0xb97a: "reunh" +0xb97b: "reud" +0xb97c: "reul" +0xb97d: "reulg" +0xb97e: "reulm" +0xb97f: "reulb" +0xb980: "reuls" +0xb981: "reult" +0xb982: "reulp" +0xb983: "reulh" +0xb984: "reum" +0xb985: "reub" +0xb986: "reubs" +0xb987: "reus" +0xb988: "reuss" +0xb989: "reung" +0xb98a: "reuj" +0xb98b: "reuc" +0xb98c: "reuk" +0xb98d: "reut" +0xb98e: "reup" +0xb98f: "reuh" +0xb990: "ryi" +0xb991: "ryig" +0xb992: "ryigg" +0xb993: "ryigs" +0xb994: "ryin" +0xb995: "ryinj" +0xb996: "ryinh" +0xb997: "ryid" +0xb998: "ryil" +0xb999: "ryilg" +0xb99a: "ryilm" +0xb99b: "ryilb" +0xb99c: "ryils" +0xb99d: "ryilt" +0xb99e: "ryilp" +0xb99f: "ryilh" +0xb9a0: "ryim" +0xb9a1: "ryib" +0xb9a2: "ryibs" +0xb9a3: "ryis" +0xb9a4: "ryiss" +0xb9a5: "rying" +0xb9a6: "ryij" +0xb9a7: "ryic" +0xb9a8: "ryik" +0xb9a9: "ryit" +0xb9aa: "ryip" +0xb9ab: "ryih" +0xb9ac: "ri" +0xb9ad: "rig" +0xb9ae: "rigg" +0xb9af: "rigs" +0xb9b0: "rin" +0xb9b1: "rinj" +0xb9b2: "rinh" +0xb9b3: "rid" +0xb9b4: "ril" +0xb9b5: "rilg" +0xb9b6: "rilm" +0xb9b7: "rilb" +0xb9b8: "rils" +0xb9b9: "rilt" +0xb9ba: "rilp" +0xb9bb: "rilh" +0xb9bc: "rim" +0xb9bd: "rib" +0xb9be: "ribs" +0xb9bf: "ris" +0xb9c0: "riss" +0xb9c1: "ring" +0xb9c2: "rij" +0xb9c3: "ric" +0xb9c4: "rik" +0xb9c5: "rit" +0xb9c6: "rip" +0xb9c7: "rih" +0xb9c8: "ma" +0xb9c9: "mag" +0xb9ca: "magg" +0xb9cb: "mags" +0xb9cc: "man" +0xb9cd: "manj" +0xb9ce: "manh" +0xb9cf: "mad" +0xb9d0: "mal" +0xb9d1: "malg" +0xb9d2: "malm" +0xb9d3: "malb" +0xb9d4: "mals" +0xb9d5: "malt" +0xb9d6: "malp" +0xb9d7: "malh" +0xb9d8: "mam" +0xb9d9: "mab" +0xb9da: "mabs" +0xb9db: "mas" +0xb9dc: "mass" +0xb9dd: "mang" +0xb9de: "maj" +0xb9df: "mac" +0xb9e0: "mak" +0xb9e1: "mat" +0xb9e2: "map" +0xb9e3: "mah" +0xb9e4: "mae" +0xb9e5: "maeg" +0xb9e6: "maegg" +0xb9e7: "maegs" +0xb9e8: "maen" +0xb9e9: "maenj" +0xb9ea: "maenh" +0xb9eb: "maed" +0xb9ec: "mael" +0xb9ed: "maelg" +0xb9ee: "maelm" +0xb9ef: "maelb" +0xb9f0: "maels" +0xb9f1: "maelt" +0xb9f2: "maelp" +0xb9f3: "maelh" +0xb9f4: "maem" +0xb9f5: "maeb" +0xb9f6: "maebs" +0xb9f7: "maes" +0xb9f8: "maess" +0xb9f9: "maeng" +0xb9fa: "maej" +0xb9fb: "maec" +0xb9fc: "maek" +0xb9fd: "maet" +0xb9fe: "maep" +0xb9ff: "maeh" +/* x0ba */ +0xba00: "mya" +0xba01: "myag" +0xba02: "myagg" +0xba03: "myags" +0xba04: "myan" +0xba05: "myanj" +0xba06: "myanh" +0xba07: "myad" +0xba08: "myal" +0xba09: "myalg" +0xba0a: "myalm" +0xba0b: "myalb" +0xba0c: "myals" +0xba0d: "myalt" +0xba0e: "myalp" +0xba0f: "myalh" +0xba10: "myam" +0xba11: "myab" +0xba12: "myabs" +0xba13: "myas" +0xba14: "myass" +0xba15: "myang" +0xba16: "myaj" +0xba17: "myac" +0xba18: "myak" +0xba19: "myat" +0xba1a: "myap" +0xba1b: "myah" +0xba1c: "myae" +0xba1d: "myaeg" +0xba1e: "myaegg" +0xba1f: "myaegs" +0xba20: "myaen" +0xba21: "myaenj" +0xba22: "myaenh" +0xba23: "myaed" +0xba24: "myael" +0xba25: "myaelg" +0xba26: "myaelm" +0xba27: "myaelb" +0xba28: "myaels" +0xba29: "myaelt" +0xba2a: "myaelp" +0xba2b: "myaelh" +0xba2c: "myaem" +0xba2d: "myaeb" +0xba2e: "myaebs" +0xba2f: "myaes" +0xba30: "myaess" +0xba31: "myaeng" +0xba32: "myaej" +0xba33: "myaec" +0xba34: "myaek" +0xba35: "myaet" +0xba36: "myaep" +0xba37: "myaeh" +0xba38: "meo" +0xba39: "meog" +0xba3a: "meogg" +0xba3b: "meogs" +0xba3c: "meon" +0xba3d: "meonj" +0xba3e: "meonh" +0xba3f: "meod" +0xba40: "meol" +0xba41: "meolg" +0xba42: "meolm" +0xba43: "meolb" +0xba44: "meols" +0xba45: "meolt" +0xba46: "meolp" +0xba47: "meolh" +0xba48: "meom" +0xba49: "meob" +0xba4a: "meobs" +0xba4b: "meos" +0xba4c: "meoss" +0xba4d: "meong" +0xba4e: "meoj" +0xba4f: "meoc" +0xba50: "meok" +0xba51: "meot" +0xba52: "meop" +0xba53: "meoh" +0xba54: "me" +0xba55: "meg" +0xba56: "megg" +0xba57: "megs" +0xba58: "men" +0xba59: "menj" +0xba5a: "menh" +0xba5b: "med" +0xba5c: "mel" +0xba5d: "melg" +0xba5e: "melm" +0xba5f: "melb" +0xba60: "mels" +0xba61: "melt" +0xba62: "melp" +0xba63: "melh" +0xba64: "mem" +0xba65: "meb" +0xba66: "mebs" +0xba67: "mes" +0xba68: "mess" +0xba69: "meng" +0xba6a: "mej" +0xba6b: "mec" +0xba6c: "mek" +0xba6d: "met" +0xba6e: "mep" +0xba6f: "meh" +0xba70: "myeo" +0xba71: "myeog" +0xba72: "myeogg" +0xba73: "myeogs" +0xba74: "myeon" +0xba75: "myeonj" +0xba76: "myeonh" +0xba77: "myeod" +0xba78: "myeol" +0xba79: "myeolg" +0xba7a: "myeolm" +0xba7b: "myeolb" +0xba7c: "myeols" +0xba7d: "myeolt" +0xba7e: "myeolp" +0xba7f: "myeolh" +0xba80: "myeom" +0xba81: "myeob" +0xba82: "myeobs" +0xba83: "myeos" +0xba84: "myeoss" +0xba85: "myeong" +0xba86: "myeoj" +0xba87: "myeoc" +0xba88: "myeok" +0xba89: "myeot" +0xba8a: "myeop" +0xba8b: "myeoh" +0xba8c: "mye" +0xba8d: "myeg" +0xba8e: "myegg" +0xba8f: "myegs" +0xba90: "myen" +0xba91: "myenj" +0xba92: "myenh" +0xba93: "myed" +0xba94: "myel" +0xba95: "myelg" +0xba96: "myelm" +0xba97: "myelb" +0xba98: "myels" +0xba99: "myelt" +0xba9a: "myelp" +0xba9b: "myelh" +0xba9c: "myem" +0xba9d: "myeb" +0xba9e: "myebs" +0xba9f: "myes" +0xbaa0: "myess" +0xbaa1: "myeng" +0xbaa2: "myej" +0xbaa3: "myec" +0xbaa4: "myek" +0xbaa5: "myet" +0xbaa6: "myep" +0xbaa7: "myeh" +0xbaa8: "mo" +0xbaa9: "mog" +0xbaaa: "mogg" +0xbaab: "mogs" +0xbaac: "mon" +0xbaad: "monj" +0xbaae: "monh" +0xbaaf: "mod" +0xbab0: "mol" +0xbab1: "molg" +0xbab2: "molm" +0xbab3: "molb" +0xbab4: "mols" +0xbab5: "molt" +0xbab6: "molp" +0xbab7: "molh" +0xbab8: "mom" +0xbab9: "mob" +0xbaba: "mobs" +0xbabb: "mos" +0xbabc: "moss" +0xbabd: "mong" +0xbabe: "moj" +0xbabf: "moc" +0xbac0: "mok" +0xbac1: "mot" +0xbac2: "mop" +0xbac3: "moh" +0xbac4: "mwa" +0xbac5: "mwag" +0xbac6: "mwagg" +0xbac7: "mwags" +0xbac8: "mwan" +0xbac9: "mwanj" +0xbaca: "mwanh" +0xbacb: "mwad" +0xbacc: "mwal" +0xbacd: "mwalg" +0xbace: "mwalm" +0xbacf: "mwalb" +0xbad0: "mwals" +0xbad1: "mwalt" +0xbad2: "mwalp" +0xbad3: "mwalh" +0xbad4: "mwam" +0xbad5: "mwab" +0xbad6: "mwabs" +0xbad7: "mwas" +0xbad8: "mwass" +0xbad9: "mwang" +0xbada: "mwaj" +0xbadb: "mwac" +0xbadc: "mwak" +0xbadd: "mwat" +0xbade: "mwap" +0xbadf: "mwah" +0xbae0: "mwae" +0xbae1: "mwaeg" +0xbae2: "mwaegg" +0xbae3: "mwaegs" +0xbae4: "mwaen" +0xbae5: "mwaenj" +0xbae6: "mwaenh" +0xbae7: "mwaed" +0xbae8: "mwael" +0xbae9: "mwaelg" +0xbaea: "mwaelm" +0xbaeb: "mwaelb" +0xbaec: "mwaels" +0xbaed: "mwaelt" +0xbaee: "mwaelp" +0xbaef: "mwaelh" +0xbaf0: "mwaem" +0xbaf1: "mwaeb" +0xbaf2: "mwaebs" +0xbaf3: "mwaes" +0xbaf4: "mwaess" +0xbaf5: "mwaeng" +0xbaf6: "mwaej" +0xbaf7: "mwaec" +0xbaf8: "mwaek" +0xbaf9: "mwaet" +0xbafa: "mwaep" +0xbafb: "mwaeh" +0xbafc: "moe" +0xbafd: "moeg" +0xbafe: "moegg" +0xbaff: "moegs" +/* x0bb */ +0xbb00: "moen" +0xbb01: "moenj" +0xbb02: "moenh" +0xbb03: "moed" +0xbb04: "moel" +0xbb05: "moelg" +0xbb06: "moelm" +0xbb07: "moelb" +0xbb08: "moels" +0xbb09: "moelt" +0xbb0a: "moelp" +0xbb0b: "moelh" +0xbb0c: "moem" +0xbb0d: "moeb" +0xbb0e: "moebs" +0xbb0f: "moes" +0xbb10: "moess" +0xbb11: "moeng" +0xbb12: "moej" +0xbb13: "moec" +0xbb14: "moek" +0xbb15: "moet" +0xbb16: "moep" +0xbb17: "moeh" +0xbb18: "myo" +0xbb19: "myog" +0xbb1a: "myogg" +0xbb1b: "myogs" +0xbb1c: "myon" +0xbb1d: "myonj" +0xbb1e: "myonh" +0xbb1f: "myod" +0xbb20: "myol" +0xbb21: "myolg" +0xbb22: "myolm" +0xbb23: "myolb" +0xbb24: "myols" +0xbb25: "myolt" +0xbb26: "myolp" +0xbb27: "myolh" +0xbb28: "myom" +0xbb29: "myob" +0xbb2a: "myobs" +0xbb2b: "myos" +0xbb2c: "myoss" +0xbb2d: "myong" +0xbb2e: "myoj" +0xbb2f: "myoc" +0xbb30: "myok" +0xbb31: "myot" +0xbb32: "myop" +0xbb33: "myoh" +0xbb34: "mu" +0xbb35: "mug" +0xbb36: "mugg" +0xbb37: "mugs" +0xbb38: "mun" +0xbb39: "munj" +0xbb3a: "munh" +0xbb3b: "mud" +0xbb3c: "mul" +0xbb3d: "mulg" +0xbb3e: "mulm" +0xbb3f: "mulb" +0xbb40: "muls" +0xbb41: "mult" +0xbb42: "mulp" +0xbb43: "mulh" +0xbb44: "mum" +0xbb45: "mub" +0xbb46: "mubs" +0xbb47: "mus" +0xbb48: "muss" +0xbb49: "mung" +0xbb4a: "muj" +0xbb4b: "muc" +0xbb4c: "muk" +0xbb4d: "mut" +0xbb4e: "mup" +0xbb4f: "muh" +0xbb50: "mweo" +0xbb51: "mweog" +0xbb52: "mweogg" +0xbb53: "mweogs" +0xbb54: "mweon" +0xbb55: "mweonj" +0xbb56: "mweonh" +0xbb57: "mweod" +0xbb58: "mweol" +0xbb59: "mweolg" +0xbb5a: "mweolm" +0xbb5b: "mweolb" +0xbb5c: "mweols" +0xbb5d: "mweolt" +0xbb5e: "mweolp" +0xbb5f: "mweolh" +0xbb60: "mweom" +0xbb61: "mweob" +0xbb62: "mweobs" +0xbb63: "mweos" +0xbb64: "mweoss" +0xbb65: "mweong" +0xbb66: "mweoj" +0xbb67: "mweoc" +0xbb68: "mweok" +0xbb69: "mweot" +0xbb6a: "mweop" +0xbb6b: "mweoh" +0xbb6c: "mwe" +0xbb6d: "mweg" +0xbb6e: "mwegg" +0xbb6f: "mwegs" +0xbb70: "mwen" +0xbb71: "mwenj" +0xbb72: "mwenh" +0xbb73: "mwed" +0xbb74: "mwel" +0xbb75: "mwelg" +0xbb76: "mwelm" +0xbb77: "mwelb" +0xbb78: "mwels" +0xbb79: "mwelt" +0xbb7a: "mwelp" +0xbb7b: "mwelh" +0xbb7c: "mwem" +0xbb7d: "mweb" +0xbb7e: "mwebs" +0xbb7f: "mwes" +0xbb80: "mwess" +0xbb81: "mweng" +0xbb82: "mwej" +0xbb83: "mwec" +0xbb84: "mwek" +0xbb85: "mwet" +0xbb86: "mwep" +0xbb87: "mweh" +0xbb88: "mwi" +0xbb89: "mwig" +0xbb8a: "mwigg" +0xbb8b: "mwigs" +0xbb8c: "mwin" +0xbb8d: "mwinj" +0xbb8e: "mwinh" +0xbb8f: "mwid" +0xbb90: "mwil" +0xbb91: "mwilg" +0xbb92: "mwilm" +0xbb93: "mwilb" +0xbb94: "mwils" +0xbb95: "mwilt" +0xbb96: "mwilp" +0xbb97: "mwilh" +0xbb98: "mwim" +0xbb99: "mwib" +0xbb9a: "mwibs" +0xbb9b: "mwis" +0xbb9c: "mwiss" +0xbb9d: "mwing" +0xbb9e: "mwij" +0xbb9f: "mwic" +0xbba0: "mwik" +0xbba1: "mwit" +0xbba2: "mwip" +0xbba3: "mwih" +0xbba4: "myu" +0xbba5: "myug" +0xbba6: "myugg" +0xbba7: "myugs" +0xbba8: "myun" +0xbba9: "myunj" +0xbbaa: "myunh" +0xbbab: "myud" +0xbbac: "myul" +0xbbad: "myulg" +0xbbae: "myulm" +0xbbaf: "myulb" +0xbbb0: "myuls" +0xbbb1: "myult" +0xbbb2: "myulp" +0xbbb3: "myulh" +0xbbb4: "myum" +0xbbb5: "myub" +0xbbb6: "myubs" +0xbbb7: "myus" +0xbbb8: "myuss" +0xbbb9: "myung" +0xbbba: "myuj" +0xbbbb: "myuc" +0xbbbc: "myuk" +0xbbbd: "myut" +0xbbbe: "myup" +0xbbbf: "myuh" +0xbbc0: "meu" +0xbbc1: "meug" +0xbbc2: "meugg" +0xbbc3: "meugs" +0xbbc4: "meun" +0xbbc5: "meunj" +0xbbc6: "meunh" +0xbbc7: "meud" +0xbbc8: "meul" +0xbbc9: "meulg" +0xbbca: "meulm" +0xbbcb: "meulb" +0xbbcc: "meuls" +0xbbcd: "meult" +0xbbce: "meulp" +0xbbcf: "meulh" +0xbbd0: "meum" +0xbbd1: "meub" +0xbbd2: "meubs" +0xbbd3: "meus" +0xbbd4: "meuss" +0xbbd5: "meung" +0xbbd6: "meuj" +0xbbd7: "meuc" +0xbbd8: "meuk" +0xbbd9: "meut" +0xbbda: "meup" +0xbbdb: "meuh" +0xbbdc: "myi" +0xbbdd: "myig" +0xbbde: "myigg" +0xbbdf: "myigs" +0xbbe0: "myin" +0xbbe1: "myinj" +0xbbe2: "myinh" +0xbbe3: "myid" +0xbbe4: "myil" +0xbbe5: "myilg" +0xbbe6: "myilm" +0xbbe7: "myilb" +0xbbe8: "myils" +0xbbe9: "myilt" +0xbbea: "myilp" +0xbbeb: "myilh" +0xbbec: "myim" +0xbbed: "myib" +0xbbee: "myibs" +0xbbef: "myis" +0xbbf0: "myiss" +0xbbf1: "mying" +0xbbf2: "myij" +0xbbf3: "myic" +0xbbf4: "myik" +0xbbf5: "myit" +0xbbf6: "myip" +0xbbf7: "myih" +0xbbf8: "mi" +0xbbf9: "mig" +0xbbfa: "migg" +0xbbfb: "migs" +0xbbfc: "min" +0xbbfd: "minj" +0xbbfe: "minh" +0xbbff: "mid" +/* x0bc */ +0xbc00: "mil" +0xbc01: "milg" +0xbc02: "milm" +0xbc03: "milb" +0xbc04: "mils" +0xbc05: "milt" +0xbc06: "milp" +0xbc07: "milh" +0xbc08: "mim" +0xbc09: "mib" +0xbc0a: "mibs" +0xbc0b: "mis" +0xbc0c: "miss" +0xbc0d: "ming" +0xbc0e: "mij" +0xbc0f: "mic" +0xbc10: "mik" +0xbc11: "mit" +0xbc12: "mip" +0xbc13: "mih" +0xbc14: "ba" +0xbc15: "bag" +0xbc16: "bagg" +0xbc17: "bags" +0xbc18: "ban" +0xbc19: "banj" +0xbc1a: "banh" +0xbc1b: "bad" +0xbc1c: "bal" +0xbc1d: "balg" +0xbc1e: "balm" +0xbc1f: "balb" +0xbc20: "bals" +0xbc21: "balt" +0xbc22: "balp" +0xbc23: "balh" +0xbc24: "bam" +0xbc25: "bab" +0xbc26: "babs" +0xbc27: "bas" +0xbc28: "bass" +0xbc29: "bang" +0xbc2a: "baj" +0xbc2b: "bac" +0xbc2c: "bak" +0xbc2d: "bat" +0xbc2e: "bap" +0xbc2f: "bah" +0xbc30: "bae" +0xbc31: "baeg" +0xbc32: "baegg" +0xbc33: "baegs" +0xbc34: "baen" +0xbc35: "baenj" +0xbc36: "baenh" +0xbc37: "baed" +0xbc38: "bael" +0xbc39: "baelg" +0xbc3a: "baelm" +0xbc3b: "baelb" +0xbc3c: "baels" +0xbc3d: "baelt" +0xbc3e: "baelp" +0xbc3f: "baelh" +0xbc40: "baem" +0xbc41: "baeb" +0xbc42: "baebs" +0xbc43: "baes" +0xbc44: "baess" +0xbc45: "baeng" +0xbc46: "baej" +0xbc47: "baec" +0xbc48: "baek" +0xbc49: "baet" +0xbc4a: "baep" +0xbc4b: "baeh" +0xbc4c: "bya" +0xbc4d: "byag" +0xbc4e: "byagg" +0xbc4f: "byags" +0xbc50: "byan" +0xbc51: "byanj" +0xbc52: "byanh" +0xbc53: "byad" +0xbc54: "byal" +0xbc55: "byalg" +0xbc56: "byalm" +0xbc57: "byalb" +0xbc58: "byals" +0xbc59: "byalt" +0xbc5a: "byalp" +0xbc5b: "byalh" +0xbc5c: "byam" +0xbc5d: "byab" +0xbc5e: "byabs" +0xbc5f: "byas" +0xbc60: "byass" +0xbc61: "byang" +0xbc62: "byaj" +0xbc63: "byac" +0xbc64: "byak" +0xbc65: "byat" +0xbc66: "byap" +0xbc67: "byah" +0xbc68: "byae" +0xbc69: "byaeg" +0xbc6a: "byaegg" +0xbc6b: "byaegs" +0xbc6c: "byaen" +0xbc6d: "byaenj" +0xbc6e: "byaenh" +0xbc6f: "byaed" +0xbc70: "byael" +0xbc71: "byaelg" +0xbc72: "byaelm" +0xbc73: "byaelb" +0xbc74: "byaels" +0xbc75: "byaelt" +0xbc76: "byaelp" +0xbc77: "byaelh" +0xbc78: "byaem" +0xbc79: "byaeb" +0xbc7a: "byaebs" +0xbc7b: "byaes" +0xbc7c: "byaess" +0xbc7d: "byaeng" +0xbc7e: "byaej" +0xbc7f: "byaec" +0xbc80: "byaek" +0xbc81: "byaet" +0xbc82: "byaep" +0xbc83: "byaeh" +0xbc84: "beo" +0xbc85: "beog" +0xbc86: "beogg" +0xbc87: "beogs" +0xbc88: "beon" +0xbc89: "beonj" +0xbc8a: "beonh" +0xbc8b: "beod" +0xbc8c: "beol" +0xbc8d: "beolg" +0xbc8e: "beolm" +0xbc8f: "beolb" +0xbc90: "beols" +0xbc91: "beolt" +0xbc92: "beolp" +0xbc93: "beolh" +0xbc94: "beom" +0xbc95: "beob" +0xbc96: "beobs" +0xbc97: "beos" +0xbc98: "beoss" +0xbc99: "beong" +0xbc9a: "beoj" +0xbc9b: "beoc" +0xbc9c: "beok" +0xbc9d: "beot" +0xbc9e: "beop" +0xbc9f: "beoh" +0xbca0: "be" +0xbca1: "beg" +0xbca2: "begg" +0xbca3: "begs" +0xbca4: "ben" +0xbca5: "benj" +0xbca6: "benh" +0xbca7: "bed" +0xbca8: "bel" +0xbca9: "belg" +0xbcaa: "belm" +0xbcab: "belb" +0xbcac: "bels" +0xbcad: "belt" +0xbcae: "belp" +0xbcaf: "belh" +0xbcb0: "bem" +0xbcb1: "beb" +0xbcb2: "bebs" +0xbcb3: "bes" +0xbcb4: "bess" +0xbcb5: "beng" +0xbcb6: "bej" +0xbcb7: "bec" +0xbcb8: "bek" +0xbcb9: "bet" +0xbcba: "bep" +0xbcbb: "beh" +0xbcbc: "byeo" +0xbcbd: "byeog" +0xbcbe: "byeogg" +0xbcbf: "byeogs" +0xbcc0: "byeon" +0xbcc1: "byeonj" +0xbcc2: "byeonh" +0xbcc3: "byeod" +0xbcc4: "byeol" +0xbcc5: "byeolg" +0xbcc6: "byeolm" +0xbcc7: "byeolb" +0xbcc8: "byeols" +0xbcc9: "byeolt" +0xbcca: "byeolp" +0xbccb: "byeolh" +0xbccc: "byeom" +0xbccd: "byeob" +0xbcce: "byeobs" +0xbccf: "byeos" +0xbcd0: "byeoss" +0xbcd1: "byeong" +0xbcd2: "byeoj" +0xbcd3: "byeoc" +0xbcd4: "byeok" +0xbcd5: "byeot" +0xbcd6: "byeop" +0xbcd7: "byeoh" +0xbcd8: "bye" +0xbcd9: "byeg" +0xbcda: "byegg" +0xbcdb: "byegs" +0xbcdc: "byen" +0xbcdd: "byenj" +0xbcde: "byenh" +0xbcdf: "byed" +0xbce0: "byel" +0xbce1: "byelg" +0xbce2: "byelm" +0xbce3: "byelb" +0xbce4: "byels" +0xbce5: "byelt" +0xbce6: "byelp" +0xbce7: "byelh" +0xbce8: "byem" +0xbce9: "byeb" +0xbcea: "byebs" +0xbceb: "byes" +0xbcec: "byess" +0xbced: "byeng" +0xbcee: "byej" +0xbcef: "byec" +0xbcf0: "byek" +0xbcf1: "byet" +0xbcf2: "byep" +0xbcf3: "byeh" +0xbcf4: "bo" +0xbcf5: "bog" +0xbcf6: "bogg" +0xbcf7: "bogs" +0xbcf8: "bon" +0xbcf9: "bonj" +0xbcfa: "bonh" +0xbcfb: "bod" +0xbcfc: "bol" +0xbcfd: "bolg" +0xbcfe: "bolm" +0xbcff: "bolb" +/* x0bd */ +0xbd00: "bols" +0xbd01: "bolt" +0xbd02: "bolp" +0xbd03: "bolh" +0xbd04: "bom" +0xbd05: "bob" +0xbd06: "bobs" +0xbd07: "bos" +0xbd08: "boss" +0xbd09: "bong" +0xbd0a: "boj" +0xbd0b: "boc" +0xbd0c: "bok" +0xbd0d: "bot" +0xbd0e: "bop" +0xbd0f: "boh" +0xbd10: "bwa" +0xbd11: "bwag" +0xbd12: "bwagg" +0xbd13: "bwags" +0xbd14: "bwan" +0xbd15: "bwanj" +0xbd16: "bwanh" +0xbd17: "bwad" +0xbd18: "bwal" +0xbd19: "bwalg" +0xbd1a: "bwalm" +0xbd1b: "bwalb" +0xbd1c: "bwals" +0xbd1d: "bwalt" +0xbd1e: "bwalp" +0xbd1f: "bwalh" +0xbd20: "bwam" +0xbd21: "bwab" +0xbd22: "bwabs" +0xbd23: "bwas" +0xbd24: "bwass" +0xbd25: "bwang" +0xbd26: "bwaj" +0xbd27: "bwac" +0xbd28: "bwak" +0xbd29: "bwat" +0xbd2a: "bwap" +0xbd2b: "bwah" +0xbd2c: "bwae" +0xbd2d: "bwaeg" +0xbd2e: "bwaegg" +0xbd2f: "bwaegs" +0xbd30: "bwaen" +0xbd31: "bwaenj" +0xbd32: "bwaenh" +0xbd33: "bwaed" +0xbd34: "bwael" +0xbd35: "bwaelg" +0xbd36: "bwaelm" +0xbd37: "bwaelb" +0xbd38: "bwaels" +0xbd39: "bwaelt" +0xbd3a: "bwaelp" +0xbd3b: "bwaelh" +0xbd3c: "bwaem" +0xbd3d: "bwaeb" +0xbd3e: "bwaebs" +0xbd3f: "bwaes" +0xbd40: "bwaess" +0xbd41: "bwaeng" +0xbd42: "bwaej" +0xbd43: "bwaec" +0xbd44: "bwaek" +0xbd45: "bwaet" +0xbd46: "bwaep" +0xbd47: "bwaeh" +0xbd48: "boe" +0xbd49: "boeg" +0xbd4a: "boegg" +0xbd4b: "boegs" +0xbd4c: "boen" +0xbd4d: "boenj" +0xbd4e: "boenh" +0xbd4f: "boed" +0xbd50: "boel" +0xbd51: "boelg" +0xbd52: "boelm" +0xbd53: "boelb" +0xbd54: "boels" +0xbd55: "boelt" +0xbd56: "boelp" +0xbd57: "boelh" +0xbd58: "boem" +0xbd59: "boeb" +0xbd5a: "boebs" +0xbd5b: "boes" +0xbd5c: "boess" +0xbd5d: "boeng" +0xbd5e: "boej" +0xbd5f: "boec" +0xbd60: "boek" +0xbd61: "boet" +0xbd62: "boep" +0xbd63: "boeh" +0xbd64: "byo" +0xbd65: "byog" +0xbd66: "byogg" +0xbd67: "byogs" +0xbd68: "byon" +0xbd69: "byonj" +0xbd6a: "byonh" +0xbd6b: "byod" +0xbd6c: "byol" +0xbd6d: "byolg" +0xbd6e: "byolm" +0xbd6f: "byolb" +0xbd70: "byols" +0xbd71: "byolt" +0xbd72: "byolp" +0xbd73: "byolh" +0xbd74: "byom" +0xbd75: "byob" +0xbd76: "byobs" +0xbd77: "byos" +0xbd78: "byoss" +0xbd79: "byong" +0xbd7a: "byoj" +0xbd7b: "byoc" +0xbd7c: "byok" +0xbd7d: "byot" +0xbd7e: "byop" +0xbd7f: "byoh" +0xbd80: "bu" +0xbd81: "bug" +0xbd82: "bugg" +0xbd83: "bugs" +0xbd84: "bun" +0xbd85: "bunj" +0xbd86: "bunh" +0xbd87: "bud" +0xbd88: "bul" +0xbd89: "bulg" +0xbd8a: "bulm" +0xbd8b: "bulb" +0xbd8c: "buls" +0xbd8d: "bult" +0xbd8e: "bulp" +0xbd8f: "bulh" +0xbd90: "bum" +0xbd91: "bub" +0xbd92: "bubs" +0xbd93: "bus" +0xbd94: "buss" +0xbd95: "bung" +0xbd96: "buj" +0xbd97: "buc" +0xbd98: "buk" +0xbd99: "but" +0xbd9a: "bup" +0xbd9b: "buh" +0xbd9c: "bweo" +0xbd9d: "bweog" +0xbd9e: "bweogg" +0xbd9f: "bweogs" +0xbda0: "bweon" +0xbda1: "bweonj" +0xbda2: "bweonh" +0xbda3: "bweod" +0xbda4: "bweol" +0xbda5: "bweolg" +0xbda6: "bweolm" +0xbda7: "bweolb" +0xbda8: "bweols" +0xbda9: "bweolt" +0xbdaa: "bweolp" +0xbdab: "bweolh" +0xbdac: "bweom" +0xbdad: "bweob" +0xbdae: "bweobs" +0xbdaf: "bweos" +0xbdb0: "bweoss" +0xbdb1: "bweong" +0xbdb2: "bweoj" +0xbdb3: "bweoc" +0xbdb4: "bweok" +0xbdb5: "bweot" +0xbdb6: "bweop" +0xbdb7: "bweoh" +0xbdb8: "bwe" +0xbdb9: "bweg" +0xbdba: "bwegg" +0xbdbb: "bwegs" +0xbdbc: "bwen" +0xbdbd: "bwenj" +0xbdbe: "bwenh" +0xbdbf: "bwed" +0xbdc0: "bwel" +0xbdc1: "bwelg" +0xbdc2: "bwelm" +0xbdc3: "bwelb" +0xbdc4: "bwels" +0xbdc5: "bwelt" +0xbdc6: "bwelp" +0xbdc7: "bwelh" +0xbdc8: "bwem" +0xbdc9: "bweb" +0xbdca: "bwebs" +0xbdcb: "bwes" +0xbdcc: "bwess" +0xbdcd: "bweng" +0xbdce: "bwej" +0xbdcf: "bwec" +0xbdd0: "bwek" +0xbdd1: "bwet" +0xbdd2: "bwep" +0xbdd3: "bweh" +0xbdd4: "bwi" +0xbdd5: "bwig" +0xbdd6: "bwigg" +0xbdd7: "bwigs" +0xbdd8: "bwin" +0xbdd9: "bwinj" +0xbdda: "bwinh" +0xbddb: "bwid" +0xbddc: "bwil" +0xbddd: "bwilg" +0xbdde: "bwilm" +0xbddf: "bwilb" +0xbde0: "bwils" +0xbde1: "bwilt" +0xbde2: "bwilp" +0xbde3: "bwilh" +0xbde4: "bwim" +0xbde5: "bwib" +0xbde6: "bwibs" +0xbde7: "bwis" +0xbde8: "bwiss" +0xbde9: "bwing" +0xbdea: "bwij" +0xbdeb: "bwic" +0xbdec: "bwik" +0xbded: "bwit" +0xbdee: "bwip" +0xbdef: "bwih" +0xbdf0: "byu" +0xbdf1: "byug" +0xbdf2: "byugg" +0xbdf3: "byugs" +0xbdf4: "byun" +0xbdf5: "byunj" +0xbdf6: "byunh" +0xbdf7: "byud" +0xbdf8: "byul" +0xbdf9: "byulg" +0xbdfa: "byulm" +0xbdfb: "byulb" +0xbdfc: "byuls" +0xbdfd: "byult" +0xbdfe: "byulp" +0xbdff: "byulh" +/* x0be */ +0xbe00: "byum" +0xbe01: "byub" +0xbe02: "byubs" +0xbe03: "byus" +0xbe04: "byuss" +0xbe05: "byung" +0xbe06: "byuj" +0xbe07: "byuc" +0xbe08: "byuk" +0xbe09: "byut" +0xbe0a: "byup" +0xbe0b: "byuh" +0xbe0c: "beu" +0xbe0d: "beug" +0xbe0e: "beugg" +0xbe0f: "beugs" +0xbe10: "beun" +0xbe11: "beunj" +0xbe12: "beunh" +0xbe13: "beud" +0xbe14: "beul" +0xbe15: "beulg" +0xbe16: "beulm" +0xbe17: "beulb" +0xbe18: "beuls" +0xbe19: "beult" +0xbe1a: "beulp" +0xbe1b: "beulh" +0xbe1c: "beum" +0xbe1d: "beub" +0xbe1e: "beubs" +0xbe1f: "beus" +0xbe20: "beuss" +0xbe21: "beung" +0xbe22: "beuj" +0xbe23: "beuc" +0xbe24: "beuk" +0xbe25: "beut" +0xbe26: "beup" +0xbe27: "beuh" +0xbe28: "byi" +0xbe29: "byig" +0xbe2a: "byigg" +0xbe2b: "byigs" +0xbe2c: "byin" +0xbe2d: "byinj" +0xbe2e: "byinh" +0xbe2f: "byid" +0xbe30: "byil" +0xbe31: "byilg" +0xbe32: "byilm" +0xbe33: "byilb" +0xbe34: "byils" +0xbe35: "byilt" +0xbe36: "byilp" +0xbe37: "byilh" +0xbe38: "byim" +0xbe39: "byib" +0xbe3a: "byibs" +0xbe3b: "byis" +0xbe3c: "byiss" +0xbe3d: "bying" +0xbe3e: "byij" +0xbe3f: "byic" +0xbe40: "byik" +0xbe41: "byit" +0xbe42: "byip" +0xbe43: "byih" +0xbe44: "bi" +0xbe45: "big" +0xbe46: "bigg" +0xbe47: "bigs" +0xbe48: "bin" +0xbe49: "binj" +0xbe4a: "binh" +0xbe4b: "bid" +0xbe4c: "bil" +0xbe4d: "bilg" +0xbe4e: "bilm" +0xbe4f: "bilb" +0xbe50: "bils" +0xbe51: "bilt" +0xbe52: "bilp" +0xbe53: "bilh" +0xbe54: "bim" +0xbe55: "bib" +0xbe56: "bibs" +0xbe57: "bis" +0xbe58: "biss" +0xbe59: "bing" +0xbe5a: "bij" +0xbe5b: "bic" +0xbe5c: "bik" +0xbe5d: "bit" +0xbe5e: "bip" +0xbe5f: "bih" +0xbe60: "bba" +0xbe61: "bbag" +0xbe62: "bbagg" +0xbe63: "bbags" +0xbe64: "bban" +0xbe65: "bbanj" +0xbe66: "bbanh" +0xbe67: "bbad" +0xbe68: "bbal" +0xbe69: "bbalg" +0xbe6a: "bbalm" +0xbe6b: "bbalb" +0xbe6c: "bbals" +0xbe6d: "bbalt" +0xbe6e: "bbalp" +0xbe6f: "bbalh" +0xbe70: "bbam" +0xbe71: "bbab" +0xbe72: "bbabs" +0xbe73: "bbas" +0xbe74: "bbass" +0xbe75: "bbang" +0xbe76: "bbaj" +0xbe77: "bbac" +0xbe78: "bbak" +0xbe79: "bbat" +0xbe7a: "bbap" +0xbe7b: "bbah" +0xbe7c: "bbae" +0xbe7d: "bbaeg" +0xbe7e: "bbaegg" +0xbe7f: "bbaegs" +0xbe80: "bbaen" +0xbe81: "bbaenj" +0xbe82: "bbaenh" +0xbe83: "bbaed" +0xbe84: "bbael" +0xbe85: "bbaelg" +0xbe86: "bbaelm" +0xbe87: "bbaelb" +0xbe88: "bbaels" +0xbe89: "bbaelt" +0xbe8a: "bbaelp" +0xbe8b: "bbaelh" +0xbe8c: "bbaem" +0xbe8d: "bbaeb" +0xbe8e: "bbaebs" +0xbe8f: "bbaes" +0xbe90: "bbaess" +0xbe91: "bbaeng" +0xbe92: "bbaej" +0xbe93: "bbaec" +0xbe94: "bbaek" +0xbe95: "bbaet" +0xbe96: "bbaep" +0xbe97: "bbaeh" +0xbe98: "bbya" +0xbe99: "bbyag" +0xbe9a: "bbyagg" +0xbe9b: "bbyags" +0xbe9c: "bbyan" +0xbe9d: "bbyanj" +0xbe9e: "bbyanh" +0xbe9f: "bbyad" +0xbea0: "bbyal" +0xbea1: "bbyalg" +0xbea2: "bbyalm" +0xbea3: "bbyalb" +0xbea4: "bbyals" +0xbea5: "bbyalt" +0xbea6: "bbyalp" +0xbea7: "bbyalh" +0xbea8: "bbyam" +0xbea9: "bbyab" +0xbeaa: "bbyabs" +0xbeab: "bbyas" +0xbeac: "bbyass" +0xbead: "bbyang" +0xbeae: "bbyaj" +0xbeaf: "bbyac" +0xbeb0: "bbyak" +0xbeb1: "bbyat" +0xbeb2: "bbyap" +0xbeb3: "bbyah" +0xbeb4: "bbyae" +0xbeb5: "bbyaeg" +0xbeb6: "bbyaegg" +0xbeb7: "bbyaegs" +0xbeb8: "bbyaen" +0xbeb9: "bbyaenj" +0xbeba: "bbyaenh" +0xbebb: "bbyaed" +0xbebc: "bbyael" +0xbebd: "bbyaelg" +0xbebe: "bbyaelm" +0xbebf: "bbyaelb" +0xbec0: "bbyaels" +0xbec1: "bbyaelt" +0xbec2: "bbyaelp" +0xbec3: "bbyaelh" +0xbec4: "bbyaem" +0xbec5: "bbyaeb" +0xbec6: "bbyaebs" +0xbec7: "bbyaes" +0xbec8: "bbyaess" +0xbec9: "bbyaeng" +0xbeca: "bbyaej" +0xbecb: "bbyaec" +0xbecc: "bbyaek" +0xbecd: "bbyaet" +0xbece: "bbyaep" +0xbecf: "bbyaeh" +0xbed0: "bbeo" +0xbed1: "bbeog" +0xbed2: "bbeogg" +0xbed3: "bbeogs" +0xbed4: "bbeon" +0xbed5: "bbeonj" +0xbed6: "bbeonh" +0xbed7: "bbeod" +0xbed8: "bbeol" +0xbed9: "bbeolg" +0xbeda: "bbeolm" +0xbedb: "bbeolb" +0xbedc: "bbeols" +0xbedd: "bbeolt" +0xbede: "bbeolp" +0xbedf: "bbeolh" +0xbee0: "bbeom" +0xbee1: "bbeob" +0xbee2: "bbeobs" +0xbee3: "bbeos" +0xbee4: "bbeoss" +0xbee5: "bbeong" +0xbee6: "bbeoj" +0xbee7: "bbeoc" +0xbee8: "bbeok" +0xbee9: "bbeot" +0xbeea: "bbeop" +0xbeeb: "bbeoh" +0xbeec: "bbe" +0xbeed: "bbeg" +0xbeee: "bbegg" +0xbeef: "bbegs" +0xbef0: "bben" +0xbef1: "bbenj" +0xbef2: "bbenh" +0xbef3: "bbed" +0xbef4: "bbel" +0xbef5: "bbelg" +0xbef6: "bbelm" +0xbef7: "bbelb" +0xbef8: "bbels" +0xbef9: "bbelt" +0xbefa: "bbelp" +0xbefb: "bbelh" +0xbefc: "bbem" +0xbefd: "bbeb" +0xbefe: "bbebs" +0xbeff: "bbes" +/* x0bf */ +0xbf00: "bbess" +0xbf01: "bbeng" +0xbf02: "bbej" +0xbf03: "bbec" +0xbf04: "bbek" +0xbf05: "bbet" +0xbf06: "bbep" +0xbf07: "bbeh" +0xbf08: "bbyeo" +0xbf09: "bbyeog" +0xbf0a: "bbyeogg" +0xbf0b: "bbyeogs" +0xbf0c: "bbyeon" +0xbf0d: "bbyeonj" +0xbf0e: "bbyeonh" +0xbf0f: "bbyeod" +0xbf10: "bbyeol" +0xbf11: "bbyeolg" +0xbf12: "bbyeolm" +0xbf13: "bbyeolb" +0xbf14: "bbyeols" +0xbf15: "bbyeolt" +0xbf16: "bbyeolp" +0xbf17: "bbyeolh" +0xbf18: "bbyeom" +0xbf19: "bbyeob" +0xbf1a: "bbyeobs" +0xbf1b: "bbyeos" +0xbf1c: "bbyeoss" +0xbf1d: "bbyeong" +0xbf1e: "bbyeoj" +0xbf1f: "bbyeoc" +0xbf20: "bbyeok" +0xbf21: "bbyeot" +0xbf22: "bbyeop" +0xbf23: "bbyeoh" +0xbf24: "bbye" +0xbf25: "bbyeg" +0xbf26: "bbyegg" +0xbf27: "bbyegs" +0xbf28: "bbyen" +0xbf29: "bbyenj" +0xbf2a: "bbyenh" +0xbf2b: "bbyed" +0xbf2c: "bbyel" +0xbf2d: "bbyelg" +0xbf2e: "bbyelm" +0xbf2f: "bbyelb" +0xbf30: "bbyels" +0xbf31: "bbyelt" +0xbf32: "bbyelp" +0xbf33: "bbyelh" +0xbf34: "bbyem" +0xbf35: "bbyeb" +0xbf36: "bbyebs" +0xbf37: "bbyes" +0xbf38: "bbyess" +0xbf39: "bbyeng" +0xbf3a: "bbyej" +0xbf3b: "bbyec" +0xbf3c: "bbyek" +0xbf3d: "bbyet" +0xbf3e: "bbyep" +0xbf3f: "bbyeh" +0xbf40: "bbo" +0xbf41: "bbog" +0xbf42: "bbogg" +0xbf43: "bbogs" +0xbf44: "bbon" +0xbf45: "bbonj" +0xbf46: "bbonh" +0xbf47: "bbod" +0xbf48: "bbol" +0xbf49: "bbolg" +0xbf4a: "bbolm" +0xbf4b: "bbolb" +0xbf4c: "bbols" +0xbf4d: "bbolt" +0xbf4e: "bbolp" +0xbf4f: "bbolh" +0xbf50: "bbom" +0xbf51: "bbob" +0xbf52: "bbobs" +0xbf53: "bbos" +0xbf54: "bboss" +0xbf55: "bbong" +0xbf56: "bboj" +0xbf57: "bboc" +0xbf58: "bbok" +0xbf59: "bbot" +0xbf5a: "bbop" +0xbf5b: "bboh" +0xbf5c: "bbwa" +0xbf5d: "bbwag" +0xbf5e: "bbwagg" +0xbf5f: "bbwags" +0xbf60: "bbwan" +0xbf61: "bbwanj" +0xbf62: "bbwanh" +0xbf63: "bbwad" +0xbf64: "bbwal" +0xbf65: "bbwalg" +0xbf66: "bbwalm" +0xbf67: "bbwalb" +0xbf68: "bbwals" +0xbf69: "bbwalt" +0xbf6a: "bbwalp" +0xbf6b: "bbwalh" +0xbf6c: "bbwam" +0xbf6d: "bbwab" +0xbf6e: "bbwabs" +0xbf6f: "bbwas" +0xbf70: "bbwass" +0xbf71: "bbwang" +0xbf72: "bbwaj" +0xbf73: "bbwac" +0xbf74: "bbwak" +0xbf75: "bbwat" +0xbf76: "bbwap" +0xbf77: "bbwah" +0xbf78: "bbwae" +0xbf79: "bbwaeg" +0xbf7a: "bbwaegg" +0xbf7b: "bbwaegs" +0xbf7c: "bbwaen" +0xbf7d: "bbwaenj" +0xbf7e: "bbwaenh" +0xbf7f: "bbwaed" +0xbf80: "bbwael" +0xbf81: "bbwaelg" +0xbf82: "bbwaelm" +0xbf83: "bbwaelb" +0xbf84: "bbwaels" +0xbf85: "bbwaelt" +0xbf86: "bbwaelp" +0xbf87: "bbwaelh" +0xbf88: "bbwaem" +0xbf89: "bbwaeb" +0xbf8a: "bbwaebs" +0xbf8b: "bbwaes" +0xbf8c: "bbwaess" +0xbf8d: "bbwaeng" +0xbf8e: "bbwaej" +0xbf8f: "bbwaec" +0xbf90: "bbwaek" +0xbf91: "bbwaet" +0xbf92: "bbwaep" +0xbf93: "bbwaeh" +0xbf94: "bboe" +0xbf95: "bboeg" +0xbf96: "bboegg" +0xbf97: "bboegs" +0xbf98: "bboen" +0xbf99: "bboenj" +0xbf9a: "bboenh" +0xbf9b: "bboed" +0xbf9c: "bboel" +0xbf9d: "bboelg" +0xbf9e: "bboelm" +0xbf9f: "bboelb" +0xbfa0: "bboels" +0xbfa1: "bboelt" +0xbfa2: "bboelp" +0xbfa3: "bboelh" +0xbfa4: "bboem" +0xbfa5: "bboeb" +0xbfa6: "bboebs" +0xbfa7: "bboes" +0xbfa8: "bboess" +0xbfa9: "bboeng" +0xbfaa: "bboej" +0xbfab: "bboec" +0xbfac: "bboek" +0xbfad: "bboet" +0xbfae: "bboep" +0xbfaf: "bboeh" +0xbfb0: "bbyo" +0xbfb1: "bbyog" +0xbfb2: "bbyogg" +0xbfb3: "bbyogs" +0xbfb4: "bbyon" +0xbfb5: "bbyonj" +0xbfb6: "bbyonh" +0xbfb7: "bbyod" +0xbfb8: "bbyol" +0xbfb9: "bbyolg" +0xbfba: "bbyolm" +0xbfbb: "bbyolb" +0xbfbc: "bbyols" +0xbfbd: "bbyolt" +0xbfbe: "bbyolp" +0xbfbf: "bbyolh" +0xbfc0: "bbyom" +0xbfc1: "bbyob" +0xbfc2: "bbyobs" +0xbfc3: "bbyos" +0xbfc4: "bbyoss" +0xbfc5: "bbyong" +0xbfc6: "bbyoj" +0xbfc7: "bbyoc" +0xbfc8: "bbyok" +0xbfc9: "bbyot" +0xbfca: "bbyop" +0xbfcb: "bbyoh" +0xbfcc: "bbu" +0xbfcd: "bbug" +0xbfce: "bbugg" +0xbfcf: "bbugs" +0xbfd0: "bbun" +0xbfd1: "bbunj" +0xbfd2: "bbunh" +0xbfd3: "bbud" +0xbfd4: "bbul" +0xbfd5: "bbulg" +0xbfd6: "bbulm" +0xbfd7: "bbulb" +0xbfd8: "bbuls" +0xbfd9: "bbult" +0xbfda: "bbulp" +0xbfdb: "bbulh" +0xbfdc: "bbum" +0xbfdd: "bbub" +0xbfde: "bbubs" +0xbfdf: "bbus" +0xbfe0: "bbuss" +0xbfe1: "bbung" +0xbfe2: "bbuj" +0xbfe3: "bbuc" +0xbfe4: "bbuk" +0xbfe5: "bbut" +0xbfe6: "bbup" +0xbfe7: "bbuh" +0xbfe8: "bbweo" +0xbfe9: "bbweog" +0xbfea: "bbweogg" +0xbfeb: "bbweogs" +0xbfec: "bbweon" +0xbfed: "bbweonj" +0xbfee: "bbweonh" +0xbfef: "bbweod" +0xbff0: "bbweol" +0xbff1: "bbweolg" +0xbff2: "bbweolm" +0xbff3: "bbweolb" +0xbff4: "bbweols" +0xbff5: "bbweolt" +0xbff6: "bbweolp" +0xbff7: "bbweolh" +0xbff8: "bbweom" +0xbff9: "bbweob" +0xbffa: "bbweobs" +0xbffb: "bbweos" +0xbffc: "bbweoss" +0xbffd: "bbweong" +0xbffe: "bbweoj" +0xbfff: "bbweoc" +/* x0c0 */ +0xc000: "bbweok" +0xc001: "bbweot" +0xc002: "bbweop" +0xc003: "bbweoh" +0xc004: "bbwe" +0xc005: "bbweg" +0xc006: "bbwegg" +0xc007: "bbwegs" +0xc008: "bbwen" +0xc009: "bbwenj" +0xc00a: "bbwenh" +0xc00b: "bbwed" +0xc00c: "bbwel" +0xc00d: "bbwelg" +0xc00e: "bbwelm" +0xc00f: "bbwelb" +0xc010: "bbwels" +0xc011: "bbwelt" +0xc012: "bbwelp" +0xc013: "bbwelh" +0xc014: "bbwem" +0xc015: "bbweb" +0xc016: "bbwebs" +0xc017: "bbwes" +0xc018: "bbwess" +0xc019: "bbweng" +0xc01a: "bbwej" +0xc01b: "bbwec" +0xc01c: "bbwek" +0xc01d: "bbwet" +0xc01e: "bbwep" +0xc01f: "bbweh" +0xc020: "bbwi" +0xc021: "bbwig" +0xc022: "bbwigg" +0xc023: "bbwigs" +0xc024: "bbwin" +0xc025: "bbwinj" +0xc026: "bbwinh" +0xc027: "bbwid" +0xc028: "bbwil" +0xc029: "bbwilg" +0xc02a: "bbwilm" +0xc02b: "bbwilb" +0xc02c: "bbwils" +0xc02d: "bbwilt" +0xc02e: "bbwilp" +0xc02f: "bbwilh" +0xc030: "bbwim" +0xc031: "bbwib" +0xc032: "bbwibs" +0xc033: "bbwis" +0xc034: "bbwiss" +0xc035: "bbwing" +0xc036: "bbwij" +0xc037: "bbwic" +0xc038: "bbwik" +0xc039: "bbwit" +0xc03a: "bbwip" +0xc03b: "bbwih" +0xc03c: "bbyu" +0xc03d: "bbyug" +0xc03e: "bbyugg" +0xc03f: "bbyugs" +0xc040: "bbyun" +0xc041: "bbyunj" +0xc042: "bbyunh" +0xc043: "bbyud" +0xc044: "bbyul" +0xc045: "bbyulg" +0xc046: "bbyulm" +0xc047: "bbyulb" +0xc048: "bbyuls" +0xc049: "bbyult" +0xc04a: "bbyulp" +0xc04b: "bbyulh" +0xc04c: "bbyum" +0xc04d: "bbyub" +0xc04e: "bbyubs" +0xc04f: "bbyus" +0xc050: "bbyuss" +0xc051: "bbyung" +0xc052: "bbyuj" +0xc053: "bbyuc" +0xc054: "bbyuk" +0xc055: "bbyut" +0xc056: "bbyup" +0xc057: "bbyuh" +0xc058: "bbeu" +0xc059: "bbeug" +0xc05a: "bbeugg" +0xc05b: "bbeugs" +0xc05c: "bbeun" +0xc05d: "bbeunj" +0xc05e: "bbeunh" +0xc05f: "bbeud" +0xc060: "bbeul" +0xc061: "bbeulg" +0xc062: "bbeulm" +0xc063: "bbeulb" +0xc064: "bbeuls" +0xc065: "bbeult" +0xc066: "bbeulp" +0xc067: "bbeulh" +0xc068: "bbeum" +0xc069: "bbeub" +0xc06a: "bbeubs" +0xc06b: "bbeus" +0xc06c: "bbeuss" +0xc06d: "bbeung" +0xc06e: "bbeuj" +0xc06f: "bbeuc" +0xc070: "bbeuk" +0xc071: "bbeut" +0xc072: "bbeup" +0xc073: "bbeuh" +0xc074: "bbyi" +0xc075: "bbyig" +0xc076: "bbyigg" +0xc077: "bbyigs" +0xc078: "bbyin" +0xc079: "bbyinj" +0xc07a: "bbyinh" +0xc07b: "bbyid" +0xc07c: "bbyil" +0xc07d: "bbyilg" +0xc07e: "bbyilm" +0xc07f: "bbyilb" +0xc080: "bbyils" +0xc081: "bbyilt" +0xc082: "bbyilp" +0xc083: "bbyilh" +0xc084: "bbyim" +0xc085: "bbyib" +0xc086: "bbyibs" +0xc087: "bbyis" +0xc088: "bbyiss" +0xc089: "bbying" +0xc08a: "bbyij" +0xc08b: "bbyic" +0xc08c: "bbyik" +0xc08d: "bbyit" +0xc08e: "bbyip" +0xc08f: "bbyih" +0xc090: "bbi" +0xc091: "bbig" +0xc092: "bbigg" +0xc093: "bbigs" +0xc094: "bbin" +0xc095: "bbinj" +0xc096: "bbinh" +0xc097: "bbid" +0xc098: "bbil" +0xc099: "bbilg" +0xc09a: "bbilm" +0xc09b: "bbilb" +0xc09c: "bbils" +0xc09d: "bbilt" +0xc09e: "bbilp" +0xc09f: "bbilh" +0xc0a0: "bbim" +0xc0a1: "bbib" +0xc0a2: "bbibs" +0xc0a3: "bbis" +0xc0a4: "bbiss" +0xc0a5: "bbing" +0xc0a6: "bbij" +0xc0a7: "bbic" +0xc0a8: "bbik" +0xc0a9: "bbit" +0xc0aa: "bbip" +0xc0ab: "bbih" +0xc0ac: "sa" +0xc0ad: "sag" +0xc0ae: "sagg" +0xc0af: "sags" +0xc0b0: "san" +0xc0b1: "sanj" +0xc0b2: "sanh" +0xc0b3: "sad" +0xc0b4: "sal" +0xc0b5: "salg" +0xc0b6: "salm" +0xc0b7: "salb" +0xc0b8: "sals" +0xc0b9: "salt" +0xc0ba: "salp" +0xc0bb: "salh" +0xc0bc: "sam" +0xc0bd: "sab" +0xc0be: "sabs" +0xc0bf: "sas" +0xc0c0: "sass" +0xc0c1: "sang" +0xc0c2: "saj" +0xc0c3: "sac" +0xc0c4: "sak" +0xc0c5: "sat" +0xc0c6: "sap" +0xc0c7: "sah" +0xc0c8: "sae" +0xc0c9: "saeg" +0xc0ca: "saegg" +0xc0cb: "saegs" +0xc0cc: "saen" +0xc0cd: "saenj" +0xc0ce: "saenh" +0xc0cf: "saed" +0xc0d0: "sael" +0xc0d1: "saelg" +0xc0d2: "saelm" +0xc0d3: "saelb" +0xc0d4: "saels" +0xc0d5: "saelt" +0xc0d6: "saelp" +0xc0d7: "saelh" +0xc0d8: "saem" +0xc0d9: "saeb" +0xc0da: "saebs" +0xc0db: "saes" +0xc0dc: "saess" +0xc0dd: "saeng" +0xc0de: "saej" +0xc0df: "saec" +0xc0e0: "saek" +0xc0e1: "saet" +0xc0e2: "saep" +0xc0e3: "saeh" +0xc0e4: "sya" +0xc0e5: "syag" +0xc0e6: "syagg" +0xc0e7: "syags" +0xc0e8: "syan" +0xc0e9: "syanj" +0xc0ea: "syanh" +0xc0eb: "syad" +0xc0ec: "syal" +0xc0ed: "syalg" +0xc0ee: "syalm" +0xc0ef: "syalb" +0xc0f0: "syals" +0xc0f1: "syalt" +0xc0f2: "syalp" +0xc0f3: "syalh" +0xc0f4: "syam" +0xc0f5: "syab" +0xc0f6: "syabs" +0xc0f7: "syas" +0xc0f8: "syass" +0xc0f9: "syang" +0xc0fa: "syaj" +0xc0fb: "syac" +0xc0fc: "syak" +0xc0fd: "syat" +0xc0fe: "syap" +0xc0ff: "syah" +/* x0c1 */ +0xc100: "syae" +0xc101: "syaeg" +0xc102: "syaegg" +0xc103: "syaegs" +0xc104: "syaen" +0xc105: "syaenj" +0xc106: "syaenh" +0xc107: "syaed" +0xc108: "syael" +0xc109: "syaelg" +0xc10a: "syaelm" +0xc10b: "syaelb" +0xc10c: "syaels" +0xc10d: "syaelt" +0xc10e: "syaelp" +0xc10f: "syaelh" +0xc110: "syaem" +0xc111: "syaeb" +0xc112: "syaebs" +0xc113: "syaes" +0xc114: "syaess" +0xc115: "syaeng" +0xc116: "syaej" +0xc117: "syaec" +0xc118: "syaek" +0xc119: "syaet" +0xc11a: "syaep" +0xc11b: "syaeh" +0xc11c: "seo" +0xc11d: "seog" +0xc11e: "seogg" +0xc11f: "seogs" +0xc120: "seon" +0xc121: "seonj" +0xc122: "seonh" +0xc123: "seod" +0xc124: "seol" +0xc125: "seolg" +0xc126: "seolm" +0xc127: "seolb" +0xc128: "seols" +0xc129: "seolt" +0xc12a: "seolp" +0xc12b: "seolh" +0xc12c: "seom" +0xc12d: "seob" +0xc12e: "seobs" +0xc12f: "seos" +0xc130: "seoss" +0xc131: "seong" +0xc132: "seoj" +0xc133: "seoc" +0xc134: "seok" +0xc135: "seot" +0xc136: "seop" +0xc137: "seoh" +0xc138: "se" +0xc139: "seg" +0xc13a: "segg" +0xc13b: "segs" +0xc13c: "sen" +0xc13d: "senj" +0xc13e: "senh" +0xc13f: "sed" +0xc140: "sel" +0xc141: "selg" +0xc142: "selm" +0xc143: "selb" +0xc144: "sels" +0xc145: "selt" +0xc146: "selp" +0xc147: "selh" +0xc148: "sem" +0xc149: "seb" +0xc14a: "sebs" +0xc14b: "ses" +0xc14c: "sess" +0xc14d: "seng" +0xc14e: "sej" +0xc14f: "sec" +0xc150: "sek" +0xc151: "set" +0xc152: "sep" +0xc153: "seh" +0xc154: "syeo" +0xc155: "syeog" +0xc156: "syeogg" +0xc157: "syeogs" +0xc158: "syeon" +0xc159: "syeonj" +0xc15a: "syeonh" +0xc15b: "syeod" +0xc15c: "syeol" +0xc15d: "syeolg" +0xc15e: "syeolm" +0xc15f: "syeolb" +0xc160: "syeols" +0xc161: "syeolt" +0xc162: "syeolp" +0xc163: "syeolh" +0xc164: "syeom" +0xc165: "syeob" +0xc166: "syeobs" +0xc167: "syeos" +0xc168: "syeoss" +0xc169: "syeong" +0xc16a: "syeoj" +0xc16b: "syeoc" +0xc16c: "syeok" +0xc16d: "syeot" +0xc16e: "syeop" +0xc16f: "syeoh" +0xc170: "sye" +0xc171: "syeg" +0xc172: "syegg" +0xc173: "syegs" +0xc174: "syen" +0xc175: "syenj" +0xc176: "syenh" +0xc177: "syed" +0xc178: "syel" +0xc179: "syelg" +0xc17a: "syelm" +0xc17b: "syelb" +0xc17c: "syels" +0xc17d: "syelt" +0xc17e: "syelp" +0xc17f: "syelh" +0xc180: "syem" +0xc181: "syeb" +0xc182: "syebs" +0xc183: "syes" +0xc184: "syess" +0xc185: "syeng" +0xc186: "syej" +0xc187: "syec" +0xc188: "syek" +0xc189: "syet" +0xc18a: "syep" +0xc18b: "syeh" +0xc18c: "so" +0xc18d: "sog" +0xc18e: "sogg" +0xc18f: "sogs" +0xc190: "son" +0xc191: "sonj" +0xc192: "sonh" +0xc193: "sod" +0xc194: "sol" +0xc195: "solg" +0xc196: "solm" +0xc197: "solb" +0xc198: "sols" +0xc199: "solt" +0xc19a: "solp" +0xc19b: "solh" +0xc19c: "som" +0xc19d: "sob" +0xc19e: "sobs" +0xc19f: "sos" +0xc1a0: "soss" +0xc1a1: "song" +0xc1a2: "soj" +0xc1a3: "soc" +0xc1a4: "sok" +0xc1a5: "sot" +0xc1a6: "sop" +0xc1a7: "soh" +0xc1a8: "swa" +0xc1a9: "swag" +0xc1aa: "swagg" +0xc1ab: "swags" +0xc1ac: "swan" +0xc1ad: "swanj" +0xc1ae: "swanh" +0xc1af: "swad" +0xc1b0: "swal" +0xc1b1: "swalg" +0xc1b2: "swalm" +0xc1b3: "swalb" +0xc1b4: "swals" +0xc1b5: "swalt" +0xc1b6: "swalp" +0xc1b7: "swalh" +0xc1b8: "swam" +0xc1b9: "swab" +0xc1ba: "swabs" +0xc1bb: "swas" +0xc1bc: "swass" +0xc1bd: "swang" +0xc1be: "swaj" +0xc1bf: "swac" +0xc1c0: "swak" +0xc1c1: "swat" +0xc1c2: "swap" +0xc1c3: "swah" +0xc1c4: "swae" +0xc1c5: "swaeg" +0xc1c6: "swaegg" +0xc1c7: "swaegs" +0xc1c8: "swaen" +0xc1c9: "swaenj" +0xc1ca: "swaenh" +0xc1cb: "swaed" +0xc1cc: "swael" +0xc1cd: "swaelg" +0xc1ce: "swaelm" +0xc1cf: "swaelb" +0xc1d0: "swaels" +0xc1d1: "swaelt" +0xc1d2: "swaelp" +0xc1d3: "swaelh" +0xc1d4: "swaem" +0xc1d5: "swaeb" +0xc1d6: "swaebs" +0xc1d7: "swaes" +0xc1d8: "swaess" +0xc1d9: "swaeng" +0xc1da: "swaej" +0xc1db: "swaec" +0xc1dc: "swaek" +0xc1dd: "swaet" +0xc1de: "swaep" +0xc1df: "swaeh" +0xc1e0: "soe" +0xc1e1: "soeg" +0xc1e2: "soegg" +0xc1e3: "soegs" +0xc1e4: "soen" +0xc1e5: "soenj" +0xc1e6: "soenh" +0xc1e7: "soed" +0xc1e8: "soel" +0xc1e9: "soelg" +0xc1ea: "soelm" +0xc1eb: "soelb" +0xc1ec: "soels" +0xc1ed: "soelt" +0xc1ee: "soelp" +0xc1ef: "soelh" +0xc1f0: "soem" +0xc1f1: "soeb" +0xc1f2: "soebs" +0xc1f3: "soes" +0xc1f4: "soess" +0xc1f5: "soeng" +0xc1f6: "soej" +0xc1f7: "soec" +0xc1f8: "soek" +0xc1f9: "soet" +0xc1fa: "soep" +0xc1fb: "soeh" +0xc1fc: "syo" +0xc1fd: "syog" +0xc1fe: "syogg" +0xc1ff: "syogs" +/* x0c2 */ +0xc200: "syon" +0xc201: "syonj" +0xc202: "syonh" +0xc203: "syod" +0xc204: "syol" +0xc205: "syolg" +0xc206: "syolm" +0xc207: "syolb" +0xc208: "syols" +0xc209: "syolt" +0xc20a: "syolp" +0xc20b: "syolh" +0xc20c: "syom" +0xc20d: "syob" +0xc20e: "syobs" +0xc20f: "syos" +0xc210: "syoss" +0xc211: "syong" +0xc212: "syoj" +0xc213: "syoc" +0xc214: "syok" +0xc215: "syot" +0xc216: "syop" +0xc217: "syoh" +0xc218: "su" +0xc219: "sug" +0xc21a: "sugg" +0xc21b: "sugs" +0xc21c: "sun" +0xc21d: "sunj" +0xc21e: "sunh" +0xc21f: "sud" +0xc220: "sul" +0xc221: "sulg" +0xc222: "sulm" +0xc223: "sulb" +0xc224: "suls" +0xc225: "sult" +0xc226: "sulp" +0xc227: "sulh" +0xc228: "sum" +0xc229: "sub" +0xc22a: "subs" +0xc22b: "sus" +0xc22c: "suss" +0xc22d: "sung" +0xc22e: "suj" +0xc22f: "suc" +0xc230: "suk" +0xc231: "sut" +0xc232: "sup" +0xc233: "suh" +0xc234: "sweo" +0xc235: "sweog" +0xc236: "sweogg" +0xc237: "sweogs" +0xc238: "sweon" +0xc239: "sweonj" +0xc23a: "sweonh" +0xc23b: "sweod" +0xc23c: "sweol" +0xc23d: "sweolg" +0xc23e: "sweolm" +0xc23f: "sweolb" +0xc240: "sweols" +0xc241: "sweolt" +0xc242: "sweolp" +0xc243: "sweolh" +0xc244: "sweom" +0xc245: "sweob" +0xc246: "sweobs" +0xc247: "sweos" +0xc248: "sweoss" +0xc249: "sweong" +0xc24a: "sweoj" +0xc24b: "sweoc" +0xc24c: "sweok" +0xc24d: "sweot" +0xc24e: "sweop" +0xc24f: "sweoh" +0xc250: "swe" +0xc251: "sweg" +0xc252: "swegg" +0xc253: "swegs" +0xc254: "swen" +0xc255: "swenj" +0xc256: "swenh" +0xc257: "swed" +0xc258: "swel" +0xc259: "swelg" +0xc25a: "swelm" +0xc25b: "swelb" +0xc25c: "swels" +0xc25d: "swelt" +0xc25e: "swelp" +0xc25f: "swelh" +0xc260: "swem" +0xc261: "sweb" +0xc262: "swebs" +0xc263: "swes" +0xc264: "swess" +0xc265: "sweng" +0xc266: "swej" +0xc267: "swec" +0xc268: "swek" +0xc269: "swet" +0xc26a: "swep" +0xc26b: "sweh" +0xc26c: "swi" +0xc26d: "swig" +0xc26e: "swigg" +0xc26f: "swigs" +0xc270: "swin" +0xc271: "swinj" +0xc272: "swinh" +0xc273: "swid" +0xc274: "swil" +0xc275: "swilg" +0xc276: "swilm" +0xc277: "swilb" +0xc278: "swils" +0xc279: "swilt" +0xc27a: "swilp" +0xc27b: "swilh" +0xc27c: "swim" +0xc27d: "swib" +0xc27e: "swibs" +0xc27f: "swis" +0xc280: "swiss" +0xc281: "swing" +0xc282: "swij" +0xc283: "swic" +0xc284: "swik" +0xc285: "swit" +0xc286: "swip" +0xc287: "swih" +0xc288: "syu" +0xc289: "syug" +0xc28a: "syugg" +0xc28b: "syugs" +0xc28c: "syun" +0xc28d: "syunj" +0xc28e: "syunh" +0xc28f: "syud" +0xc290: "syul" +0xc291: "syulg" +0xc292: "syulm" +0xc293: "syulb" +0xc294: "syuls" +0xc295: "syult" +0xc296: "syulp" +0xc297: "syulh" +0xc298: "syum" +0xc299: "syub" +0xc29a: "syubs" +0xc29b: "syus" +0xc29c: "syuss" +0xc29d: "syung" +0xc29e: "syuj" +0xc29f: "syuc" +0xc2a0: "syuk" +0xc2a1: "syut" +0xc2a2: "syup" +0xc2a3: "syuh" +0xc2a4: "seu" +0xc2a5: "seug" +0xc2a6: "seugg" +0xc2a7: "seugs" +0xc2a8: "seun" +0xc2a9: "seunj" +0xc2aa: "seunh" +0xc2ab: "seud" +0xc2ac: "seul" +0xc2ad: "seulg" +0xc2ae: "seulm" +0xc2af: "seulb" +0xc2b0: "seuls" +0xc2b1: "seult" +0xc2b2: "seulp" +0xc2b3: "seulh" +0xc2b4: "seum" +0xc2b5: "seub" +0xc2b6: "seubs" +0xc2b7: "seus" +0xc2b8: "seuss" +0xc2b9: "seung" +0xc2ba: "seuj" +0xc2bb: "seuc" +0xc2bc: "seuk" +0xc2bd: "seut" +0xc2be: "seup" +0xc2bf: "seuh" +0xc2c0: "syi" +0xc2c1: "syig" +0xc2c2: "syigg" +0xc2c3: "syigs" +0xc2c4: "syin" +0xc2c5: "syinj" +0xc2c6: "syinh" +0xc2c7: "syid" +0xc2c8: "syil" +0xc2c9: "syilg" +0xc2ca: "syilm" +0xc2cb: "syilb" +0xc2cc: "syils" +0xc2cd: "syilt" +0xc2ce: "syilp" +0xc2cf: "syilh" +0xc2d0: "syim" +0xc2d1: "syib" +0xc2d2: "syibs" +0xc2d3: "syis" +0xc2d4: "syiss" +0xc2d5: "sying" +0xc2d6: "syij" +0xc2d7: "syic" +0xc2d8: "syik" +0xc2d9: "syit" +0xc2da: "syip" +0xc2db: "syih" +0xc2dc: "si" +0xc2dd: "sig" +0xc2de: "sigg" +0xc2df: "sigs" +0xc2e0: "sin" +0xc2e1: "sinj" +0xc2e2: "sinh" +0xc2e3: "sid" +0xc2e4: "sil" +0xc2e5: "silg" +0xc2e6: "silm" +0xc2e7: "silb" +0xc2e8: "sils" +0xc2e9: "silt" +0xc2ea: "silp" +0xc2eb: "silh" +0xc2ec: "sim" +0xc2ed: "sib" +0xc2ee: "sibs" +0xc2ef: "sis" +0xc2f0: "siss" +0xc2f1: "sing" +0xc2f2: "sij" +0xc2f3: "sic" +0xc2f4: "sik" +0xc2f5: "sit" +0xc2f6: "sip" +0xc2f7: "sih" +0xc2f8: "ssa" +0xc2f9: "ssag" +0xc2fa: "ssagg" +0xc2fb: "ssags" +0xc2fc: "ssan" +0xc2fd: "ssanj" +0xc2fe: "ssanh" +0xc2ff: "ssad" +/* x0c3 */ +0xc300: "ssal" +0xc301: "ssalg" +0xc302: "ssalm" +0xc303: "ssalb" +0xc304: "ssals" +0xc305: "ssalt" +0xc306: "ssalp" +0xc307: "ssalh" +0xc308: "ssam" +0xc309: "ssab" +0xc30a: "ssabs" +0xc30b: "ssas" +0xc30c: "ssass" +0xc30d: "ssang" +0xc30e: "ssaj" +0xc30f: "ssac" +0xc310: "ssak" +0xc311: "ssat" +0xc312: "ssap" +0xc313: "ssah" +0xc314: "ssae" +0xc315: "ssaeg" +0xc316: "ssaegg" +0xc317: "ssaegs" +0xc318: "ssaen" +0xc319: "ssaenj" +0xc31a: "ssaenh" +0xc31b: "ssaed" +0xc31c: "ssael" +0xc31d: "ssaelg" +0xc31e: "ssaelm" +0xc31f: "ssaelb" +0xc320: "ssaels" +0xc321: "ssaelt" +0xc322: "ssaelp" +0xc323: "ssaelh" +0xc324: "ssaem" +0xc325: "ssaeb" +0xc326: "ssaebs" +0xc327: "ssaes" +0xc328: "ssaess" +0xc329: "ssaeng" +0xc32a: "ssaej" +0xc32b: "ssaec" +0xc32c: "ssaek" +0xc32d: "ssaet" +0xc32e: "ssaep" +0xc32f: "ssaeh" +0xc330: "ssya" +0xc331: "ssyag" +0xc332: "ssyagg" +0xc333: "ssyags" +0xc334: "ssyan" +0xc335: "ssyanj" +0xc336: "ssyanh" +0xc337: "ssyad" +0xc338: "ssyal" +0xc339: "ssyalg" +0xc33a: "ssyalm" +0xc33b: "ssyalb" +0xc33c: "ssyals" +0xc33d: "ssyalt" +0xc33e: "ssyalp" +0xc33f: "ssyalh" +0xc340: "ssyam" +0xc341: "ssyab" +0xc342: "ssyabs" +0xc343: "ssyas" +0xc344: "ssyass" +0xc345: "ssyang" +0xc346: "ssyaj" +0xc347: "ssyac" +0xc348: "ssyak" +0xc349: "ssyat" +0xc34a: "ssyap" +0xc34b: "ssyah" +0xc34c: "ssyae" +0xc34d: "ssyaeg" +0xc34e: "ssyaegg" +0xc34f: "ssyaegs" +0xc350: "ssyaen" +0xc351: "ssyaenj" +0xc352: "ssyaenh" +0xc353: "ssyaed" +0xc354: "ssyael" +0xc355: "ssyaelg" +0xc356: "ssyaelm" +0xc357: "ssyaelb" +0xc358: "ssyaels" +0xc359: "ssyaelt" +0xc35a: "ssyaelp" +0xc35b: "ssyaelh" +0xc35c: "ssyaem" +0xc35d: "ssyaeb" +0xc35e: "ssyaebs" +0xc35f: "ssyaes" +0xc360: "ssyaess" +0xc361: "ssyaeng" +0xc362: "ssyaej" +0xc363: "ssyaec" +0xc364: "ssyaek" +0xc365: "ssyaet" +0xc366: "ssyaep" +0xc367: "ssyaeh" +0xc368: "sseo" +0xc369: "sseog" +0xc36a: "sseogg" +0xc36b: "sseogs" +0xc36c: "sseon" +0xc36d: "sseonj" +0xc36e: "sseonh" +0xc36f: "sseod" +0xc370: "sseol" +0xc371: "sseolg" +0xc372: "sseolm" +0xc373: "sseolb" +0xc374: "sseols" +0xc375: "sseolt" +0xc376: "sseolp" +0xc377: "sseolh" +0xc378: "sseom" +0xc379: "sseob" +0xc37a: "sseobs" +0xc37b: "sseos" +0xc37c: "sseoss" +0xc37d: "sseong" +0xc37e: "sseoj" +0xc37f: "sseoc" +0xc380: "sseok" +0xc381: "sseot" +0xc382: "sseop" +0xc383: "sseoh" +0xc384: "sse" +0xc385: "sseg" +0xc386: "ssegg" +0xc387: "ssegs" +0xc388: "ssen" +0xc389: "ssenj" +0xc38a: "ssenh" +0xc38b: "ssed" +0xc38c: "ssel" +0xc38d: "sselg" +0xc38e: "sselm" +0xc38f: "sselb" +0xc390: "ssels" +0xc391: "sselt" +0xc392: "sselp" +0xc393: "sselh" +0xc394: "ssem" +0xc395: "sseb" +0xc396: "ssebs" +0xc397: "sses" +0xc398: "ssess" +0xc399: "sseng" +0xc39a: "ssej" +0xc39b: "ssec" +0xc39c: "ssek" +0xc39d: "sset" +0xc39e: "ssep" +0xc39f: "sseh" +0xc3a0: "ssyeo" +0xc3a1: "ssyeog" +0xc3a2: "ssyeogg" +0xc3a3: "ssyeogs" +0xc3a4: "ssyeon" +0xc3a5: "ssyeonj" +0xc3a6: "ssyeonh" +0xc3a7: "ssyeod" +0xc3a8: "ssyeol" +0xc3a9: "ssyeolg" +0xc3aa: "ssyeolm" +0xc3ab: "ssyeolb" +0xc3ac: "ssyeols" +0xc3ad: "ssyeolt" +0xc3ae: "ssyeolp" +0xc3af: "ssyeolh" +0xc3b0: "ssyeom" +0xc3b1: "ssyeob" +0xc3b2: "ssyeobs" +0xc3b3: "ssyeos" +0xc3b4: "ssyeoss" +0xc3b5: "ssyeong" +0xc3b6: "ssyeoj" +0xc3b7: "ssyeoc" +0xc3b8: "ssyeok" +0xc3b9: "ssyeot" +0xc3ba: "ssyeop" +0xc3bb: "ssyeoh" +0xc3bc: "ssye" +0xc3bd: "ssyeg" +0xc3be: "ssyegg" +0xc3bf: "ssyegs" +0xc3c0: "ssyen" +0xc3c1: "ssyenj" +0xc3c2: "ssyenh" +0xc3c3: "ssyed" +0xc3c4: "ssyel" +0xc3c5: "ssyelg" +0xc3c6: "ssyelm" +0xc3c7: "ssyelb" +0xc3c8: "ssyels" +0xc3c9: "ssyelt" +0xc3ca: "ssyelp" +0xc3cb: "ssyelh" +0xc3cc: "ssyem" +0xc3cd: "ssyeb" +0xc3ce: "ssyebs" +0xc3cf: "ssyes" +0xc3d0: "ssyess" +0xc3d1: "ssyeng" +0xc3d2: "ssyej" +0xc3d3: "ssyec" +0xc3d4: "ssyek" +0xc3d5: "ssyet" +0xc3d6: "ssyep" +0xc3d7: "ssyeh" +0xc3d8: "sso" +0xc3d9: "ssog" +0xc3da: "ssogg" +0xc3db: "ssogs" +0xc3dc: "sson" +0xc3dd: "ssonj" +0xc3de: "ssonh" +0xc3df: "ssod" +0xc3e0: "ssol" +0xc3e1: "ssolg" +0xc3e2: "ssolm" +0xc3e3: "ssolb" +0xc3e4: "ssols" +0xc3e5: "ssolt" +0xc3e6: "ssolp" +0xc3e7: "ssolh" +0xc3e8: "ssom" +0xc3e9: "ssob" +0xc3ea: "ssobs" +0xc3eb: "ssos" +0xc3ec: "ssoss" +0xc3ed: "ssong" +0xc3ee: "ssoj" +0xc3ef: "ssoc" +0xc3f0: "ssok" +0xc3f1: "ssot" +0xc3f2: "ssop" +0xc3f3: "ssoh" +0xc3f4: "sswa" +0xc3f5: "sswag" +0xc3f6: "sswagg" +0xc3f7: "sswags" +0xc3f8: "sswan" +0xc3f9: "sswanj" +0xc3fa: "sswanh" +0xc3fb: "sswad" +0xc3fc: "sswal" +0xc3fd: "sswalg" +0xc3fe: "sswalm" +0xc3ff: "sswalb" +/* x0c4 */ +0xc400: "sswals" +0xc401: "sswalt" +0xc402: "sswalp" +0xc403: "sswalh" +0xc404: "sswam" +0xc405: "sswab" +0xc406: "sswabs" +0xc407: "sswas" +0xc408: "sswass" +0xc409: "sswang" +0xc40a: "sswaj" +0xc40b: "sswac" +0xc40c: "sswak" +0xc40d: "sswat" +0xc40e: "sswap" +0xc40f: "sswah" +0xc410: "sswae" +0xc411: "sswaeg" +0xc412: "sswaegg" +0xc413: "sswaegs" +0xc414: "sswaen" +0xc415: "sswaenj" +0xc416: "sswaenh" +0xc417: "sswaed" +0xc418: "sswael" +0xc419: "sswaelg" +0xc41a: "sswaelm" +0xc41b: "sswaelb" +0xc41c: "sswaels" +0xc41d: "sswaelt" +0xc41e: "sswaelp" +0xc41f: "sswaelh" +0xc420: "sswaem" +0xc421: "sswaeb" +0xc422: "sswaebs" +0xc423: "sswaes" +0xc424: "sswaess" +0xc425: "sswaeng" +0xc426: "sswaej" +0xc427: "sswaec" +0xc428: "sswaek" +0xc429: "sswaet" +0xc42a: "sswaep" +0xc42b: "sswaeh" +0xc42c: "ssoe" +0xc42d: "ssoeg" +0xc42e: "ssoegg" +0xc42f: "ssoegs" +0xc430: "ssoen" +0xc431: "ssoenj" +0xc432: "ssoenh" +0xc433: "ssoed" +0xc434: "ssoel" +0xc435: "ssoelg" +0xc436: "ssoelm" +0xc437: "ssoelb" +0xc438: "ssoels" +0xc439: "ssoelt" +0xc43a: "ssoelp" +0xc43b: "ssoelh" +0xc43c: "ssoem" +0xc43d: "ssoeb" +0xc43e: "ssoebs" +0xc43f: "ssoes" +0xc440: "ssoess" +0xc441: "ssoeng" +0xc442: "ssoej" +0xc443: "ssoec" +0xc444: "ssoek" +0xc445: "ssoet" +0xc446: "ssoep" +0xc447: "ssoeh" +0xc448: "ssyo" +0xc449: "ssyog" +0xc44a: "ssyogg" +0xc44b: "ssyogs" +0xc44c: "ssyon" +0xc44d: "ssyonj" +0xc44e: "ssyonh" +0xc44f: "ssyod" +0xc450: "ssyol" +0xc451: "ssyolg" +0xc452: "ssyolm" +0xc453: "ssyolb" +0xc454: "ssyols" +0xc455: "ssyolt" +0xc456: "ssyolp" +0xc457: "ssyolh" +0xc458: "ssyom" +0xc459: "ssyob" +0xc45a: "ssyobs" +0xc45b: "ssyos" +0xc45c: "ssyoss" +0xc45d: "ssyong" +0xc45e: "ssyoj" +0xc45f: "ssyoc" +0xc460: "ssyok" +0xc461: "ssyot" +0xc462: "ssyop" +0xc463: "ssyoh" +0xc464: "ssu" +0xc465: "ssug" +0xc466: "ssugg" +0xc467: "ssugs" +0xc468: "ssun" +0xc469: "ssunj" +0xc46a: "ssunh" +0xc46b: "ssud" +0xc46c: "ssul" +0xc46d: "ssulg" +0xc46e: "ssulm" +0xc46f: "ssulb" +0xc470: "ssuls" +0xc471: "ssult" +0xc472: "ssulp" +0xc473: "ssulh" +0xc474: "ssum" +0xc475: "ssub" +0xc476: "ssubs" +0xc477: "ssus" +0xc478: "ssuss" +0xc479: "ssung" +0xc47a: "ssuj" +0xc47b: "ssuc" +0xc47c: "ssuk" +0xc47d: "ssut" +0xc47e: "ssup" +0xc47f: "ssuh" +0xc480: "ssweo" +0xc481: "ssweog" +0xc482: "ssweogg" +0xc483: "ssweogs" +0xc484: "ssweon" +0xc485: "ssweonj" +0xc486: "ssweonh" +0xc487: "ssweod" +0xc488: "ssweol" +0xc489: "ssweolg" +0xc48a: "ssweolm" +0xc48b: "ssweolb" +0xc48c: "ssweols" +0xc48d: "ssweolt" +0xc48e: "ssweolp" +0xc48f: "ssweolh" +0xc490: "ssweom" +0xc491: "ssweob" +0xc492: "ssweobs" +0xc493: "ssweos" +0xc494: "ssweoss" +0xc495: "ssweong" +0xc496: "ssweoj" +0xc497: "ssweoc" +0xc498: "ssweok" +0xc499: "ssweot" +0xc49a: "ssweop" +0xc49b: "ssweoh" +0xc49c: "sswe" +0xc49d: "ssweg" +0xc49e: "sswegg" +0xc49f: "sswegs" +0xc4a0: "sswen" +0xc4a1: "sswenj" +0xc4a2: "sswenh" +0xc4a3: "sswed" +0xc4a4: "sswel" +0xc4a5: "sswelg" +0xc4a6: "sswelm" +0xc4a7: "sswelb" +0xc4a8: "sswels" +0xc4a9: "sswelt" +0xc4aa: "sswelp" +0xc4ab: "sswelh" +0xc4ac: "sswem" +0xc4ad: "ssweb" +0xc4ae: "sswebs" +0xc4af: "sswes" +0xc4b0: "sswess" +0xc4b1: "ssweng" +0xc4b2: "sswej" +0xc4b3: "sswec" +0xc4b4: "sswek" +0xc4b5: "sswet" +0xc4b6: "sswep" +0xc4b7: "ssweh" +0xc4b8: "sswi" +0xc4b9: "sswig" +0xc4ba: "sswigg" +0xc4bb: "sswigs" +0xc4bc: "sswin" +0xc4bd: "sswinj" +0xc4be: "sswinh" +0xc4bf: "sswid" +0xc4c0: "sswil" +0xc4c1: "sswilg" +0xc4c2: "sswilm" +0xc4c3: "sswilb" +0xc4c4: "sswils" +0xc4c5: "sswilt" +0xc4c6: "sswilp" +0xc4c7: "sswilh" +0xc4c8: "sswim" +0xc4c9: "sswib" +0xc4ca: "sswibs" +0xc4cb: "sswis" +0xc4cc: "sswiss" +0xc4cd: "sswing" +0xc4ce: "sswij" +0xc4cf: "sswic" +0xc4d0: "sswik" +0xc4d1: "sswit" +0xc4d2: "sswip" +0xc4d3: "sswih" +0xc4d4: "ssyu" +0xc4d5: "ssyug" +0xc4d6: "ssyugg" +0xc4d7: "ssyugs" +0xc4d8: "ssyun" +0xc4d9: "ssyunj" +0xc4da: "ssyunh" +0xc4db: "ssyud" +0xc4dc: "ssyul" +0xc4dd: "ssyulg" +0xc4de: "ssyulm" +0xc4df: "ssyulb" +0xc4e0: "ssyuls" +0xc4e1: "ssyult" +0xc4e2: "ssyulp" +0xc4e3: "ssyulh" +0xc4e4: "ssyum" +0xc4e5: "ssyub" +0xc4e6: "ssyubs" +0xc4e7: "ssyus" +0xc4e8: "ssyuss" +0xc4e9: "ssyung" +0xc4ea: "ssyuj" +0xc4eb: "ssyuc" +0xc4ec: "ssyuk" +0xc4ed: "ssyut" +0xc4ee: "ssyup" +0xc4ef: "ssyuh" +0xc4f0: "sseu" +0xc4f1: "sseug" +0xc4f2: "sseugg" +0xc4f3: "sseugs" +0xc4f4: "sseun" +0xc4f5: "sseunj" +0xc4f6: "sseunh" +0xc4f7: "sseud" +0xc4f8: "sseul" +0xc4f9: "sseulg" +0xc4fa: "sseulm" +0xc4fb: "sseulb" +0xc4fc: "sseuls" +0xc4fd: "sseult" +0xc4fe: "sseulp" +0xc4ff: "sseulh" +/* x0c5 */ +0xc500: "sseum" +0xc501: "sseub" +0xc502: "sseubs" +0xc503: "sseus" +0xc504: "sseuss" +0xc505: "sseung" +0xc506: "sseuj" +0xc507: "sseuc" +0xc508: "sseuk" +0xc509: "sseut" +0xc50a: "sseup" +0xc50b: "sseuh" +0xc50c: "ssyi" +0xc50d: "ssyig" +0xc50e: "ssyigg" +0xc50f: "ssyigs" +0xc510: "ssyin" +0xc511: "ssyinj" +0xc512: "ssyinh" +0xc513: "ssyid" +0xc514: "ssyil" +0xc515: "ssyilg" +0xc516: "ssyilm" +0xc517: "ssyilb" +0xc518: "ssyils" +0xc519: "ssyilt" +0xc51a: "ssyilp" +0xc51b: "ssyilh" +0xc51c: "ssyim" +0xc51d: "ssyib" +0xc51e: "ssyibs" +0xc51f: "ssyis" +0xc520: "ssyiss" +0xc521: "ssying" +0xc522: "ssyij" +0xc523: "ssyic" +0xc524: "ssyik" +0xc525: "ssyit" +0xc526: "ssyip" +0xc527: "ssyih" +0xc528: "ssi" +0xc529: "ssig" +0xc52a: "ssigg" +0xc52b: "ssigs" +0xc52c: "ssin" +0xc52d: "ssinj" +0xc52e: "ssinh" +0xc52f: "ssid" +0xc530: "ssil" +0xc531: "ssilg" +0xc532: "ssilm" +0xc533: "ssilb" +0xc534: "ssils" +0xc535: "ssilt" +0xc536: "ssilp" +0xc537: "ssilh" +0xc538: "ssim" +0xc539: "ssib" +0xc53a: "ssibs" +0xc53b: "ssis" +0xc53c: "ssiss" +0xc53d: "ssing" +0xc53e: "ssij" +0xc53f: "ssic" +0xc540: "ssik" +0xc541: "ssit" +0xc542: "ssip" +0xc543: "ssih" +0xc544: "a" +0xc545: "ag" +0xc546: "agg" +0xc547: "ags" +0xc548: "an" +0xc549: "anj" +0xc54a: "anh" +0xc54b: "ad" +0xc54c: "al" +0xc54d: "alg" +0xc54e: "alm" +0xc54f: "alb" +0xc550: "als" +0xc551: "alt" +0xc552: "alp" +0xc553: "alh" +0xc554: "am" +0xc555: "ab" +0xc556: "abs" +0xc557: "as" +0xc558: "ass" +0xc559: "ang" +0xc55a: "aj" +0xc55b: "ac" +0xc55c: "ak" +0xc55d: "at" +0xc55e: "ap" +0xc55f: "ah" +0xc560: "ae" +0xc561: "aeg" +0xc562: "aegg" +0xc563: "aegs" +0xc564: "aen" +0xc565: "aenj" +0xc566: "aenh" +0xc567: "aed" +0xc568: "ael" +0xc569: "aelg" +0xc56a: "aelm" +0xc56b: "aelb" +0xc56c: "aels" +0xc56d: "aelt" +0xc56e: "aelp" +0xc56f: "aelh" +0xc570: "aem" +0xc571: "aeb" +0xc572: "aebs" +0xc573: "aes" +0xc574: "aess" +0xc575: "aeng" +0xc576: "aej" +0xc577: "aec" +0xc578: "aek" +0xc579: "aet" +0xc57a: "aep" +0xc57b: "aeh" +0xc57c: "ya" +0xc57d: "yag" +0xc57e: "yagg" +0xc57f: "yags" +0xc580: "yan" +0xc581: "yanj" +0xc582: "yanh" +0xc583: "yad" +0xc584: "yal" +0xc585: "yalg" +0xc586: "yalm" +0xc587: "yalb" +0xc588: "yals" +0xc589: "yalt" +0xc58a: "yalp" +0xc58b: "yalh" +0xc58c: "yam" +0xc58d: "yab" +0xc58e: "yabs" +0xc58f: "yas" +0xc590: "yass" +0xc591: "yang" +0xc592: "yaj" +0xc593: "yac" +0xc594: "yak" +0xc595: "yat" +0xc596: "yap" +0xc597: "yah" +0xc598: "yae" +0xc599: "yaeg" +0xc59a: "yaegg" +0xc59b: "yaegs" +0xc59c: "yaen" +0xc59d: "yaenj" +0xc59e: "yaenh" +0xc59f: "yaed" +0xc5a0: "yael" +0xc5a1: "yaelg" +0xc5a2: "yaelm" +0xc5a3: "yaelb" +0xc5a4: "yaels" +0xc5a5: "yaelt" +0xc5a6: "yaelp" +0xc5a7: "yaelh" +0xc5a8: "yaem" +0xc5a9: "yaeb" +0xc5aa: "yaebs" +0xc5ab: "yaes" +0xc5ac: "yaess" +0xc5ad: "yaeng" +0xc5ae: "yaej" +0xc5af: "yaec" +0xc5b0: "yaek" +0xc5b1: "yaet" +0xc5b2: "yaep" +0xc5b3: "yaeh" +0xc5b4: "eo" +0xc5b5: "eog" +0xc5b6: "eogg" +0xc5b7: "eogs" +0xc5b8: "eon" +0xc5b9: "eonj" +0xc5ba: "eonh" +0xc5bb: "eod" +0xc5bc: "eol" +0xc5bd: "eolg" +0xc5be: "eolm" +0xc5bf: "eolb" +0xc5c0: "eols" +0xc5c1: "eolt" +0xc5c2: "eolp" +0xc5c3: "eolh" +0xc5c4: "eom" +0xc5c5: "eob" +0xc5c6: "eobs" +0xc5c7: "eos" +0xc5c8: "eoss" +0xc5c9: "eong" +0xc5ca: "eoj" +0xc5cb: "eoc" +0xc5cc: "eok" +0xc5cd: "eot" +0xc5ce: "eop" +0xc5cf: "eoh" +0xc5d0: "e" +0xc5d1: "eg" +0xc5d2: "egg" +0xc5d3: "egs" +0xc5d4: "en" +0xc5d5: "enj" +0xc5d6: "enh" +0xc5d7: "ed" +0xc5d8: "el" +0xc5d9: "elg" +0xc5da: "elm" +0xc5db: "elb" +0xc5dc: "els" +0xc5dd: "elt" +0xc5de: "elp" +0xc5df: "elh" +0xc5e0: "em" +0xc5e1: "eb" +0xc5e2: "ebs" +0xc5e3: "es" +0xc5e4: "ess" +0xc5e5: "eng" +0xc5e6: "ej" +0xc5e7: "ec" +0xc5e8: "ek" +0xc5e9: "et" +0xc5ea: "ep" +0xc5eb: "eh" +0xc5ec: "yeo" +0xc5ed: "yeog" +0xc5ee: "yeogg" +0xc5ef: "yeogs" +0xc5f0: "yeon" +0xc5f1: "yeonj" +0xc5f2: "yeonh" +0xc5f3: "yeod" +0xc5f4: "yeol" +0xc5f5: "yeolg" +0xc5f6: "yeolm" +0xc5f7: "yeolb" +0xc5f8: "yeols" +0xc5f9: "yeolt" +0xc5fa: "yeolp" +0xc5fb: "yeolh" +0xc5fc: "yeom" +0xc5fd: "yeob" +0xc5fe: "yeobs" +0xc5ff: "yeos" +/* x0c6 */ +0xc600: "yeoss" +0xc601: "yeong" +0xc602: "yeoj" +0xc603: "yeoc" +0xc604: "yeok" +0xc605: "yeot" +0xc606: "yeop" +0xc607: "yeoh" +0xc608: "ye" +0xc609: "yeg" +0xc60a: "yegg" +0xc60b: "yegs" +0xc60c: "yen" +0xc60d: "yenj" +0xc60e: "yenh" +0xc60f: "yed" +0xc610: "yel" +0xc611: "yelg" +0xc612: "yelm" +0xc613: "yelb" +0xc614: "yels" +0xc615: "yelt" +0xc616: "yelp" +0xc617: "yelh" +0xc618: "yem" +0xc619: "yeb" +0xc61a: "yebs" +0xc61b: "yes" +0xc61c: "yess" +0xc61d: "yeng" +0xc61e: "yej" +0xc61f: "yec" +0xc620: "yek" +0xc621: "yet" +0xc622: "yep" +0xc623: "yeh" +0xc624: "o" +0xc625: "og" +0xc626: "ogg" +0xc627: "ogs" +0xc628: "on" +0xc629: "onj" +0xc62a: "onh" +0xc62b: "od" +0xc62c: "ol" +0xc62d: "olg" +0xc62e: "olm" +0xc62f: "olb" +0xc630: "ols" +0xc631: "olt" +0xc632: "olp" +0xc633: "olh" +0xc634: "om" +0xc635: "ob" +0xc636: "obs" +0xc637: "os" +0xc638: "oss" +0xc639: "ong" +0xc63a: "oj" +0xc63b: "oc" +0xc63c: "ok" +0xc63d: "ot" +0xc63e: "op" +0xc63f: "oh" +0xc640: "wa" +0xc641: "wag" +0xc642: "wagg" +0xc643: "wags" +0xc644: "wan" +0xc645: "wanj" +0xc646: "wanh" +0xc647: "wad" +0xc648: "wal" +0xc649: "walg" +0xc64a: "walm" +0xc64b: "walb" +0xc64c: "wals" +0xc64d: "walt" +0xc64e: "walp" +0xc64f: "walh" +0xc650: "wam" +0xc651: "wab" +0xc652: "wabs" +0xc653: "was" +0xc654: "wass" +0xc655: "wang" +0xc656: "waj" +0xc657: "wac" +0xc658: "wak" +0xc659: "wat" +0xc65a: "wap" +0xc65b: "wah" +0xc65c: "wae" +0xc65d: "waeg" +0xc65e: "waegg" +0xc65f: "waegs" +0xc660: "waen" +0xc661: "waenj" +0xc662: "waenh" +0xc663: "waed" +0xc664: "wael" +0xc665: "waelg" +0xc666: "waelm" +0xc667: "waelb" +0xc668: "waels" +0xc669: "waelt" +0xc66a: "waelp" +0xc66b: "waelh" +0xc66c: "waem" +0xc66d: "waeb" +0xc66e: "waebs" +0xc66f: "waes" +0xc670: "waess" +0xc671: "waeng" +0xc672: "waej" +0xc673: "waec" +0xc674: "waek" +0xc675: "waet" +0xc676: "waep" +0xc677: "waeh" +0xc678: "oe" +0xc679: "oeg" +0xc67a: "oegg" +0xc67b: "oegs" +0xc67c: "oen" +0xc67d: "oenj" +0xc67e: "oenh" +0xc67f: "oed" +0xc680: "oel" +0xc681: "oelg" +0xc682: "oelm" +0xc683: "oelb" +0xc684: "oels" +0xc685: "oelt" +0xc686: "oelp" +0xc687: "oelh" +0xc688: "oem" +0xc689: "oeb" +0xc68a: "oebs" +0xc68b: "oes" +0xc68c: "oess" +0xc68d: "oeng" +0xc68e: "oej" +0xc68f: "oec" +0xc690: "oek" +0xc691: "oet" +0xc692: "oep" +0xc693: "oeh" +0xc694: "yo" +0xc695: "yog" +0xc696: "yogg" +0xc697: "yogs" +0xc698: "yon" +0xc699: "yonj" +0xc69a: "yonh" +0xc69b: "yod" +0xc69c: "yol" +0xc69d: "yolg" +0xc69e: "yolm" +0xc69f: "yolb" +0xc6a0: "yols" +0xc6a1: "yolt" +0xc6a2: "yolp" +0xc6a3: "yolh" +0xc6a4: "yom" +0xc6a5: "yob" +0xc6a6: "yobs" +0xc6a7: "yos" +0xc6a8: "yoss" +0xc6a9: "yong" +0xc6aa: "yoj" +0xc6ab: "yoc" +0xc6ac: "yok" +0xc6ad: "yot" +0xc6ae: "yop" +0xc6af: "yoh" +0xc6b0: "u" +0xc6b1: "ug" +0xc6b2: "ugg" +0xc6b3: "ugs" +0xc6b4: "un" +0xc6b5: "unj" +0xc6b6: "unh" +0xc6b7: "ud" +0xc6b8: "ul" +0xc6b9: "ulg" +0xc6ba: "ulm" +0xc6bb: "ulb" +0xc6bc: "uls" +0xc6bd: "ult" +0xc6be: "ulp" +0xc6bf: "ulh" +0xc6c0: "um" +0xc6c1: "ub" +0xc6c2: "ubs" +0xc6c3: "us" +0xc6c4: "uss" +0xc6c5: "ung" +0xc6c6: "uj" +0xc6c7: "uc" +0xc6c8: "uk" +0xc6c9: "ut" +0xc6ca: "up" +0xc6cb: "uh" +0xc6cc: "weo" +0xc6cd: "weog" +0xc6ce: "weogg" +0xc6cf: "weogs" +0xc6d0: "weon" +0xc6d1: "weonj" +0xc6d2: "weonh" +0xc6d3: "weod" +0xc6d4: "weol" +0xc6d5: "weolg" +0xc6d6: "weolm" +0xc6d7: "weolb" +0xc6d8: "weols" +0xc6d9: "weolt" +0xc6da: "weolp" +0xc6db: "weolh" +0xc6dc: "weom" +0xc6dd: "weob" +0xc6de: "weobs" +0xc6df: "weos" +0xc6e0: "weoss" +0xc6e1: "weong" +0xc6e2: "weoj" +0xc6e3: "weoc" +0xc6e4: "weok" +0xc6e5: "weot" +0xc6e6: "weop" +0xc6e7: "weoh" +0xc6e8: "we" +0xc6e9: "weg" +0xc6ea: "wegg" +0xc6eb: "wegs" +0xc6ec: "wen" +0xc6ed: "wenj" +0xc6ee: "wenh" +0xc6ef: "wed" +0xc6f0: "wel" +0xc6f1: "welg" +0xc6f2: "welm" +0xc6f3: "welb" +0xc6f4: "wels" +0xc6f5: "welt" +0xc6f6: "welp" +0xc6f7: "welh" +0xc6f8: "wem" +0xc6f9: "web" +0xc6fa: "webs" +0xc6fb: "wes" +0xc6fc: "wess" +0xc6fd: "weng" +0xc6fe: "wej" +0xc6ff: "wec" +/* x0c7 */ +0xc700: "wek" +0xc701: "wet" +0xc702: "wep" +0xc703: "weh" +0xc704: "wi" +0xc705: "wig" +0xc706: "wigg" +0xc707: "wigs" +0xc708: "win" +0xc709: "winj" +0xc70a: "winh" +0xc70b: "wid" +0xc70c: "wil" +0xc70d: "wilg" +0xc70e: "wilm" +0xc70f: "wilb" +0xc710: "wils" +0xc711: "wilt" +0xc712: "wilp" +0xc713: "wilh" +0xc714: "wim" +0xc715: "wib" +0xc716: "wibs" +0xc717: "wis" +0xc718: "wiss" +0xc719: "wing" +0xc71a: "wij" +0xc71b: "wic" +0xc71c: "wik" +0xc71d: "wit" +0xc71e: "wip" +0xc71f: "wih" +0xc720: "yu" +0xc721: "yug" +0xc722: "yugg" +0xc723: "yugs" +0xc724: "yun" +0xc725: "yunj" +0xc726: "yunh" +0xc727: "yud" +0xc728: "yul" +0xc729: "yulg" +0xc72a: "yulm" +0xc72b: "yulb" +0xc72c: "yuls" +0xc72d: "yult" +0xc72e: "yulp" +0xc72f: "yulh" +0xc730: "yum" +0xc731: "yub" +0xc732: "yubs" +0xc733: "yus" +0xc734: "yuss" +0xc735: "yung" +0xc736: "yuj" +0xc737: "yuc" +0xc738: "yuk" +0xc739: "yut" +0xc73a: "yup" +0xc73b: "yuh" +0xc73c: "eu" +0xc73d: "eug" +0xc73e: "eugg" +0xc73f: "eugs" +0xc740: "eun" +0xc741: "eunj" +0xc742: "eunh" +0xc743: "eud" +0xc744: "eul" +0xc745: "eulg" +0xc746: "eulm" +0xc747: "eulb" +0xc748: "euls" +0xc749: "eult" +0xc74a: "eulp" +0xc74b: "eulh" +0xc74c: "eum" +0xc74d: "eub" +0xc74e: "eubs" +0xc74f: "eus" +0xc750: "euss" +0xc751: "eung" +0xc752: "euj" +0xc753: "euc" +0xc754: "euk" +0xc755: "eut" +0xc756: "eup" +0xc757: "euh" +0xc758: "yi" +0xc759: "yig" +0xc75a: "yigg" +0xc75b: "yigs" +0xc75c: "yin" +0xc75d: "yinj" +0xc75e: "yinh" +0xc75f: "yid" +0xc760: "yil" +0xc761: "yilg" +0xc762: "yilm" +0xc763: "yilb" +0xc764: "yils" +0xc765: "yilt" +0xc766: "yilp" +0xc767: "yilh" +0xc768: "yim" +0xc769: "yib" +0xc76a: "yibs" +0xc76b: "yis" +0xc76c: "yiss" +0xc76d: "ying" +0xc76e: "yij" +0xc76f: "yic" +0xc770: "yik" +0xc771: "yit" +0xc772: "yip" +0xc773: "yih" +0xc774: "i" +0xc775: "ig" +0xc776: "igg" +0xc777: "igs" +0xc778: "in" +0xc779: "inj" +0xc77a: "inh" +0xc77b: "id" +0xc77c: "il" +0xc77d: "ilg" +0xc77e: "ilm" +0xc77f: "ilb" +0xc780: "ils" +0xc781: "ilt" +0xc782: "ilp" +0xc783: "ilh" +0xc784: "im" +0xc785: "ib" +0xc786: "ibs" +0xc787: "is" +0xc788: "iss" +0xc789: "ing" +0xc78a: "ij" +0xc78b: "ic" +0xc78c: "ik" +0xc78d: "it" +0xc78e: "ip" +0xc78f: "ih" +0xc790: "ja" +0xc791: "jag" +0xc792: "jagg" +0xc793: "jags" +0xc794: "jan" +0xc795: "janj" +0xc796: "janh" +0xc797: "jad" +0xc798: "jal" +0xc799: "jalg" +0xc79a: "jalm" +0xc79b: "jalb" +0xc79c: "jals" +0xc79d: "jalt" +0xc79e: "jalp" +0xc79f: "jalh" +0xc7a0: "jam" +0xc7a1: "jab" +0xc7a2: "jabs" +0xc7a3: "jas" +0xc7a4: "jass" +0xc7a5: "jang" +0xc7a6: "jaj" +0xc7a7: "jac" +0xc7a8: "jak" +0xc7a9: "jat" +0xc7aa: "jap" +0xc7ab: "jah" +0xc7ac: "jae" +0xc7ad: "jaeg" +0xc7ae: "jaegg" +0xc7af: "jaegs" +0xc7b0: "jaen" +0xc7b1: "jaenj" +0xc7b2: "jaenh" +0xc7b3: "jaed" +0xc7b4: "jael" +0xc7b5: "jaelg" +0xc7b6: "jaelm" +0xc7b7: "jaelb" +0xc7b8: "jaels" +0xc7b9: "jaelt" +0xc7ba: "jaelp" +0xc7bb: "jaelh" +0xc7bc: "jaem" +0xc7bd: "jaeb" +0xc7be: "jaebs" +0xc7bf: "jaes" +0xc7c0: "jaess" +0xc7c1: "jaeng" +0xc7c2: "jaej" +0xc7c3: "jaec" +0xc7c4: "jaek" +0xc7c5: "jaet" +0xc7c6: "jaep" +0xc7c7: "jaeh" +0xc7c8: "jya" +0xc7c9: "jyag" +0xc7ca: "jyagg" +0xc7cb: "jyags" +0xc7cc: "jyan" +0xc7cd: "jyanj" +0xc7ce: "jyanh" +0xc7cf: "jyad" +0xc7d0: "jyal" +0xc7d1: "jyalg" +0xc7d2: "jyalm" +0xc7d3: "jyalb" +0xc7d4: "jyals" +0xc7d5: "jyalt" +0xc7d6: "jyalp" +0xc7d7: "jyalh" +0xc7d8: "jyam" +0xc7d9: "jyab" +0xc7da: "jyabs" +0xc7db: "jyas" +0xc7dc: "jyass" +0xc7dd: "jyang" +0xc7de: "jyaj" +0xc7df: "jyac" +0xc7e0: "jyak" +0xc7e1: "jyat" +0xc7e2: "jyap" +0xc7e3: "jyah" +0xc7e4: "jyae" +0xc7e5: "jyaeg" +0xc7e6: "jyaegg" +0xc7e7: "jyaegs" +0xc7e8: "jyaen" +0xc7e9: "jyaenj" +0xc7ea: "jyaenh" +0xc7eb: "jyaed" +0xc7ec: "jyael" +0xc7ed: "jyaelg" +0xc7ee: "jyaelm" +0xc7ef: "jyaelb" +0xc7f0: "jyaels" +0xc7f1: "jyaelt" +0xc7f2: "jyaelp" +0xc7f3: "jyaelh" +0xc7f4: "jyaem" +0xc7f5: "jyaeb" +0xc7f6: "jyaebs" +0xc7f7: "jyaes" +0xc7f8: "jyaess" +0xc7f9: "jyaeng" +0xc7fa: "jyaej" +0xc7fb: "jyaec" +0xc7fc: "jyaek" +0xc7fd: "jyaet" +0xc7fe: "jyaep" +0xc7ff: "jyaeh" +/* x0c8 */ +0xc800: "jeo" +0xc801: "jeog" +0xc802: "jeogg" +0xc803: "jeogs" +0xc804: "jeon" +0xc805: "jeonj" +0xc806: "jeonh" +0xc807: "jeod" +0xc808: "jeol" +0xc809: "jeolg" +0xc80a: "jeolm" +0xc80b: "jeolb" +0xc80c: "jeols" +0xc80d: "jeolt" +0xc80e: "jeolp" +0xc80f: "jeolh" +0xc810: "jeom" +0xc811: "jeob" +0xc812: "jeobs" +0xc813: "jeos" +0xc814: "jeoss" +0xc815: "jeong" +0xc816: "jeoj" +0xc817: "jeoc" +0xc818: "jeok" +0xc819: "jeot" +0xc81a: "jeop" +0xc81b: "jeoh" +0xc81c: "je" +0xc81d: "jeg" +0xc81e: "jegg" +0xc81f: "jegs" +0xc820: "jen" +0xc821: "jenj" +0xc822: "jenh" +0xc823: "jed" +0xc824: "jel" +0xc825: "jelg" +0xc826: "jelm" +0xc827: "jelb" +0xc828: "jels" +0xc829: "jelt" +0xc82a: "jelp" +0xc82b: "jelh" +0xc82c: "jem" +0xc82d: "jeb" +0xc82e: "jebs" +0xc82f: "jes" +0xc830: "jess" +0xc831: "jeng" +0xc832: "jej" +0xc833: "jec" +0xc834: "jek" +0xc835: "jet" +0xc836: "jep" +0xc837: "jeh" +0xc838: "jyeo" +0xc839: "jyeog" +0xc83a: "jyeogg" +0xc83b: "jyeogs" +0xc83c: "jyeon" +0xc83d: "jyeonj" +0xc83e: "jyeonh" +0xc83f: "jyeod" +0xc840: "jyeol" +0xc841: "jyeolg" +0xc842: "jyeolm" +0xc843: "jyeolb" +0xc844: "jyeols" +0xc845: "jyeolt" +0xc846: "jyeolp" +0xc847: "jyeolh" +0xc848: "jyeom" +0xc849: "jyeob" +0xc84a: "jyeobs" +0xc84b: "jyeos" +0xc84c: "jyeoss" +0xc84d: "jyeong" +0xc84e: "jyeoj" +0xc84f: "jyeoc" +0xc850: "jyeok" +0xc851: "jyeot" +0xc852: "jyeop" +0xc853: "jyeoh" +0xc854: "jye" +0xc855: "jyeg" +0xc856: "jyegg" +0xc857: "jyegs" +0xc858: "jyen" +0xc859: "jyenj" +0xc85a: "jyenh" +0xc85b: "jyed" +0xc85c: "jyel" +0xc85d: "jyelg" +0xc85e: "jyelm" +0xc85f: "jyelb" +0xc860: "jyels" +0xc861: "jyelt" +0xc862: "jyelp" +0xc863: "jyelh" +0xc864: "jyem" +0xc865: "jyeb" +0xc866: "jyebs" +0xc867: "jyes" +0xc868: "jyess" +0xc869: "jyeng" +0xc86a: "jyej" +0xc86b: "jyec" +0xc86c: "jyek" +0xc86d: "jyet" +0xc86e: "jyep" +0xc86f: "jyeh" +0xc870: "jo" +0xc871: "jog" +0xc872: "jogg" +0xc873: "jogs" +0xc874: "jon" +0xc875: "jonj" +0xc876: "jonh" +0xc877: "jod" +0xc878: "jol" +0xc879: "jolg" +0xc87a: "jolm" +0xc87b: "jolb" +0xc87c: "jols" +0xc87d: "jolt" +0xc87e: "jolp" +0xc87f: "jolh" +0xc880: "jom" +0xc881: "job" +0xc882: "jobs" +0xc883: "jos" +0xc884: "joss" +0xc885: "jong" +0xc886: "joj" +0xc887: "joc" +0xc888: "jok" +0xc889: "jot" +0xc88a: "jop" +0xc88b: "joh" +0xc88c: "jwa" +0xc88d: "jwag" +0xc88e: "jwagg" +0xc88f: "jwags" +0xc890: "jwan" +0xc891: "jwanj" +0xc892: "jwanh" +0xc893: "jwad" +0xc894: "jwal" +0xc895: "jwalg" +0xc896: "jwalm" +0xc897: "jwalb" +0xc898: "jwals" +0xc899: "jwalt" +0xc89a: "jwalp" +0xc89b: "jwalh" +0xc89c: "jwam" +0xc89d: "jwab" +0xc89e: "jwabs" +0xc89f: "jwas" +0xc8a0: "jwass" +0xc8a1: "jwang" +0xc8a2: "jwaj" +0xc8a3: "jwac" +0xc8a4: "jwak" +0xc8a5: "jwat" +0xc8a6: "jwap" +0xc8a7: "jwah" +0xc8a8: "jwae" +0xc8a9: "jwaeg" +0xc8aa: "jwaegg" +0xc8ab: "jwaegs" +0xc8ac: "jwaen" +0xc8ad: "jwaenj" +0xc8ae: "jwaenh" +0xc8af: "jwaed" +0xc8b0: "jwael" +0xc8b1: "jwaelg" +0xc8b2: "jwaelm" +0xc8b3: "jwaelb" +0xc8b4: "jwaels" +0xc8b5: "jwaelt" +0xc8b6: "jwaelp" +0xc8b7: "jwaelh" +0xc8b8: "jwaem" +0xc8b9: "jwaeb" +0xc8ba: "jwaebs" +0xc8bb: "jwaes" +0xc8bc: "jwaess" +0xc8bd: "jwaeng" +0xc8be: "jwaej" +0xc8bf: "jwaec" +0xc8c0: "jwaek" +0xc8c1: "jwaet" +0xc8c2: "jwaep" +0xc8c3: "jwaeh" +0xc8c4: "joe" +0xc8c5: "joeg" +0xc8c6: "joegg" +0xc8c7: "joegs" +0xc8c8: "joen" +0xc8c9: "joenj" +0xc8ca: "joenh" +0xc8cb: "joed" +0xc8cc: "joel" +0xc8cd: "joelg" +0xc8ce: "joelm" +0xc8cf: "joelb" +0xc8d0: "joels" +0xc8d1: "joelt" +0xc8d2: "joelp" +0xc8d3: "joelh" +0xc8d4: "joem" +0xc8d5: "joeb" +0xc8d6: "joebs" +0xc8d7: "joes" +0xc8d8: "joess" +0xc8d9: "joeng" +0xc8da: "joej" +0xc8db: "joec" +0xc8dc: "joek" +0xc8dd: "joet" +0xc8de: "joep" +0xc8df: "joeh" +0xc8e0: "jyo" +0xc8e1: "jyog" +0xc8e2: "jyogg" +0xc8e3: "jyogs" +0xc8e4: "jyon" +0xc8e5: "jyonj" +0xc8e6: "jyonh" +0xc8e7: "jyod" +0xc8e8: "jyol" +0xc8e9: "jyolg" +0xc8ea: "jyolm" +0xc8eb: "jyolb" +0xc8ec: "jyols" +0xc8ed: "jyolt" +0xc8ee: "jyolp" +0xc8ef: "jyolh" +0xc8f0: "jyom" +0xc8f1: "jyob" +0xc8f2: "jyobs" +0xc8f3: "jyos" +0xc8f4: "jyoss" +0xc8f5: "jyong" +0xc8f6: "jyoj" +0xc8f7: "jyoc" +0xc8f8: "jyok" +0xc8f9: "jyot" +0xc8fa: "jyop" +0xc8fb: "jyoh" +0xc8fc: "ju" +0xc8fd: "jug" +0xc8fe: "jugg" +0xc8ff: "jugs" +/* x0c9 */ +0xc900: "jun" +0xc901: "junj" +0xc902: "junh" +0xc903: "jud" +0xc904: "jul" +0xc905: "julg" +0xc906: "julm" +0xc907: "julb" +0xc908: "juls" +0xc909: "jult" +0xc90a: "julp" +0xc90b: "julh" +0xc90c: "jum" +0xc90d: "jub" +0xc90e: "jubs" +0xc90f: "jus" +0xc910: "juss" +0xc911: "jung" +0xc912: "juj" +0xc913: "juc" +0xc914: "juk" +0xc915: "jut" +0xc916: "jup" +0xc917: "juh" +0xc918: "jweo" +0xc919: "jweog" +0xc91a: "jweogg" +0xc91b: "jweogs" +0xc91c: "jweon" +0xc91d: "jweonj" +0xc91e: "jweonh" +0xc91f: "jweod" +0xc920: "jweol" +0xc921: "jweolg" +0xc922: "jweolm" +0xc923: "jweolb" +0xc924: "jweols" +0xc925: "jweolt" +0xc926: "jweolp" +0xc927: "jweolh" +0xc928: "jweom" +0xc929: "jweob" +0xc92a: "jweobs" +0xc92b: "jweos" +0xc92c: "jweoss" +0xc92d: "jweong" +0xc92e: "jweoj" +0xc92f: "jweoc" +0xc930: "jweok" +0xc931: "jweot" +0xc932: "jweop" +0xc933: "jweoh" +0xc934: "jwe" +0xc935: "jweg" +0xc936: "jwegg" +0xc937: "jwegs" +0xc938: "jwen" +0xc939: "jwenj" +0xc93a: "jwenh" +0xc93b: "jwed" +0xc93c: "jwel" +0xc93d: "jwelg" +0xc93e: "jwelm" +0xc93f: "jwelb" +0xc940: "jwels" +0xc941: "jwelt" +0xc942: "jwelp" +0xc943: "jwelh" +0xc944: "jwem" +0xc945: "jweb" +0xc946: "jwebs" +0xc947: "jwes" +0xc948: "jwess" +0xc949: "jweng" +0xc94a: "jwej" +0xc94b: "jwec" +0xc94c: "jwek" +0xc94d: "jwet" +0xc94e: "jwep" +0xc94f: "jweh" +0xc950: "jwi" +0xc951: "jwig" +0xc952: "jwigg" +0xc953: "jwigs" +0xc954: "jwin" +0xc955: "jwinj" +0xc956: "jwinh" +0xc957: "jwid" +0xc958: "jwil" +0xc959: "jwilg" +0xc95a: "jwilm" +0xc95b: "jwilb" +0xc95c: "jwils" +0xc95d: "jwilt" +0xc95e: "jwilp" +0xc95f: "jwilh" +0xc960: "jwim" +0xc961: "jwib" +0xc962: "jwibs" +0xc963: "jwis" +0xc964: "jwiss" +0xc965: "jwing" +0xc966: "jwij" +0xc967: "jwic" +0xc968: "jwik" +0xc969: "jwit" +0xc96a: "jwip" +0xc96b: "jwih" +0xc96c: "jyu" +0xc96d: "jyug" +0xc96e: "jyugg" +0xc96f: "jyugs" +0xc970: "jyun" +0xc971: "jyunj" +0xc972: "jyunh" +0xc973: "jyud" +0xc974: "jyul" +0xc975: "jyulg" +0xc976: "jyulm" +0xc977: "jyulb" +0xc978: "jyuls" +0xc979: "jyult" +0xc97a: "jyulp" +0xc97b: "jyulh" +0xc97c: "jyum" +0xc97d: "jyub" +0xc97e: "jyubs" +0xc97f: "jyus" +0xc980: "jyuss" +0xc981: "jyung" +0xc982: "jyuj" +0xc983: "jyuc" +0xc984: "jyuk" +0xc985: "jyut" +0xc986: "jyup" +0xc987: "jyuh" +0xc988: "jeu" +0xc989: "jeug" +0xc98a: "jeugg" +0xc98b: "jeugs" +0xc98c: "jeun" +0xc98d: "jeunj" +0xc98e: "jeunh" +0xc98f: "jeud" +0xc990: "jeul" +0xc991: "jeulg" +0xc992: "jeulm" +0xc993: "jeulb" +0xc994: "jeuls" +0xc995: "jeult" +0xc996: "jeulp" +0xc997: "jeulh" +0xc998: "jeum" +0xc999: "jeub" +0xc99a: "jeubs" +0xc99b: "jeus" +0xc99c: "jeuss" +0xc99d: "jeung" +0xc99e: "jeuj" +0xc99f: "jeuc" +0xc9a0: "jeuk" +0xc9a1: "jeut" +0xc9a2: "jeup" +0xc9a3: "jeuh" +0xc9a4: "jyi" +0xc9a5: "jyig" +0xc9a6: "jyigg" +0xc9a7: "jyigs" +0xc9a8: "jyin" +0xc9a9: "jyinj" +0xc9aa: "jyinh" +0xc9ab: "jyid" +0xc9ac: "jyil" +0xc9ad: "jyilg" +0xc9ae: "jyilm" +0xc9af: "jyilb" +0xc9b0: "jyils" +0xc9b1: "jyilt" +0xc9b2: "jyilp" +0xc9b3: "jyilh" +0xc9b4: "jyim" +0xc9b5: "jyib" +0xc9b6: "jyibs" +0xc9b7: "jyis" +0xc9b8: "jyiss" +0xc9b9: "jying" +0xc9ba: "jyij" +0xc9bb: "jyic" +0xc9bc: "jyik" +0xc9bd: "jyit" +0xc9be: "jyip" +0xc9bf: "jyih" +0xc9c0: "ji" +0xc9c1: "jig" +0xc9c2: "jigg" +0xc9c3: "jigs" +0xc9c4: "jin" +0xc9c5: "jinj" +0xc9c6: "jinh" +0xc9c7: "jid" +0xc9c8: "jil" +0xc9c9: "jilg" +0xc9ca: "jilm" +0xc9cb: "jilb" +0xc9cc: "jils" +0xc9cd: "jilt" +0xc9ce: "jilp" +0xc9cf: "jilh" +0xc9d0: "jim" +0xc9d1: "jib" +0xc9d2: "jibs" +0xc9d3: "jis" +0xc9d4: "jiss" +0xc9d5: "jing" +0xc9d6: "jij" +0xc9d7: "jic" +0xc9d8: "jik" +0xc9d9: "jit" +0xc9da: "jip" +0xc9db: "jih" +0xc9dc: "jja" +0xc9dd: "jjag" +0xc9de: "jjagg" +0xc9df: "jjags" +0xc9e0: "jjan" +0xc9e1: "jjanj" +0xc9e2: "jjanh" +0xc9e3: "jjad" +0xc9e4: "jjal" +0xc9e5: "jjalg" +0xc9e6: "jjalm" +0xc9e7: "jjalb" +0xc9e8: "jjals" +0xc9e9: "jjalt" +0xc9ea: "jjalp" +0xc9eb: "jjalh" +0xc9ec: "jjam" +0xc9ed: "jjab" +0xc9ee: "jjabs" +0xc9ef: "jjas" +0xc9f0: "jjass" +0xc9f1: "jjang" +0xc9f2: "jjaj" +0xc9f3: "jjac" +0xc9f4: "jjak" +0xc9f5: "jjat" +0xc9f6: "jjap" +0xc9f7: "jjah" +0xc9f8: "jjae" +0xc9f9: "jjaeg" +0xc9fa: "jjaegg" +0xc9fb: "jjaegs" +0xc9fc: "jjaen" +0xc9fd: "jjaenj" +0xc9fe: "jjaenh" +0xc9ff: "jjaed" +/* x0ca */ +0xca00: "jjael" +0xca01: "jjaelg" +0xca02: "jjaelm" +0xca03: "jjaelb" +0xca04: "jjaels" +0xca05: "jjaelt" +0xca06: "jjaelp" +0xca07: "jjaelh" +0xca08: "jjaem" +0xca09: "jjaeb" +0xca0a: "jjaebs" +0xca0b: "jjaes" +0xca0c: "jjaess" +0xca0d: "jjaeng" +0xca0e: "jjaej" +0xca0f: "jjaec" +0xca10: "jjaek" +0xca11: "jjaet" +0xca12: "jjaep" +0xca13: "jjaeh" +0xca14: "jjya" +0xca15: "jjyag" +0xca16: "jjyagg" +0xca17: "jjyags" +0xca18: "jjyan" +0xca19: "jjyanj" +0xca1a: "jjyanh" +0xca1b: "jjyad" +0xca1c: "jjyal" +0xca1d: "jjyalg" +0xca1e: "jjyalm" +0xca1f: "jjyalb" +0xca20: "jjyals" +0xca21: "jjyalt" +0xca22: "jjyalp" +0xca23: "jjyalh" +0xca24: "jjyam" +0xca25: "jjyab" +0xca26: "jjyabs" +0xca27: "jjyas" +0xca28: "jjyass" +0xca29: "jjyang" +0xca2a: "jjyaj" +0xca2b: "jjyac" +0xca2c: "jjyak" +0xca2d: "jjyat" +0xca2e: "jjyap" +0xca2f: "jjyah" +0xca30: "jjyae" +0xca31: "jjyaeg" +0xca32: "jjyaegg" +0xca33: "jjyaegs" +0xca34: "jjyaen" +0xca35: "jjyaenj" +0xca36: "jjyaenh" +0xca37: "jjyaed" +0xca38: "jjyael" +0xca39: "jjyaelg" +0xca3a: "jjyaelm" +0xca3b: "jjyaelb" +0xca3c: "jjyaels" +0xca3d: "jjyaelt" +0xca3e: "jjyaelp" +0xca3f: "jjyaelh" +0xca40: "jjyaem" +0xca41: "jjyaeb" +0xca42: "jjyaebs" +0xca43: "jjyaes" +0xca44: "jjyaess" +0xca45: "jjyaeng" +0xca46: "jjyaej" +0xca47: "jjyaec" +0xca48: "jjyaek" +0xca49: "jjyaet" +0xca4a: "jjyaep" +0xca4b: "jjyaeh" +0xca4c: "jjeo" +0xca4d: "jjeog" +0xca4e: "jjeogg" +0xca4f: "jjeogs" +0xca50: "jjeon" +0xca51: "jjeonj" +0xca52: "jjeonh" +0xca53: "jjeod" +0xca54: "jjeol" +0xca55: "jjeolg" +0xca56: "jjeolm" +0xca57: "jjeolb" +0xca58: "jjeols" +0xca59: "jjeolt" +0xca5a: "jjeolp" +0xca5b: "jjeolh" +0xca5c: "jjeom" +0xca5d: "jjeob" +0xca5e: "jjeobs" +0xca5f: "jjeos" +0xca60: "jjeoss" +0xca61: "jjeong" +0xca62: "jjeoj" +0xca63: "jjeoc" +0xca64: "jjeok" +0xca65: "jjeot" +0xca66: "jjeop" +0xca67: "jjeoh" +0xca68: "jje" +0xca69: "jjeg" +0xca6a: "jjegg" +0xca6b: "jjegs" +0xca6c: "jjen" +0xca6d: "jjenj" +0xca6e: "jjenh" +0xca6f: "jjed" +0xca70: "jjel" +0xca71: "jjelg" +0xca72: "jjelm" +0xca73: "jjelb" +0xca74: "jjels" +0xca75: "jjelt" +0xca76: "jjelp" +0xca77: "jjelh" +0xca78: "jjem" +0xca79: "jjeb" +0xca7a: "jjebs" +0xca7b: "jjes" +0xca7c: "jjess" +0xca7d: "jjeng" +0xca7e: "jjej" +0xca7f: "jjec" +0xca80: "jjek" +0xca81: "jjet" +0xca82: "jjep" +0xca83: "jjeh" +0xca84: "jjyeo" +0xca85: "jjyeog" +0xca86: "jjyeogg" +0xca87: "jjyeogs" +0xca88: "jjyeon" +0xca89: "jjyeonj" +0xca8a: "jjyeonh" +0xca8b: "jjyeod" +0xca8c: "jjyeol" +0xca8d: "jjyeolg" +0xca8e: "jjyeolm" +0xca8f: "jjyeolb" +0xca90: "jjyeols" +0xca91: "jjyeolt" +0xca92: "jjyeolp" +0xca93: "jjyeolh" +0xca94: "jjyeom" +0xca95: "jjyeob" +0xca96: "jjyeobs" +0xca97: "jjyeos" +0xca98: "jjyeoss" +0xca99: "jjyeong" +0xca9a: "jjyeoj" +0xca9b: "jjyeoc" +0xca9c: "jjyeok" +0xca9d: "jjyeot" +0xca9e: "jjyeop" +0xca9f: "jjyeoh" +0xcaa0: "jjye" +0xcaa1: "jjyeg" +0xcaa2: "jjyegg" +0xcaa3: "jjyegs" +0xcaa4: "jjyen" +0xcaa5: "jjyenj" +0xcaa6: "jjyenh" +0xcaa7: "jjyed" +0xcaa8: "jjyel" +0xcaa9: "jjyelg" +0xcaaa: "jjyelm" +0xcaab: "jjyelb" +0xcaac: "jjyels" +0xcaad: "jjyelt" +0xcaae: "jjyelp" +0xcaaf: "jjyelh" +0xcab0: "jjyem" +0xcab1: "jjyeb" +0xcab2: "jjyebs" +0xcab3: "jjyes" +0xcab4: "jjyess" +0xcab5: "jjyeng" +0xcab6: "jjyej" +0xcab7: "jjyec" +0xcab8: "jjyek" +0xcab9: "jjyet" +0xcaba: "jjyep" +0xcabb: "jjyeh" +0xcabc: "jjo" +0xcabd: "jjog" +0xcabe: "jjogg" +0xcabf: "jjogs" +0xcac0: "jjon" +0xcac1: "jjonj" +0xcac2: "jjonh" +0xcac3: "jjod" +0xcac4: "jjol" +0xcac5: "jjolg" +0xcac6: "jjolm" +0xcac7: "jjolb" +0xcac8: "jjols" +0xcac9: "jjolt" +0xcaca: "jjolp" +0xcacb: "jjolh" +0xcacc: "jjom" +0xcacd: "jjob" +0xcace: "jjobs" +0xcacf: "jjos" +0xcad0: "jjoss" +0xcad1: "jjong" +0xcad2: "jjoj" +0xcad3: "jjoc" +0xcad4: "jjok" +0xcad5: "jjot" +0xcad6: "jjop" +0xcad7: "jjoh" +0xcad8: "jjwa" +0xcad9: "jjwag" +0xcada: "jjwagg" +0xcadb: "jjwags" +0xcadc: "jjwan" +0xcadd: "jjwanj" +0xcade: "jjwanh" +0xcadf: "jjwad" +0xcae0: "jjwal" +0xcae1: "jjwalg" +0xcae2: "jjwalm" +0xcae3: "jjwalb" +0xcae4: "jjwals" +0xcae5: "jjwalt" +0xcae6: "jjwalp" +0xcae7: "jjwalh" +0xcae8: "jjwam" +0xcae9: "jjwab" +0xcaea: "jjwabs" +0xcaeb: "jjwas" +0xcaec: "jjwass" +0xcaed: "jjwang" +0xcaee: "jjwaj" +0xcaef: "jjwac" +0xcaf0: "jjwak" +0xcaf1: "jjwat" +0xcaf2: "jjwap" +0xcaf3: "jjwah" +0xcaf4: "jjwae" +0xcaf5: "jjwaeg" +0xcaf6: "jjwaegg" +0xcaf7: "jjwaegs" +0xcaf8: "jjwaen" +0xcaf9: "jjwaenj" +0xcafa: "jjwaenh" +0xcafb: "jjwaed" +0xcafc: "jjwael" +0xcafd: "jjwaelg" +0xcafe: "jjwaelm" +0xcaff: "jjwaelb" +/* x0cb */ +0xcb00: "jjwaels" +0xcb01: "jjwaelt" +0xcb02: "jjwaelp" +0xcb03: "jjwaelh" +0xcb04: "jjwaem" +0xcb05: "jjwaeb" +0xcb06: "jjwaebs" +0xcb07: "jjwaes" +0xcb08: "jjwaess" +0xcb09: "jjwaeng" +0xcb0a: "jjwaej" +0xcb0b: "jjwaec" +0xcb0c: "jjwaek" +0xcb0d: "jjwaet" +0xcb0e: "jjwaep" +0xcb0f: "jjwaeh" +0xcb10: "jjoe" +0xcb11: "jjoeg" +0xcb12: "jjoegg" +0xcb13: "jjoegs" +0xcb14: "jjoen" +0xcb15: "jjoenj" +0xcb16: "jjoenh" +0xcb17: "jjoed" +0xcb18: "jjoel" +0xcb19: "jjoelg" +0xcb1a: "jjoelm" +0xcb1b: "jjoelb" +0xcb1c: "jjoels" +0xcb1d: "jjoelt" +0xcb1e: "jjoelp" +0xcb1f: "jjoelh" +0xcb20: "jjoem" +0xcb21: "jjoeb" +0xcb22: "jjoebs" +0xcb23: "jjoes" +0xcb24: "jjoess" +0xcb25: "jjoeng" +0xcb26: "jjoej" +0xcb27: "jjoec" +0xcb28: "jjoek" +0xcb29: "jjoet" +0xcb2a: "jjoep" +0xcb2b: "jjoeh" +0xcb2c: "jjyo" +0xcb2d: "jjyog" +0xcb2e: "jjyogg" +0xcb2f: "jjyogs" +0xcb30: "jjyon" +0xcb31: "jjyonj" +0xcb32: "jjyonh" +0xcb33: "jjyod" +0xcb34: "jjyol" +0xcb35: "jjyolg" +0xcb36: "jjyolm" +0xcb37: "jjyolb" +0xcb38: "jjyols" +0xcb39: "jjyolt" +0xcb3a: "jjyolp" +0xcb3b: "jjyolh" +0xcb3c: "jjyom" +0xcb3d: "jjyob" +0xcb3e: "jjyobs" +0xcb3f: "jjyos" +0xcb40: "jjyoss" +0xcb41: "jjyong" +0xcb42: "jjyoj" +0xcb43: "jjyoc" +0xcb44: "jjyok" +0xcb45: "jjyot" +0xcb46: "jjyop" +0xcb47: "jjyoh" +0xcb48: "jju" +0xcb49: "jjug" +0xcb4a: "jjugg" +0xcb4b: "jjugs" +0xcb4c: "jjun" +0xcb4d: "jjunj" +0xcb4e: "jjunh" +0xcb4f: "jjud" +0xcb50: "jjul" +0xcb51: "jjulg" +0xcb52: "jjulm" +0xcb53: "jjulb" +0xcb54: "jjuls" +0xcb55: "jjult" +0xcb56: "jjulp" +0xcb57: "jjulh" +0xcb58: "jjum" +0xcb59: "jjub" +0xcb5a: "jjubs" +0xcb5b: "jjus" +0xcb5c: "jjuss" +0xcb5d: "jjung" +0xcb5e: "jjuj" +0xcb5f: "jjuc" +0xcb60: "jjuk" +0xcb61: "jjut" +0xcb62: "jjup" +0xcb63: "jjuh" +0xcb64: "jjweo" +0xcb65: "jjweog" +0xcb66: "jjweogg" +0xcb67: "jjweogs" +0xcb68: "jjweon" +0xcb69: "jjweonj" +0xcb6a: "jjweonh" +0xcb6b: "jjweod" +0xcb6c: "jjweol" +0xcb6d: "jjweolg" +0xcb6e: "jjweolm" +0xcb6f: "jjweolb" +0xcb70: "jjweols" +0xcb71: "jjweolt" +0xcb72: "jjweolp" +0xcb73: "jjweolh" +0xcb74: "jjweom" +0xcb75: "jjweob" +0xcb76: "jjweobs" +0xcb77: "jjweos" +0xcb78: "jjweoss" +0xcb79: "jjweong" +0xcb7a: "jjweoj" +0xcb7b: "jjweoc" +0xcb7c: "jjweok" +0xcb7d: "jjweot" +0xcb7e: "jjweop" +0xcb7f: "jjweoh" +0xcb80: "jjwe" +0xcb81: "jjweg" +0xcb82: "jjwegg" +0xcb83: "jjwegs" +0xcb84: "jjwen" +0xcb85: "jjwenj" +0xcb86: "jjwenh" +0xcb87: "jjwed" +0xcb88: "jjwel" +0xcb89: "jjwelg" +0xcb8a: "jjwelm" +0xcb8b: "jjwelb" +0xcb8c: "jjwels" +0xcb8d: "jjwelt" +0xcb8e: "jjwelp" +0xcb8f: "jjwelh" +0xcb90: "jjwem" +0xcb91: "jjweb" +0xcb92: "jjwebs" +0xcb93: "jjwes" +0xcb94: "jjwess" +0xcb95: "jjweng" +0xcb96: "jjwej" +0xcb97: "jjwec" +0xcb98: "jjwek" +0xcb99: "jjwet" +0xcb9a: "jjwep" +0xcb9b: "jjweh" +0xcb9c: "jjwi" +0xcb9d: "jjwig" +0xcb9e: "jjwigg" +0xcb9f: "jjwigs" +0xcba0: "jjwin" +0xcba1: "jjwinj" +0xcba2: "jjwinh" +0xcba3: "jjwid" +0xcba4: "jjwil" +0xcba5: "jjwilg" +0xcba6: "jjwilm" +0xcba7: "jjwilb" +0xcba8: "jjwils" +0xcba9: "jjwilt" +0xcbaa: "jjwilp" +0xcbab: "jjwilh" +0xcbac: "jjwim" +0xcbad: "jjwib" +0xcbae: "jjwibs" +0xcbaf: "jjwis" +0xcbb0: "jjwiss" +0xcbb1: "jjwing" +0xcbb2: "jjwij" +0xcbb3: "jjwic" +0xcbb4: "jjwik" +0xcbb5: "jjwit" +0xcbb6: "jjwip" +0xcbb7: "jjwih" +0xcbb8: "jjyu" +0xcbb9: "jjyug" +0xcbba: "jjyugg" +0xcbbb: "jjyugs" +0xcbbc: "jjyun" +0xcbbd: "jjyunj" +0xcbbe: "jjyunh" +0xcbbf: "jjyud" +0xcbc0: "jjyul" +0xcbc1: "jjyulg" +0xcbc2: "jjyulm" +0xcbc3: "jjyulb" +0xcbc4: "jjyuls" +0xcbc5: "jjyult" +0xcbc6: "jjyulp" +0xcbc7: "jjyulh" +0xcbc8: "jjyum" +0xcbc9: "jjyub" +0xcbca: "jjyubs" +0xcbcb: "jjyus" +0xcbcc: "jjyuss" +0xcbcd: "jjyung" +0xcbce: "jjyuj" +0xcbcf: "jjyuc" +0xcbd0: "jjyuk" +0xcbd1: "jjyut" +0xcbd2: "jjyup" +0xcbd3: "jjyuh" +0xcbd4: "jjeu" +0xcbd5: "jjeug" +0xcbd6: "jjeugg" +0xcbd7: "jjeugs" +0xcbd8: "jjeun" +0xcbd9: "jjeunj" +0xcbda: "jjeunh" +0xcbdb: "jjeud" +0xcbdc: "jjeul" +0xcbdd: "jjeulg" +0xcbde: "jjeulm" +0xcbdf: "jjeulb" +0xcbe0: "jjeuls" +0xcbe1: "jjeult" +0xcbe2: "jjeulp" +0xcbe3: "jjeulh" +0xcbe4: "jjeum" +0xcbe5: "jjeub" +0xcbe6: "jjeubs" +0xcbe7: "jjeus" +0xcbe8: "jjeuss" +0xcbe9: "jjeung" +0xcbea: "jjeuj" +0xcbeb: "jjeuc" +0xcbec: "jjeuk" +0xcbed: "jjeut" +0xcbee: "jjeup" +0xcbef: "jjeuh" +0xcbf0: "jjyi" +0xcbf1: "jjyig" +0xcbf2: "jjyigg" +0xcbf3: "jjyigs" +0xcbf4: "jjyin" +0xcbf5: "jjyinj" +0xcbf6: "jjyinh" +0xcbf7: "jjyid" +0xcbf8: "jjyil" +0xcbf9: "jjyilg" +0xcbfa: "jjyilm" +0xcbfb: "jjyilb" +0xcbfc: "jjyils" +0xcbfd: "jjyilt" +0xcbfe: "jjyilp" +0xcbff: "jjyilh" +/* x0cc */ +0xcc00: "jjyim" +0xcc01: "jjyib" +0xcc02: "jjyibs" +0xcc03: "jjyis" +0xcc04: "jjyiss" +0xcc05: "jjying" +0xcc06: "jjyij" +0xcc07: "jjyic" +0xcc08: "jjyik" +0xcc09: "jjyit" +0xcc0a: "jjyip" +0xcc0b: "jjyih" +0xcc0c: "jji" +0xcc0d: "jjig" +0xcc0e: "jjigg" +0xcc0f: "jjigs" +0xcc10: "jjin" +0xcc11: "jjinj" +0xcc12: "jjinh" +0xcc13: "jjid" +0xcc14: "jjil" +0xcc15: "jjilg" +0xcc16: "jjilm" +0xcc17: "jjilb" +0xcc18: "jjils" +0xcc19: "jjilt" +0xcc1a: "jjilp" +0xcc1b: "jjilh" +0xcc1c: "jjim" +0xcc1d: "jjib" +0xcc1e: "jjibs" +0xcc1f: "jjis" +0xcc20: "jjiss" +0xcc21: "jjing" +0xcc22: "jjij" +0xcc23: "jjic" +0xcc24: "jjik" +0xcc25: "jjit" +0xcc26: "jjip" +0xcc27: "jjih" +0xcc28: "ca" +0xcc29: "cag" +0xcc2a: "cagg" +0xcc2b: "cags" +0xcc2c: "can" +0xcc2d: "canj" +0xcc2e: "canh" +0xcc2f: "cad" +0xcc30: "cal" +0xcc31: "calg" +0xcc32: "calm" +0xcc33: "calb" +0xcc34: "cals" +0xcc35: "calt" +0xcc36: "calp" +0xcc37: "calh" +0xcc38: "cam" +0xcc39: "cab" +0xcc3a: "cabs" +0xcc3b: "cas" +0xcc3c: "cass" +0xcc3d: "cang" +0xcc3e: "caj" +0xcc3f: "cac" +0xcc40: "cak" +0xcc41: "cat" +0xcc42: "cap" +0xcc43: "cah" +0xcc44: "cae" +0xcc45: "caeg" +0xcc46: "caegg" +0xcc47: "caegs" +0xcc48: "caen" +0xcc49: "caenj" +0xcc4a: "caenh" +0xcc4b: "caed" +0xcc4c: "cael" +0xcc4d: "caelg" +0xcc4e: "caelm" +0xcc4f: "caelb" +0xcc50: "caels" +0xcc51: "caelt" +0xcc52: "caelp" +0xcc53: "caelh" +0xcc54: "caem" +0xcc55: "caeb" +0xcc56: "caebs" +0xcc57: "caes" +0xcc58: "caess" +0xcc59: "caeng" +0xcc5a: "caej" +0xcc5b: "caec" +0xcc5c: "caek" +0xcc5d: "caet" +0xcc5e: "caep" +0xcc5f: "caeh" +0xcc60: "cya" +0xcc61: "cyag" +0xcc62: "cyagg" +0xcc63: "cyags" +0xcc64: "cyan" +0xcc65: "cyanj" +0xcc66: "cyanh" +0xcc67: "cyad" +0xcc68: "cyal" +0xcc69: "cyalg" +0xcc6a: "cyalm" +0xcc6b: "cyalb" +0xcc6c: "cyals" +0xcc6d: "cyalt" +0xcc6e: "cyalp" +0xcc6f: "cyalh" +0xcc70: "cyam" +0xcc71: "cyab" +0xcc72: "cyabs" +0xcc73: "cyas" +0xcc74: "cyass" +0xcc75: "cyang" +0xcc76: "cyaj" +0xcc77: "cyac" +0xcc78: "cyak" +0xcc79: "cyat" +0xcc7a: "cyap" +0xcc7b: "cyah" +0xcc7c: "cyae" +0xcc7d: "cyaeg" +0xcc7e: "cyaegg" +0xcc7f: "cyaegs" +0xcc80: "cyaen" +0xcc81: "cyaenj" +0xcc82: "cyaenh" +0xcc83: "cyaed" +0xcc84: "cyael" +0xcc85: "cyaelg" +0xcc86: "cyaelm" +0xcc87: "cyaelb" +0xcc88: "cyaels" +0xcc89: "cyaelt" +0xcc8a: "cyaelp" +0xcc8b: "cyaelh" +0xcc8c: "cyaem" +0xcc8d: "cyaeb" +0xcc8e: "cyaebs" +0xcc8f: "cyaes" +0xcc90: "cyaess" +0xcc91: "cyaeng" +0xcc92: "cyaej" +0xcc93: "cyaec" +0xcc94: "cyaek" +0xcc95: "cyaet" +0xcc96: "cyaep" +0xcc97: "cyaeh" +0xcc98: "ceo" +0xcc99: "ceog" +0xcc9a: "ceogg" +0xcc9b: "ceogs" +0xcc9c: "ceon" +0xcc9d: "ceonj" +0xcc9e: "ceonh" +0xcc9f: "ceod" +0xcca0: "ceol" +0xcca1: "ceolg" +0xcca2: "ceolm" +0xcca3: "ceolb" +0xcca4: "ceols" +0xcca5: "ceolt" +0xcca6: "ceolp" +0xcca7: "ceolh" +0xcca8: "ceom" +0xcca9: "ceob" +0xccaa: "ceobs" +0xccab: "ceos" +0xccac: "ceoss" +0xccad: "ceong" +0xccae: "ceoj" +0xccaf: "ceoc" +0xccb0: "ceok" +0xccb1: "ceot" +0xccb2: "ceop" +0xccb3: "ceoh" +0xccb4: "ce" +0xccb5: "ceg" +0xccb6: "cegg" +0xccb7: "cegs" +0xccb8: "cen" +0xccb9: "cenj" +0xccba: "cenh" +0xccbb: "ced" +0xccbc: "cel" +0xccbd: "celg" +0xccbe: "celm" +0xccbf: "celb" +0xccc0: "cels" +0xccc1: "celt" +0xccc2: "celp" +0xccc3: "celh" +0xccc4: "cem" +0xccc5: "ceb" +0xccc6: "cebs" +0xccc7: "ces" +0xccc8: "cess" +0xccc9: "ceng" +0xccca: "cej" +0xcccb: "cec" +0xcccc: "cek" +0xcccd: "cet" +0xccce: "cep" +0xcccf: "ceh" +0xccd0: "cyeo" +0xccd1: "cyeog" +0xccd2: "cyeogg" +0xccd3: "cyeogs" +0xccd4: "cyeon" +0xccd5: "cyeonj" +0xccd6: "cyeonh" +0xccd7: "cyeod" +0xccd8: "cyeol" +0xccd9: "cyeolg" +0xccda: "cyeolm" +0xccdb: "cyeolb" +0xccdc: "cyeols" +0xccdd: "cyeolt" +0xccde: "cyeolp" +0xccdf: "cyeolh" +0xcce0: "cyeom" +0xcce1: "cyeob" +0xcce2: "cyeobs" +0xcce3: "cyeos" +0xcce4: "cyeoss" +0xcce5: "cyeong" +0xcce6: "cyeoj" +0xcce7: "cyeoc" +0xcce8: "cyeok" +0xcce9: "cyeot" +0xccea: "cyeop" +0xcceb: "cyeoh" +0xccec: "cye" +0xcced: "cyeg" +0xccee: "cyegg" +0xccef: "cyegs" +0xccf0: "cyen" +0xccf1: "cyenj" +0xccf2: "cyenh" +0xccf3: "cyed" +0xccf4: "cyel" +0xccf5: "cyelg" +0xccf6: "cyelm" +0xccf7: "cyelb" +0xccf8: "cyels" +0xccf9: "cyelt" +0xccfa: "cyelp" +0xccfb: "cyelh" +0xccfc: "cyem" +0xccfd: "cyeb" +0xccfe: "cyebs" +0xccff: "cyes" +/* x0cd */ +0xcd00: "cyess" +0xcd01: "cyeng" +0xcd02: "cyej" +0xcd03: "cyec" +0xcd04: "cyek" +0xcd05: "cyet" +0xcd06: "cyep" +0xcd07: "cyeh" +0xcd08: "co" +0xcd09: "cog" +0xcd0a: "cogg" +0xcd0b: "cogs" +0xcd0c: "con" +0xcd0d: "conj" +0xcd0e: "conh" +0xcd0f: "cod" +0xcd10: "col" +0xcd11: "colg" +0xcd12: "colm" +0xcd13: "colb" +0xcd14: "cols" +0xcd15: "colt" +0xcd16: "colp" +0xcd17: "colh" +0xcd18: "com" +0xcd19: "cob" +0xcd1a: "cobs" +0xcd1b: "cos" +0xcd1c: "coss" +0xcd1d: "cong" +0xcd1e: "coj" +0xcd1f: "coc" +0xcd20: "cok" +0xcd21: "cot" +0xcd22: "cop" +0xcd23: "coh" +0xcd24: "cwa" +0xcd25: "cwag" +0xcd26: "cwagg" +0xcd27: "cwags" +0xcd28: "cwan" +0xcd29: "cwanj" +0xcd2a: "cwanh" +0xcd2b: "cwad" +0xcd2c: "cwal" +0xcd2d: "cwalg" +0xcd2e: "cwalm" +0xcd2f: "cwalb" +0xcd30: "cwals" +0xcd31: "cwalt" +0xcd32: "cwalp" +0xcd33: "cwalh" +0xcd34: "cwam" +0xcd35: "cwab" +0xcd36: "cwabs" +0xcd37: "cwas" +0xcd38: "cwass" +0xcd39: "cwang" +0xcd3a: "cwaj" +0xcd3b: "cwac" +0xcd3c: "cwak" +0xcd3d: "cwat" +0xcd3e: "cwap" +0xcd3f: "cwah" +0xcd40: "cwae" +0xcd41: "cwaeg" +0xcd42: "cwaegg" +0xcd43: "cwaegs" +0xcd44: "cwaen" +0xcd45: "cwaenj" +0xcd46: "cwaenh" +0xcd47: "cwaed" +0xcd48: "cwael" +0xcd49: "cwaelg" +0xcd4a: "cwaelm" +0xcd4b: "cwaelb" +0xcd4c: "cwaels" +0xcd4d: "cwaelt" +0xcd4e: "cwaelp" +0xcd4f: "cwaelh" +0xcd50: "cwaem" +0xcd51: "cwaeb" +0xcd52: "cwaebs" +0xcd53: "cwaes" +0xcd54: "cwaess" +0xcd55: "cwaeng" +0xcd56: "cwaej" +0xcd57: "cwaec" +0xcd58: "cwaek" +0xcd59: "cwaet" +0xcd5a: "cwaep" +0xcd5b: "cwaeh" +0xcd5c: "coe" +0xcd5d: "coeg" +0xcd5e: "coegg" +0xcd5f: "coegs" +0xcd60: "coen" +0xcd61: "coenj" +0xcd62: "coenh" +0xcd63: "coed" +0xcd64: "coel" +0xcd65: "coelg" +0xcd66: "coelm" +0xcd67: "coelb" +0xcd68: "coels" +0xcd69: "coelt" +0xcd6a: "coelp" +0xcd6b: "coelh" +0xcd6c: "coem" +0xcd6d: "coeb" +0xcd6e: "coebs" +0xcd6f: "coes" +0xcd70: "coess" +0xcd71: "coeng" +0xcd72: "coej" +0xcd73: "coec" +0xcd74: "coek" +0xcd75: "coet" +0xcd76: "coep" +0xcd77: "coeh" +0xcd78: "cyo" +0xcd79: "cyog" +0xcd7a: "cyogg" +0xcd7b: "cyogs" +0xcd7c: "cyon" +0xcd7d: "cyonj" +0xcd7e: "cyonh" +0xcd7f: "cyod" +0xcd80: "cyol" +0xcd81: "cyolg" +0xcd82: "cyolm" +0xcd83: "cyolb" +0xcd84: "cyols" +0xcd85: "cyolt" +0xcd86: "cyolp" +0xcd87: "cyolh" +0xcd88: "cyom" +0xcd89: "cyob" +0xcd8a: "cyobs" +0xcd8b: "cyos" +0xcd8c: "cyoss" +0xcd8d: "cyong" +0xcd8e: "cyoj" +0xcd8f: "cyoc" +0xcd90: "cyok" +0xcd91: "cyot" +0xcd92: "cyop" +0xcd93: "cyoh" +0xcd94: "cu" +0xcd95: "cug" +0xcd96: "cugg" +0xcd97: "cugs" +0xcd98: "cun" +0xcd99: "cunj" +0xcd9a: "cunh" +0xcd9b: "cud" +0xcd9c: "cul" +0xcd9d: "culg" +0xcd9e: "culm" +0xcd9f: "culb" +0xcda0: "culs" +0xcda1: "cult" +0xcda2: "culp" +0xcda3: "culh" +0xcda4: "cum" +0xcda5: "cub" +0xcda6: "cubs" +0xcda7: "cus" +0xcda8: "cuss" +0xcda9: "cung" +0xcdaa: "cuj" +0xcdab: "cuc" +0xcdac: "cuk" +0xcdad: "cut" +0xcdae: "cup" +0xcdaf: "cuh" +0xcdb0: "cweo" +0xcdb1: "cweog" +0xcdb2: "cweogg" +0xcdb3: "cweogs" +0xcdb4: "cweon" +0xcdb5: "cweonj" +0xcdb6: "cweonh" +0xcdb7: "cweod" +0xcdb8: "cweol" +0xcdb9: "cweolg" +0xcdba: "cweolm" +0xcdbb: "cweolb" +0xcdbc: "cweols" +0xcdbd: "cweolt" +0xcdbe: "cweolp" +0xcdbf: "cweolh" +0xcdc0: "cweom" +0xcdc1: "cweob" +0xcdc2: "cweobs" +0xcdc3: "cweos" +0xcdc4: "cweoss" +0xcdc5: "cweong" +0xcdc6: "cweoj" +0xcdc7: "cweoc" +0xcdc8: "cweok" +0xcdc9: "cweot" +0xcdca: "cweop" +0xcdcb: "cweoh" +0xcdcc: "cwe" +0xcdcd: "cweg" +0xcdce: "cwegg" +0xcdcf: "cwegs" +0xcdd0: "cwen" +0xcdd1: "cwenj" +0xcdd2: "cwenh" +0xcdd3: "cwed" +0xcdd4: "cwel" +0xcdd5: "cwelg" +0xcdd6: "cwelm" +0xcdd7: "cwelb" +0xcdd8: "cwels" +0xcdd9: "cwelt" +0xcdda: "cwelp" +0xcddb: "cwelh" +0xcddc: "cwem" +0xcddd: "cweb" +0xcdde: "cwebs" +0xcddf: "cwes" +0xcde0: "cwess" +0xcde1: "cweng" +0xcde2: "cwej" +0xcde3: "cwec" +0xcde4: "cwek" +0xcde5: "cwet" +0xcde6: "cwep" +0xcde7: "cweh" +0xcde8: "cwi" +0xcde9: "cwig" +0xcdea: "cwigg" +0xcdeb: "cwigs" +0xcdec: "cwin" +0xcded: "cwinj" +0xcdee: "cwinh" +0xcdef: "cwid" +0xcdf0: "cwil" +0xcdf1: "cwilg" +0xcdf2: "cwilm" +0xcdf3: "cwilb" +0xcdf4: "cwils" +0xcdf5: "cwilt" +0xcdf6: "cwilp" +0xcdf7: "cwilh" +0xcdf8: "cwim" +0xcdf9: "cwib" +0xcdfa: "cwibs" +0xcdfb: "cwis" +0xcdfc: "cwiss" +0xcdfd: "cwing" +0xcdfe: "cwij" +0xcdff: "cwic" +/* x0ce */ +0xce00: "cwik" +0xce01: "cwit" +0xce02: "cwip" +0xce03: "cwih" +0xce04: "cyu" +0xce05: "cyug" +0xce06: "cyugg" +0xce07: "cyugs" +0xce08: "cyun" +0xce09: "cyunj" +0xce0a: "cyunh" +0xce0b: "cyud" +0xce0c: "cyul" +0xce0d: "cyulg" +0xce0e: "cyulm" +0xce0f: "cyulb" +0xce10: "cyuls" +0xce11: "cyult" +0xce12: "cyulp" +0xce13: "cyulh" +0xce14: "cyum" +0xce15: "cyub" +0xce16: "cyubs" +0xce17: "cyus" +0xce18: "cyuss" +0xce19: "cyung" +0xce1a: "cyuj" +0xce1b: "cyuc" +0xce1c: "cyuk" +0xce1d: "cyut" +0xce1e: "cyup" +0xce1f: "cyuh" +0xce20: "ceu" +0xce21: "ceug" +0xce22: "ceugg" +0xce23: "ceugs" +0xce24: "ceun" +0xce25: "ceunj" +0xce26: "ceunh" +0xce27: "ceud" +0xce28: "ceul" +0xce29: "ceulg" +0xce2a: "ceulm" +0xce2b: "ceulb" +0xce2c: "ceuls" +0xce2d: "ceult" +0xce2e: "ceulp" +0xce2f: "ceulh" +0xce30: "ceum" +0xce31: "ceub" +0xce32: "ceubs" +0xce33: "ceus" +0xce34: "ceuss" +0xce35: "ceung" +0xce36: "ceuj" +0xce37: "ceuc" +0xce38: "ceuk" +0xce39: "ceut" +0xce3a: "ceup" +0xce3b: "ceuh" +0xce3c: "cyi" +0xce3d: "cyig" +0xce3e: "cyigg" +0xce3f: "cyigs" +0xce40: "cyin" +0xce41: "cyinj" +0xce42: "cyinh" +0xce43: "cyid" +0xce44: "cyil" +0xce45: "cyilg" +0xce46: "cyilm" +0xce47: "cyilb" +0xce48: "cyils" +0xce49: "cyilt" +0xce4a: "cyilp" +0xce4b: "cyilh" +0xce4c: "cyim" +0xce4d: "cyib" +0xce4e: "cyibs" +0xce4f: "cyis" +0xce50: "cyiss" +0xce51: "cying" +0xce52: "cyij" +0xce53: "cyic" +0xce54: "cyik" +0xce55: "cyit" +0xce56: "cyip" +0xce57: "cyih" +0xce58: "ci" +0xce59: "cig" +0xce5a: "cigg" +0xce5b: "cigs" +0xce5c: "cin" +0xce5d: "cinj" +0xce5e: "cinh" +0xce5f: "cid" +0xce60: "cil" +0xce61: "cilg" +0xce62: "cilm" +0xce63: "cilb" +0xce64: "cils" +0xce65: "cilt" +0xce66: "cilp" +0xce67: "cilh" +0xce68: "cim" +0xce69: "cib" +0xce6a: "cibs" +0xce6b: "cis" +0xce6c: "ciss" +0xce6d: "cing" +0xce6e: "cij" +0xce6f: "cic" +0xce70: "cik" +0xce71: "cit" +0xce72: "cip" +0xce73: "cih" +0xce74: "ka" +0xce75: "kag" +0xce76: "kagg" +0xce77: "kags" +0xce78: "kan" +0xce79: "kanj" +0xce7a: "kanh" +0xce7b: "kad" +0xce7c: "kal" +0xce7d: "kalg" +0xce7e: "kalm" +0xce7f: "kalb" +0xce80: "kals" +0xce81: "kalt" +0xce82: "kalp" +0xce83: "kalh" +0xce84: "kam" +0xce85: "kab" +0xce86: "kabs" +0xce87: "kas" +0xce88: "kass" +0xce89: "kang" +0xce8a: "kaj" +0xce8b: "kac" +0xce8c: "kak" +0xce8d: "kat" +0xce8e: "kap" +0xce8f: "kah" +0xce90: "kae" +0xce91: "kaeg" +0xce92: "kaegg" +0xce93: "kaegs" +0xce94: "kaen" +0xce95: "kaenj" +0xce96: "kaenh" +0xce97: "kaed" +0xce98: "kael" +0xce99: "kaelg" +0xce9a: "kaelm" +0xce9b: "kaelb" +0xce9c: "kaels" +0xce9d: "kaelt" +0xce9e: "kaelp" +0xce9f: "kaelh" +0xcea0: "kaem" +0xcea1: "kaeb" +0xcea2: "kaebs" +0xcea3: "kaes" +0xcea4: "kaess" +0xcea5: "kaeng" +0xcea6: "kaej" +0xcea7: "kaec" +0xcea8: "kaek" +0xcea9: "kaet" +0xceaa: "kaep" +0xceab: "kaeh" +0xceac: "kya" +0xcead: "kyag" +0xceae: "kyagg" +0xceaf: "kyags" +0xceb0: "kyan" +0xceb1: "kyanj" +0xceb2: "kyanh" +0xceb3: "kyad" +0xceb4: "kyal" +0xceb5: "kyalg" +0xceb6: "kyalm" +0xceb7: "kyalb" +0xceb8: "kyals" +0xceb9: "kyalt" +0xceba: "kyalp" +0xcebb: "kyalh" +0xcebc: "kyam" +0xcebd: "kyab" +0xcebe: "kyabs" +0xcebf: "kyas" +0xcec0: "kyass" +0xcec1: "kyang" +0xcec2: "kyaj" +0xcec3: "kyac" +0xcec4: "kyak" +0xcec5: "kyat" +0xcec6: "kyap" +0xcec7: "kyah" +0xcec8: "kyae" +0xcec9: "kyaeg" +0xceca: "kyaegg" +0xcecb: "kyaegs" +0xcecc: "kyaen" +0xcecd: "kyaenj" +0xcece: "kyaenh" +0xcecf: "kyaed" +0xced0: "kyael" +0xced1: "kyaelg" +0xced2: "kyaelm" +0xced3: "kyaelb" +0xced4: "kyaels" +0xced5: "kyaelt" +0xced6: "kyaelp" +0xced7: "kyaelh" +0xced8: "kyaem" +0xced9: "kyaeb" +0xceda: "kyaebs" +0xcedb: "kyaes" +0xcedc: "kyaess" +0xcedd: "kyaeng" +0xcede: "kyaej" +0xcedf: "kyaec" +0xcee0: "kyaek" +0xcee1: "kyaet" +0xcee2: "kyaep" +0xcee3: "kyaeh" +0xcee4: "keo" +0xcee5: "keog" +0xcee6: "keogg" +0xcee7: "keogs" +0xcee8: "keon" +0xcee9: "keonj" +0xceea: "keonh" +0xceeb: "keod" +0xceec: "keol" +0xceed: "keolg" +0xceee: "keolm" +0xceef: "keolb" +0xcef0: "keols" +0xcef1: "keolt" +0xcef2: "keolp" +0xcef3: "keolh" +0xcef4: "keom" +0xcef5: "keob" +0xcef6: "keobs" +0xcef7: "keos" +0xcef8: "keoss" +0xcef9: "keong" +0xcefa: "keoj" +0xcefb: "keoc" +0xcefc: "keok" +0xcefd: "keot" +0xcefe: "keop" +0xceff: "keoh" +/* x0cf */ +0xcf00: "ke" +0xcf01: "keg" +0xcf02: "kegg" +0xcf03: "kegs" +0xcf04: "ken" +0xcf05: "kenj" +0xcf06: "kenh" +0xcf07: "ked" +0xcf08: "kel" +0xcf09: "kelg" +0xcf0a: "kelm" +0xcf0b: "kelb" +0xcf0c: "kels" +0xcf0d: "kelt" +0xcf0e: "kelp" +0xcf0f: "kelh" +0xcf10: "kem" +0xcf11: "keb" +0xcf12: "kebs" +0xcf13: "kes" +0xcf14: "kess" +0xcf15: "keng" +0xcf16: "kej" +0xcf17: "kec" +0xcf18: "kek" +0xcf19: "ket" +0xcf1a: "kep" +0xcf1b: "keh" +0xcf1c: "kyeo" +0xcf1d: "kyeog" +0xcf1e: "kyeogg" +0xcf1f: "kyeogs" +0xcf20: "kyeon" +0xcf21: "kyeonj" +0xcf22: "kyeonh" +0xcf23: "kyeod" +0xcf24: "kyeol" +0xcf25: "kyeolg" +0xcf26: "kyeolm" +0xcf27: "kyeolb" +0xcf28: "kyeols" +0xcf29: "kyeolt" +0xcf2a: "kyeolp" +0xcf2b: "kyeolh" +0xcf2c: "kyeom" +0xcf2d: "kyeob" +0xcf2e: "kyeobs" +0xcf2f: "kyeos" +0xcf30: "kyeoss" +0xcf31: "kyeong" +0xcf32: "kyeoj" +0xcf33: "kyeoc" +0xcf34: "kyeok" +0xcf35: "kyeot" +0xcf36: "kyeop" +0xcf37: "kyeoh" +0xcf38: "kye" +0xcf39: "kyeg" +0xcf3a: "kyegg" +0xcf3b: "kyegs" +0xcf3c: "kyen" +0xcf3d: "kyenj" +0xcf3e: "kyenh" +0xcf3f: "kyed" +0xcf40: "kyel" +0xcf41: "kyelg" +0xcf42: "kyelm" +0xcf43: "kyelb" +0xcf44: "kyels" +0xcf45: "kyelt" +0xcf46: "kyelp" +0xcf47: "kyelh" +0xcf48: "kyem" +0xcf49: "kyeb" +0xcf4a: "kyebs" +0xcf4b: "kyes" +0xcf4c: "kyess" +0xcf4d: "kyeng" +0xcf4e: "kyej" +0xcf4f: "kyec" +0xcf50: "kyek" +0xcf51: "kyet" +0xcf52: "kyep" +0xcf53: "kyeh" +0xcf54: "ko" +0xcf55: "kog" +0xcf56: "kogg" +0xcf57: "kogs" +0xcf58: "kon" +0xcf59: "konj" +0xcf5a: "konh" +0xcf5b: "kod" +0xcf5c: "kol" +0xcf5d: "kolg" +0xcf5e: "kolm" +0xcf5f: "kolb" +0xcf60: "kols" +0xcf61: "kolt" +0xcf62: "kolp" +0xcf63: "kolh" +0xcf64: "kom" +0xcf65: "kob" +0xcf66: "kobs" +0xcf67: "kos" +0xcf68: "koss" +0xcf69: "kong" +0xcf6a: "koj" +0xcf6b: "koc" +0xcf6c: "kok" +0xcf6d: "kot" +0xcf6e: "kop" +0xcf6f: "koh" +0xcf70: "kwa" +0xcf71: "kwag" +0xcf72: "kwagg" +0xcf73: "kwags" +0xcf74: "kwan" +0xcf75: "kwanj" +0xcf76: "kwanh" +0xcf77: "kwad" +0xcf78: "kwal" +0xcf79: "kwalg" +0xcf7a: "kwalm" +0xcf7b: "kwalb" +0xcf7c: "kwals" +0xcf7d: "kwalt" +0xcf7e: "kwalp" +0xcf7f: "kwalh" +0xcf80: "kwam" +0xcf81: "kwab" +0xcf82: "kwabs" +0xcf83: "kwas" +0xcf84: "kwass" +0xcf85: "kwang" +0xcf86: "kwaj" +0xcf87: "kwac" +0xcf88: "kwak" +0xcf89: "kwat" +0xcf8a: "kwap" +0xcf8b: "kwah" +0xcf8c: "kwae" +0xcf8d: "kwaeg" +0xcf8e: "kwaegg" +0xcf8f: "kwaegs" +0xcf90: "kwaen" +0xcf91: "kwaenj" +0xcf92: "kwaenh" +0xcf93: "kwaed" +0xcf94: "kwael" +0xcf95: "kwaelg" +0xcf96: "kwaelm" +0xcf97: "kwaelb" +0xcf98: "kwaels" +0xcf99: "kwaelt" +0xcf9a: "kwaelp" +0xcf9b: "kwaelh" +0xcf9c: "kwaem" +0xcf9d: "kwaeb" +0xcf9e: "kwaebs" +0xcf9f: "kwaes" +0xcfa0: "kwaess" +0xcfa1: "kwaeng" +0xcfa2: "kwaej" +0xcfa3: "kwaec" +0xcfa4: "kwaek" +0xcfa5: "kwaet" +0xcfa6: "kwaep" +0xcfa7: "kwaeh" +0xcfa8: "koe" +0xcfa9: "koeg" +0xcfaa: "koegg" +0xcfab: "koegs" +0xcfac: "koen" +0xcfad: "koenj" +0xcfae: "koenh" +0xcfaf: "koed" +0xcfb0: "koel" +0xcfb1: "koelg" +0xcfb2: "koelm" +0xcfb3: "koelb" +0xcfb4: "koels" +0xcfb5: "koelt" +0xcfb6: "koelp" +0xcfb7: "koelh" +0xcfb8: "koem" +0xcfb9: "koeb" +0xcfba: "koebs" +0xcfbb: "koes" +0xcfbc: "koess" +0xcfbd: "koeng" +0xcfbe: "koej" +0xcfbf: "koec" +0xcfc0: "koek" +0xcfc1: "koet" +0xcfc2: "koep" +0xcfc3: "koeh" +0xcfc4: "kyo" +0xcfc5: "kyog" +0xcfc6: "kyogg" +0xcfc7: "kyogs" +0xcfc8: "kyon" +0xcfc9: "kyonj" +0xcfca: "kyonh" +0xcfcb: "kyod" +0xcfcc: "kyol" +0xcfcd: "kyolg" +0xcfce: "kyolm" +0xcfcf: "kyolb" +0xcfd0: "kyols" +0xcfd1: "kyolt" +0xcfd2: "kyolp" +0xcfd3: "kyolh" +0xcfd4: "kyom" +0xcfd5: "kyob" +0xcfd6: "kyobs" +0xcfd7: "kyos" +0xcfd8: "kyoss" +0xcfd9: "kyong" +0xcfda: "kyoj" +0xcfdb: "kyoc" +0xcfdc: "kyok" +0xcfdd: "kyot" +0xcfde: "kyop" +0xcfdf: "kyoh" +0xcfe0: "ku" +0xcfe1: "kug" +0xcfe2: "kugg" +0xcfe3: "kugs" +0xcfe4: "kun" +0xcfe5: "kunj" +0xcfe6: "kunh" +0xcfe7: "kud" +0xcfe8: "kul" +0xcfe9: "kulg" +0xcfea: "kulm" +0xcfeb: "kulb" +0xcfec: "kuls" +0xcfed: "kult" +0xcfee: "kulp" +0xcfef: "kulh" +0xcff0: "kum" +0xcff1: "kub" +0xcff2: "kubs" +0xcff3: "kus" +0xcff4: "kuss" +0xcff5: "kung" +0xcff6: "kuj" +0xcff7: "kuc" +0xcff8: "kuk" +0xcff9: "kut" +0xcffa: "kup" +0xcffb: "kuh" +0xcffc: "kweo" +0xcffd: "kweog" +0xcffe: "kweogg" +0xcfff: "kweogs" +/* x0d0 */ +0xd000: "kweon" +0xd001: "kweonj" +0xd002: "kweonh" +0xd003: "kweod" +0xd004: "kweol" +0xd005: "kweolg" +0xd006: "kweolm" +0xd007: "kweolb" +0xd008: "kweols" +0xd009: "kweolt" +0xd00a: "kweolp" +0xd00b: "kweolh" +0xd00c: "kweom" +0xd00d: "kweob" +0xd00e: "kweobs" +0xd00f: "kweos" +0xd010: "kweoss" +0xd011: "kweong" +0xd012: "kweoj" +0xd013: "kweoc" +0xd014: "kweok" +0xd015: "kweot" +0xd016: "kweop" +0xd017: "kweoh" +0xd018: "kwe" +0xd019: "kweg" +0xd01a: "kwegg" +0xd01b: "kwegs" +0xd01c: "kwen" +0xd01d: "kwenj" +0xd01e: "kwenh" +0xd01f: "kwed" +0xd020: "kwel" +0xd021: "kwelg" +0xd022: "kwelm" +0xd023: "kwelb" +0xd024: "kwels" +0xd025: "kwelt" +0xd026: "kwelp" +0xd027: "kwelh" +0xd028: "kwem" +0xd029: "kweb" +0xd02a: "kwebs" +0xd02b: "kwes" +0xd02c: "kwess" +0xd02d: "kweng" +0xd02e: "kwej" +0xd02f: "kwec" +0xd030: "kwek" +0xd031: "kwet" +0xd032: "kwep" +0xd033: "kweh" +0xd034: "kwi" +0xd035: "kwig" +0xd036: "kwigg" +0xd037: "kwigs" +0xd038: "kwin" +0xd039: "kwinj" +0xd03a: "kwinh" +0xd03b: "kwid" +0xd03c: "kwil" +0xd03d: "kwilg" +0xd03e: "kwilm" +0xd03f: "kwilb" +0xd040: "kwils" +0xd041: "kwilt" +0xd042: "kwilp" +0xd043: "kwilh" +0xd044: "kwim" +0xd045: "kwib" +0xd046: "kwibs" +0xd047: "kwis" +0xd048: "kwiss" +0xd049: "kwing" +0xd04a: "kwij" +0xd04b: "kwic" +0xd04c: "kwik" +0xd04d: "kwit" +0xd04e: "kwip" +0xd04f: "kwih" +0xd050: "kyu" +0xd051: "kyug" +0xd052: "kyugg" +0xd053: "kyugs" +0xd054: "kyun" +0xd055: "kyunj" +0xd056: "kyunh" +0xd057: "kyud" +0xd058: "kyul" +0xd059: "kyulg" +0xd05a: "kyulm" +0xd05b: "kyulb" +0xd05c: "kyuls" +0xd05d: "kyult" +0xd05e: "kyulp" +0xd05f: "kyulh" +0xd060: "kyum" +0xd061: "kyub" +0xd062: "kyubs" +0xd063: "kyus" +0xd064: "kyuss" +0xd065: "kyung" +0xd066: "kyuj" +0xd067: "kyuc" +0xd068: "kyuk" +0xd069: "kyut" +0xd06a: "kyup" +0xd06b: "kyuh" +0xd06c: "keu" +0xd06d: "keug" +0xd06e: "keugg" +0xd06f: "keugs" +0xd070: "keun" +0xd071: "keunj" +0xd072: "keunh" +0xd073: "keud" +0xd074: "keul" +0xd075: "keulg" +0xd076: "keulm" +0xd077: "keulb" +0xd078: "keuls" +0xd079: "keult" +0xd07a: "keulp" +0xd07b: "keulh" +0xd07c: "keum" +0xd07d: "keub" +0xd07e: "keubs" +0xd07f: "keus" +0xd080: "keuss" +0xd081: "keung" +0xd082: "keuj" +0xd083: "keuc" +0xd084: "keuk" +0xd085: "keut" +0xd086: "keup" +0xd087: "keuh" +0xd088: "kyi" +0xd089: "kyig" +0xd08a: "kyigg" +0xd08b: "kyigs" +0xd08c: "kyin" +0xd08d: "kyinj" +0xd08e: "kyinh" +0xd08f: "kyid" +0xd090: "kyil" +0xd091: "kyilg" +0xd092: "kyilm" +0xd093: "kyilb" +0xd094: "kyils" +0xd095: "kyilt" +0xd096: "kyilp" +0xd097: "kyilh" +0xd098: "kyim" +0xd099: "kyib" +0xd09a: "kyibs" +0xd09b: "kyis" +0xd09c: "kyiss" +0xd09d: "kying" +0xd09e: "kyij" +0xd09f: "kyic" +0xd0a0: "kyik" +0xd0a1: "kyit" +0xd0a2: "kyip" +0xd0a3: "kyih" +0xd0a4: "ki" +0xd0a5: "kig" +0xd0a6: "kigg" +0xd0a7: "kigs" +0xd0a8: "kin" +0xd0a9: "kinj" +0xd0aa: "kinh" +0xd0ab: "kid" +0xd0ac: "kil" +0xd0ad: "kilg" +0xd0ae: "kilm" +0xd0af: "kilb" +0xd0b0: "kils" +0xd0b1: "kilt" +0xd0b2: "kilp" +0xd0b3: "kilh" +0xd0b4: "kim" +0xd0b5: "kib" +0xd0b6: "kibs" +0xd0b7: "kis" +0xd0b8: "kiss" +0xd0b9: "king" +0xd0ba: "kij" +0xd0bb: "kic" +0xd0bc: "kik" +0xd0bd: "kit" +0xd0be: "kip" +0xd0bf: "kih" +0xd0c0: "ta" +0xd0c1: "tag" +0xd0c2: "tagg" +0xd0c3: "tags" +0xd0c4: "tan" +0xd0c5: "tanj" +0xd0c6: "tanh" +0xd0c7: "tad" +0xd0c8: "tal" +0xd0c9: "talg" +0xd0ca: "talm" +0xd0cb: "talb" +0xd0cc: "tals" +0xd0cd: "talt" +0xd0ce: "talp" +0xd0cf: "talh" +0xd0d0: "tam" +0xd0d1: "tab" +0xd0d2: "tabs" +0xd0d3: "tas" +0xd0d4: "tass" +0xd0d5: "tang" +0xd0d6: "taj" +0xd0d7: "tac" +0xd0d8: "tak" +0xd0d9: "tat" +0xd0da: "tap" +0xd0db: "tah" +0xd0dc: "tae" +0xd0dd: "taeg" +0xd0de: "taegg" +0xd0df: "taegs" +0xd0e0: "taen" +0xd0e1: "taenj" +0xd0e2: "taenh" +0xd0e3: "taed" +0xd0e4: "tael" +0xd0e5: "taelg" +0xd0e6: "taelm" +0xd0e7: "taelb" +0xd0e8: "taels" +0xd0e9: "taelt" +0xd0ea: "taelp" +0xd0eb: "taelh" +0xd0ec: "taem" +0xd0ed: "taeb" +0xd0ee: "taebs" +0xd0ef: "taes" +0xd0f0: "taess" +0xd0f1: "taeng" +0xd0f2: "taej" +0xd0f3: "taec" +0xd0f4: "taek" +0xd0f5: "taet" +0xd0f6: "taep" +0xd0f7: "taeh" +0xd0f8: "tya" +0xd0f9: "tyag" +0xd0fa: "tyagg" +0xd0fb: "tyags" +0xd0fc: "tyan" +0xd0fd: "tyanj" +0xd0fe: "tyanh" +0xd0ff: "tyad" +/* x0d1 */ +0xd100: "tyal" +0xd101: "tyalg" +0xd102: "tyalm" +0xd103: "tyalb" +0xd104: "tyals" +0xd105: "tyalt" +0xd106: "tyalp" +0xd107: "tyalh" +0xd108: "tyam" +0xd109: "tyab" +0xd10a: "tyabs" +0xd10b: "tyas" +0xd10c: "tyass" +0xd10d: "tyang" +0xd10e: "tyaj" +0xd10f: "tyac" +0xd110: "tyak" +0xd111: "tyat" +0xd112: "tyap" +0xd113: "tyah" +0xd114: "tyae" +0xd115: "tyaeg" +0xd116: "tyaegg" +0xd117: "tyaegs" +0xd118: "tyaen" +0xd119: "tyaenj" +0xd11a: "tyaenh" +0xd11b: "tyaed" +0xd11c: "tyael" +0xd11d: "tyaelg" +0xd11e: "tyaelm" +0xd11f: "tyaelb" +0xd120: "tyaels" +0xd121: "tyaelt" +0xd122: "tyaelp" +0xd123: "tyaelh" +0xd124: "tyaem" +0xd125: "tyaeb" +0xd126: "tyaebs" +0xd127: "tyaes" +0xd128: "tyaess" +0xd129: "tyaeng" +0xd12a: "tyaej" +0xd12b: "tyaec" +0xd12c: "tyaek" +0xd12d: "tyaet" +0xd12e: "tyaep" +0xd12f: "tyaeh" +0xd130: "teo" +0xd131: "teog" +0xd132: "teogg" +0xd133: "teogs" +0xd134: "teon" +0xd135: "teonj" +0xd136: "teonh" +0xd137: "teod" +0xd138: "teol" +0xd139: "teolg" +0xd13a: "teolm" +0xd13b: "teolb" +0xd13c: "teols" +0xd13d: "teolt" +0xd13e: "teolp" +0xd13f: "teolh" +0xd140: "teom" +0xd141: "teob" +0xd142: "teobs" +0xd143: "teos" +0xd144: "teoss" +0xd145: "teong" +0xd146: "teoj" +0xd147: "teoc" +0xd148: "teok" +0xd149: "teot" +0xd14a: "teop" +0xd14b: "teoh" +0xd14c: "te" +0xd14d: "teg" +0xd14e: "tegg" +0xd14f: "tegs" +0xd150: "ten" +0xd151: "tenj" +0xd152: "tenh" +0xd153: "ted" +0xd154: "tel" +0xd155: "telg" +0xd156: "telm" +0xd157: "telb" +0xd158: "tels" +0xd159: "telt" +0xd15a: "telp" +0xd15b: "telh" +0xd15c: "tem" +0xd15d: "teb" +0xd15e: "tebs" +0xd15f: "tes" +0xd160: "tess" +0xd161: "teng" +0xd162: "tej" +0xd163: "tec" +0xd164: "tek" +0xd165: "tet" +0xd166: "tep" +0xd167: "teh" +0xd168: "tyeo" +0xd169: "tyeog" +0xd16a: "tyeogg" +0xd16b: "tyeogs" +0xd16c: "tyeon" +0xd16d: "tyeonj" +0xd16e: "tyeonh" +0xd16f: "tyeod" +0xd170: "tyeol" +0xd171: "tyeolg" +0xd172: "tyeolm" +0xd173: "tyeolb" +0xd174: "tyeols" +0xd175: "tyeolt" +0xd176: "tyeolp" +0xd177: "tyeolh" +0xd178: "tyeom" +0xd179: "tyeob" +0xd17a: "tyeobs" +0xd17b: "tyeos" +0xd17c: "tyeoss" +0xd17d: "tyeong" +0xd17e: "tyeoj" +0xd17f: "tyeoc" +0xd180: "tyeok" +0xd181: "tyeot" +0xd182: "tyeop" +0xd183: "tyeoh" +0xd184: "tye" +0xd185: "tyeg" +0xd186: "tyegg" +0xd187: "tyegs" +0xd188: "tyen" +0xd189: "tyenj" +0xd18a: "tyenh" +0xd18b: "tyed" +0xd18c: "tyel" +0xd18d: "tyelg" +0xd18e: "tyelm" +0xd18f: "tyelb" +0xd190: "tyels" +0xd191: "tyelt" +0xd192: "tyelp" +0xd193: "tyelh" +0xd194: "tyem" +0xd195: "tyeb" +0xd196: "tyebs" +0xd197: "tyes" +0xd198: "tyess" +0xd199: "tyeng" +0xd19a: "tyej" +0xd19b: "tyec" +0xd19c: "tyek" +0xd19d: "tyet" +0xd19e: "tyep" +0xd19f: "tyeh" +0xd1a0: "to" +0xd1a1: "tog" +0xd1a2: "togg" +0xd1a3: "togs" +0xd1a4: "ton" +0xd1a5: "tonj" +0xd1a6: "tonh" +0xd1a7: "tod" +0xd1a8: "tol" +0xd1a9: "tolg" +0xd1aa: "tolm" +0xd1ab: "tolb" +0xd1ac: "tols" +0xd1ad: "tolt" +0xd1ae: "tolp" +0xd1af: "tolh" +0xd1b0: "tom" +0xd1b1: "tob" +0xd1b2: "tobs" +0xd1b3: "tos" +0xd1b4: "toss" +0xd1b5: "tong" +0xd1b6: "toj" +0xd1b7: "toc" +0xd1b8: "tok" +0xd1b9: "tot" +0xd1ba: "top" +0xd1bb: "toh" +0xd1bc: "twa" +0xd1bd: "twag" +0xd1be: "twagg" +0xd1bf: "twags" +0xd1c0: "twan" +0xd1c1: "twanj" +0xd1c2: "twanh" +0xd1c3: "twad" +0xd1c4: "twal" +0xd1c5: "twalg" +0xd1c6: "twalm" +0xd1c7: "twalb" +0xd1c8: "twals" +0xd1c9: "twalt" +0xd1ca: "twalp" +0xd1cb: "twalh" +0xd1cc: "twam" +0xd1cd: "twab" +0xd1ce: "twabs" +0xd1cf: "twas" +0xd1d0: "twass" +0xd1d1: "twang" +0xd1d2: "twaj" +0xd1d3: "twac" +0xd1d4: "twak" +0xd1d5: "twat" +0xd1d6: "twap" +0xd1d7: "twah" +0xd1d8: "twae" +0xd1d9: "twaeg" +0xd1da: "twaegg" +0xd1db: "twaegs" +0xd1dc: "twaen" +0xd1dd: "twaenj" +0xd1de: "twaenh" +0xd1df: "twaed" +0xd1e0: "twael" +0xd1e1: "twaelg" +0xd1e2: "twaelm" +0xd1e3: "twaelb" +0xd1e4: "twaels" +0xd1e5: "twaelt" +0xd1e6: "twaelp" +0xd1e7: "twaelh" +0xd1e8: "twaem" +0xd1e9: "twaeb" +0xd1ea: "twaebs" +0xd1eb: "twaes" +0xd1ec: "twaess" +0xd1ed: "twaeng" +0xd1ee: "twaej" +0xd1ef: "twaec" +0xd1f0: "twaek" +0xd1f1: "twaet" +0xd1f2: "twaep" +0xd1f3: "twaeh" +0xd1f4: "toe" +0xd1f5: "toeg" +0xd1f6: "toegg" +0xd1f7: "toegs" +0xd1f8: "toen" +0xd1f9: "toenj" +0xd1fa: "toenh" +0xd1fb: "toed" +0xd1fc: "toel" +0xd1fd: "toelg" +0xd1fe: "toelm" +0xd1ff: "toelb" +/* x0d2 */ +0xd200: "toels" +0xd201: "toelt" +0xd202: "toelp" +0xd203: "toelh" +0xd204: "toem" +0xd205: "toeb" +0xd206: "toebs" +0xd207: "toes" +0xd208: "toess" +0xd209: "toeng" +0xd20a: "toej" +0xd20b: "toec" +0xd20c: "toek" +0xd20d: "toet" +0xd20e: "toep" +0xd20f: "toeh" +0xd210: "tyo" +0xd211: "tyog" +0xd212: "tyogg" +0xd213: "tyogs" +0xd214: "tyon" +0xd215: "tyonj" +0xd216: "tyonh" +0xd217: "tyod" +0xd218: "tyol" +0xd219: "tyolg" +0xd21a: "tyolm" +0xd21b: "tyolb" +0xd21c: "tyols" +0xd21d: "tyolt" +0xd21e: "tyolp" +0xd21f: "tyolh" +0xd220: "tyom" +0xd221: "tyob" +0xd222: "tyobs" +0xd223: "tyos" +0xd224: "tyoss" +0xd225: "tyong" +0xd226: "tyoj" +0xd227: "tyoc" +0xd228: "tyok" +0xd229: "tyot" +0xd22a: "tyop" +0xd22b: "tyoh" +0xd22c: "tu" +0xd22d: "tug" +0xd22e: "tugg" +0xd22f: "tugs" +0xd230: "tun" +0xd231: "tunj" +0xd232: "tunh" +0xd233: "tud" +0xd234: "tul" +0xd235: "tulg" +0xd236: "tulm" +0xd237: "tulb" +0xd238: "tuls" +0xd239: "tult" +0xd23a: "tulp" +0xd23b: "tulh" +0xd23c: "tum" +0xd23d: "tub" +0xd23e: "tubs" +0xd23f: "tus" +0xd240: "tuss" +0xd241: "tung" +0xd242: "tuj" +0xd243: "tuc" +0xd244: "tuk" +0xd245: "tut" +0xd246: "tup" +0xd247: "tuh" +0xd248: "tweo" +0xd249: "tweog" +0xd24a: "tweogg" +0xd24b: "tweogs" +0xd24c: "tweon" +0xd24d: "tweonj" +0xd24e: "tweonh" +0xd24f: "tweod" +0xd250: "tweol" +0xd251: "tweolg" +0xd252: "tweolm" +0xd253: "tweolb" +0xd254: "tweols" +0xd255: "tweolt" +0xd256: "tweolp" +0xd257: "tweolh" +0xd258: "tweom" +0xd259: "tweob" +0xd25a: "tweobs" +0xd25b: "tweos" +0xd25c: "tweoss" +0xd25d: "tweong" +0xd25e: "tweoj" +0xd25f: "tweoc" +0xd260: "tweok" +0xd261: "tweot" +0xd262: "tweop" +0xd263: "tweoh" +0xd264: "twe" +0xd265: "tweg" +0xd266: "twegg" +0xd267: "twegs" +0xd268: "twen" +0xd269: "twenj" +0xd26a: "twenh" +0xd26b: "twed" +0xd26c: "twel" +0xd26d: "twelg" +0xd26e: "twelm" +0xd26f: "twelb" +0xd270: "twels" +0xd271: "twelt" +0xd272: "twelp" +0xd273: "twelh" +0xd274: "twem" +0xd275: "tweb" +0xd276: "twebs" +0xd277: "twes" +0xd278: "twess" +0xd279: "tweng" +0xd27a: "twej" +0xd27b: "twec" +0xd27c: "twek" +0xd27d: "twet" +0xd27e: "twep" +0xd27f: "tweh" +0xd280: "twi" +0xd281: "twig" +0xd282: "twigg" +0xd283: "twigs" +0xd284: "twin" +0xd285: "twinj" +0xd286: "twinh" +0xd287: "twid" +0xd288: "twil" +0xd289: "twilg" +0xd28a: "twilm" +0xd28b: "twilb" +0xd28c: "twils" +0xd28d: "twilt" +0xd28e: "twilp" +0xd28f: "twilh" +0xd290: "twim" +0xd291: "twib" +0xd292: "twibs" +0xd293: "twis" +0xd294: "twiss" +0xd295: "twing" +0xd296: "twij" +0xd297: "twic" +0xd298: "twik" +0xd299: "twit" +0xd29a: "twip" +0xd29b: "twih" +0xd29c: "tyu" +0xd29d: "tyug" +0xd29e: "tyugg" +0xd29f: "tyugs" +0xd2a0: "tyun" +0xd2a1: "tyunj" +0xd2a2: "tyunh" +0xd2a3: "tyud" +0xd2a4: "tyul" +0xd2a5: "tyulg" +0xd2a6: "tyulm" +0xd2a7: "tyulb" +0xd2a8: "tyuls" +0xd2a9: "tyult" +0xd2aa: "tyulp" +0xd2ab: "tyulh" +0xd2ac: "tyum" +0xd2ad: "tyub" +0xd2ae: "tyubs" +0xd2af: "tyus" +0xd2b0: "tyuss" +0xd2b1: "tyung" +0xd2b2: "tyuj" +0xd2b3: "tyuc" +0xd2b4: "tyuk" +0xd2b5: "tyut" +0xd2b6: "tyup" +0xd2b7: "tyuh" +0xd2b8: "teu" +0xd2b9: "teug" +0xd2ba: "teugg" +0xd2bb: "teugs" +0xd2bc: "teun" +0xd2bd: "teunj" +0xd2be: "teunh" +0xd2bf: "teud" +0xd2c0: "teul" +0xd2c1: "teulg" +0xd2c2: "teulm" +0xd2c3: "teulb" +0xd2c4: "teuls" +0xd2c5: "teult" +0xd2c6: "teulp" +0xd2c7: "teulh" +0xd2c8: "teum" +0xd2c9: "teub" +0xd2ca: "teubs" +0xd2cb: "teus" +0xd2cc: "teuss" +0xd2cd: "teung" +0xd2ce: "teuj" +0xd2cf: "teuc" +0xd2d0: "teuk" +0xd2d1: "teut" +0xd2d2: "teup" +0xd2d3: "teuh" +0xd2d4: "tyi" +0xd2d5: "tyig" +0xd2d6: "tyigg" +0xd2d7: "tyigs" +0xd2d8: "tyin" +0xd2d9: "tyinj" +0xd2da: "tyinh" +0xd2db: "tyid" +0xd2dc: "tyil" +0xd2dd: "tyilg" +0xd2de: "tyilm" +0xd2df: "tyilb" +0xd2e0: "tyils" +0xd2e1: "tyilt" +0xd2e2: "tyilp" +0xd2e3: "tyilh" +0xd2e4: "tyim" +0xd2e5: "tyib" +0xd2e6: "tyibs" +0xd2e7: "tyis" +0xd2e8: "tyiss" +0xd2e9: "tying" +0xd2ea: "tyij" +0xd2eb: "tyic" +0xd2ec: "tyik" +0xd2ed: "tyit" +0xd2ee: "tyip" +0xd2ef: "tyih" +0xd2f0: "ti" +0xd2f1: "tig" +0xd2f2: "tigg" +0xd2f3: "tigs" +0xd2f4: "tin" +0xd2f5: "tinj" +0xd2f6: "tinh" +0xd2f7: "tid" +0xd2f8: "til" +0xd2f9: "tilg" +0xd2fa: "tilm" +0xd2fb: "tilb" +0xd2fc: "tils" +0xd2fd: "tilt" +0xd2fe: "tilp" +0xd2ff: "tilh" +/* x0d3 */ +0xd300: "tim" +0xd301: "tib" +0xd302: "tibs" +0xd303: "tis" +0xd304: "tiss" +0xd305: "ting" +0xd306: "tij" +0xd307: "tic" +0xd308: "tik" +0xd309: "tit" +0xd30a: "tip" +0xd30b: "tih" +0xd30c: "pa" +0xd30d: "pag" +0xd30e: "pagg" +0xd30f: "pags" +0xd310: "pan" +0xd311: "panj" +0xd312: "panh" +0xd313: "pad" +0xd314: "pal" +0xd315: "palg" +0xd316: "palm" +0xd317: "palb" +0xd318: "pals" +0xd319: "palt" +0xd31a: "palp" +0xd31b: "palh" +0xd31c: "pam" +0xd31d: "pab" +0xd31e: "pabs" +0xd31f: "pas" +0xd320: "pass" +0xd321: "pang" +0xd322: "paj" +0xd323: "pac" +0xd324: "pak" +0xd325: "pat" +0xd326: "pap" +0xd327: "pah" +0xd328: "pae" +0xd329: "paeg" +0xd32a: "paegg" +0xd32b: "paegs" +0xd32c: "paen" +0xd32d: "paenj" +0xd32e: "paenh" +0xd32f: "paed" +0xd330: "pael" +0xd331: "paelg" +0xd332: "paelm" +0xd333: "paelb" +0xd334: "paels" +0xd335: "paelt" +0xd336: "paelp" +0xd337: "paelh" +0xd338: "paem" +0xd339: "paeb" +0xd33a: "paebs" +0xd33b: "paes" +0xd33c: "paess" +0xd33d: "paeng" +0xd33e: "paej" +0xd33f: "paec" +0xd340: "paek" +0xd341: "paet" +0xd342: "paep" +0xd343: "paeh" +0xd344: "pya" +0xd345: "pyag" +0xd346: "pyagg" +0xd347: "pyags" +0xd348: "pyan" +0xd349: "pyanj" +0xd34a: "pyanh" +0xd34b: "pyad" +0xd34c: "pyal" +0xd34d: "pyalg" +0xd34e: "pyalm" +0xd34f: "pyalb" +0xd350: "pyals" +0xd351: "pyalt" +0xd352: "pyalp" +0xd353: "pyalh" +0xd354: "pyam" +0xd355: "pyab" +0xd356: "pyabs" +0xd357: "pyas" +0xd358: "pyass" +0xd359: "pyang" +0xd35a: "pyaj" +0xd35b: "pyac" +0xd35c: "pyak" +0xd35d: "pyat" +0xd35e: "pyap" +0xd35f: "pyah" +0xd360: "pyae" +0xd361: "pyaeg" +0xd362: "pyaegg" +0xd363: "pyaegs" +0xd364: "pyaen" +0xd365: "pyaenj" +0xd366: "pyaenh" +0xd367: "pyaed" +0xd368: "pyael" +0xd369: "pyaelg" +0xd36a: "pyaelm" +0xd36b: "pyaelb" +0xd36c: "pyaels" +0xd36d: "pyaelt" +0xd36e: "pyaelp" +0xd36f: "pyaelh" +0xd370: "pyaem" +0xd371: "pyaeb" +0xd372: "pyaebs" +0xd373: "pyaes" +0xd374: "pyaess" +0xd375: "pyaeng" +0xd376: "pyaej" +0xd377: "pyaec" +0xd378: "pyaek" +0xd379: "pyaet" +0xd37a: "pyaep" +0xd37b: "pyaeh" +0xd37c: "peo" +0xd37d: "peog" +0xd37e: "peogg" +0xd37f: "peogs" +0xd380: "peon" +0xd381: "peonj" +0xd382: "peonh" +0xd383: "peod" +0xd384: "peol" +0xd385: "peolg" +0xd386: "peolm" +0xd387: "peolb" +0xd388: "peols" +0xd389: "peolt" +0xd38a: "peolp" +0xd38b: "peolh" +0xd38c: "peom" +0xd38d: "peob" +0xd38e: "peobs" +0xd38f: "peos" +0xd390: "peoss" +0xd391: "peong" +0xd392: "peoj" +0xd393: "peoc" +0xd394: "peok" +0xd395: "peot" +0xd396: "peop" +0xd397: "peoh" +0xd398: "pe" +0xd399: "peg" +0xd39a: "pegg" +0xd39b: "pegs" +0xd39c: "pen" +0xd39d: "penj" +0xd39e: "penh" +0xd39f: "ped" +0xd3a0: "pel" +0xd3a1: "pelg" +0xd3a2: "pelm" +0xd3a3: "pelb" +0xd3a4: "pels" +0xd3a5: "pelt" +0xd3a6: "pelp" +0xd3a7: "pelh" +0xd3a8: "pem" +0xd3a9: "peb" +0xd3aa: "pebs" +0xd3ab: "pes" +0xd3ac: "pess" +0xd3ad: "peng" +0xd3ae: "pej" +0xd3af: "pec" +0xd3b0: "pek" +0xd3b1: "pet" +0xd3b2: "pep" +0xd3b3: "peh" +0xd3b4: "pyeo" +0xd3b5: "pyeog" +0xd3b6: "pyeogg" +0xd3b7: "pyeogs" +0xd3b8: "pyeon" +0xd3b9: "pyeonj" +0xd3ba: "pyeonh" +0xd3bb: "pyeod" +0xd3bc: "pyeol" +0xd3bd: "pyeolg" +0xd3be: "pyeolm" +0xd3bf: "pyeolb" +0xd3c0: "pyeols" +0xd3c1: "pyeolt" +0xd3c2: "pyeolp" +0xd3c3: "pyeolh" +0xd3c4: "pyeom" +0xd3c5: "pyeob" +0xd3c6: "pyeobs" +0xd3c7: "pyeos" +0xd3c8: "pyeoss" +0xd3c9: "pyeong" +0xd3ca: "pyeoj" +0xd3cb: "pyeoc" +0xd3cc: "pyeok" +0xd3cd: "pyeot" +0xd3ce: "pyeop" +0xd3cf: "pyeoh" +0xd3d0: "pye" +0xd3d1: "pyeg" +0xd3d2: "pyegg" +0xd3d3: "pyegs" +0xd3d4: "pyen" +0xd3d5: "pyenj" +0xd3d6: "pyenh" +0xd3d7: "pyed" +0xd3d8: "pyel" +0xd3d9: "pyelg" +0xd3da: "pyelm" +0xd3db: "pyelb" +0xd3dc: "pyels" +0xd3dd: "pyelt" +0xd3de: "pyelp" +0xd3df: "pyelh" +0xd3e0: "pyem" +0xd3e1: "pyeb" +0xd3e2: "pyebs" +0xd3e3: "pyes" +0xd3e4: "pyess" +0xd3e5: "pyeng" +0xd3e6: "pyej" +0xd3e7: "pyec" +0xd3e8: "pyek" +0xd3e9: "pyet" +0xd3ea: "pyep" +0xd3eb: "pyeh" +0xd3ec: "po" +0xd3ed: "pog" +0xd3ee: "pogg" +0xd3ef: "pogs" +0xd3f0: "pon" +0xd3f1: "ponj" +0xd3f2: "ponh" +0xd3f3: "pod" +0xd3f4: "pol" +0xd3f5: "polg" +0xd3f6: "polm" +0xd3f7: "polb" +0xd3f8: "pols" +0xd3f9: "polt" +0xd3fa: "polp" +0xd3fb: "polh" +0xd3fc: "pom" +0xd3fd: "pob" +0xd3fe: "pobs" +0xd3ff: "pos" +/* x0d4 */ +0xd400: "poss" +0xd401: "pong" +0xd402: "poj" +0xd403: "poc" +0xd404: "pok" +0xd405: "pot" +0xd406: "pop" +0xd407: "poh" +0xd408: "pwa" +0xd409: "pwag" +0xd40a: "pwagg" +0xd40b: "pwags" +0xd40c: "pwan" +0xd40d: "pwanj" +0xd40e: "pwanh" +0xd40f: "pwad" +0xd410: "pwal" +0xd411: "pwalg" +0xd412: "pwalm" +0xd413: "pwalb" +0xd414: "pwals" +0xd415: "pwalt" +0xd416: "pwalp" +0xd417: "pwalh" +0xd418: "pwam" +0xd419: "pwab" +0xd41a: "pwabs" +0xd41b: "pwas" +0xd41c: "pwass" +0xd41d: "pwang" +0xd41e: "pwaj" +0xd41f: "pwac" +0xd420: "pwak" +0xd421: "pwat" +0xd422: "pwap" +0xd423: "pwah" +0xd424: "pwae" +0xd425: "pwaeg" +0xd426: "pwaegg" +0xd427: "pwaegs" +0xd428: "pwaen" +0xd429: "pwaenj" +0xd42a: "pwaenh" +0xd42b: "pwaed" +0xd42c: "pwael" +0xd42d: "pwaelg" +0xd42e: "pwaelm" +0xd42f: "pwaelb" +0xd430: "pwaels" +0xd431: "pwaelt" +0xd432: "pwaelp" +0xd433: "pwaelh" +0xd434: "pwaem" +0xd435: "pwaeb" +0xd436: "pwaebs" +0xd437: "pwaes" +0xd438: "pwaess" +0xd439: "pwaeng" +0xd43a: "pwaej" +0xd43b: "pwaec" +0xd43c: "pwaek" +0xd43d: "pwaet" +0xd43e: "pwaep" +0xd43f: "pwaeh" +0xd440: "poe" +0xd441: "poeg" +0xd442: "poegg" +0xd443: "poegs" +0xd444: "poen" +0xd445: "poenj" +0xd446: "poenh" +0xd447: "poed" +0xd448: "poel" +0xd449: "poelg" +0xd44a: "poelm" +0xd44b: "poelb" +0xd44c: "poels" +0xd44d: "poelt" +0xd44e: "poelp" +0xd44f: "poelh" +0xd450: "poem" +0xd451: "poeb" +0xd452: "poebs" +0xd453: "poes" +0xd454: "poess" +0xd455: "poeng" +0xd456: "poej" +0xd457: "poec" +0xd458: "poek" +0xd459: "poet" +0xd45a: "poep" +0xd45b: "poeh" +0xd45c: "pyo" +0xd45d: "pyog" +0xd45e: "pyogg" +0xd45f: "pyogs" +0xd460: "pyon" +0xd461: "pyonj" +0xd462: "pyonh" +0xd463: "pyod" +0xd464: "pyol" +0xd465: "pyolg" +0xd466: "pyolm" +0xd467: "pyolb" +0xd468: "pyols" +0xd469: "pyolt" +0xd46a: "pyolp" +0xd46b: "pyolh" +0xd46c: "pyom" +0xd46d: "pyob" +0xd46e: "pyobs" +0xd46f: "pyos" +0xd470: "pyoss" +0xd471: "pyong" +0xd472: "pyoj" +0xd473: "pyoc" +0xd474: "pyok" +0xd475: "pyot" +0xd476: "pyop" +0xd477: "pyoh" +0xd478: "pu" +0xd479: "pug" +0xd47a: "pugg" +0xd47b: "pugs" +0xd47c: "pun" +0xd47d: "punj" +0xd47e: "punh" +0xd47f: "pud" +0xd480: "pul" +0xd481: "pulg" +0xd482: "pulm" +0xd483: "pulb" +0xd484: "puls" +0xd485: "pult" +0xd486: "pulp" +0xd487: "pulh" +0xd488: "pum" +0xd489: "pub" +0xd48a: "pubs" +0xd48b: "pus" +0xd48c: "puss" +0xd48d: "pung" +0xd48e: "puj" +0xd48f: "puc" +0xd490: "puk" +0xd491: "put" +0xd492: "pup" +0xd493: "puh" +0xd494: "pweo" +0xd495: "pweog" +0xd496: "pweogg" +0xd497: "pweogs" +0xd498: "pweon" +0xd499: "pweonj" +0xd49a: "pweonh" +0xd49b: "pweod" +0xd49c: "pweol" +0xd49d: "pweolg" +0xd49e: "pweolm" +0xd49f: "pweolb" +0xd4a0: "pweols" +0xd4a1: "pweolt" +0xd4a2: "pweolp" +0xd4a3: "pweolh" +0xd4a4: "pweom" +0xd4a5: "pweob" +0xd4a6: "pweobs" +0xd4a7: "pweos" +0xd4a8: "pweoss" +0xd4a9: "pweong" +0xd4aa: "pweoj" +0xd4ab: "pweoc" +0xd4ac: "pweok" +0xd4ad: "pweot" +0xd4ae: "pweop" +0xd4af: "pweoh" +0xd4b0: "pwe" +0xd4b1: "pweg" +0xd4b2: "pwegg" +0xd4b3: "pwegs" +0xd4b4: "pwen" +0xd4b5: "pwenj" +0xd4b6: "pwenh" +0xd4b7: "pwed" +0xd4b8: "pwel" +0xd4b9: "pwelg" +0xd4ba: "pwelm" +0xd4bb: "pwelb" +0xd4bc: "pwels" +0xd4bd: "pwelt" +0xd4be: "pwelp" +0xd4bf: "pwelh" +0xd4c0: "pwem" +0xd4c1: "pweb" +0xd4c2: "pwebs" +0xd4c3: "pwes" +0xd4c4: "pwess" +0xd4c5: "pweng" +0xd4c6: "pwej" +0xd4c7: "pwec" +0xd4c8: "pwek" +0xd4c9: "pwet" +0xd4ca: "pwep" +0xd4cb: "pweh" +0xd4cc: "pwi" +0xd4cd: "pwig" +0xd4ce: "pwigg" +0xd4cf: "pwigs" +0xd4d0: "pwin" +0xd4d1: "pwinj" +0xd4d2: "pwinh" +0xd4d3: "pwid" +0xd4d4: "pwil" +0xd4d5: "pwilg" +0xd4d6: "pwilm" +0xd4d7: "pwilb" +0xd4d8: "pwils" +0xd4d9: "pwilt" +0xd4da: "pwilp" +0xd4db: "pwilh" +0xd4dc: "pwim" +0xd4dd: "pwib" +0xd4de: "pwibs" +0xd4df: "pwis" +0xd4e0: "pwiss" +0xd4e1: "pwing" +0xd4e2: "pwij" +0xd4e3: "pwic" +0xd4e4: "pwik" +0xd4e5: "pwit" +0xd4e6: "pwip" +0xd4e7: "pwih" +0xd4e8: "pyu" +0xd4e9: "pyug" +0xd4ea: "pyugg" +0xd4eb: "pyugs" +0xd4ec: "pyun" +0xd4ed: "pyunj" +0xd4ee: "pyunh" +0xd4ef: "pyud" +0xd4f0: "pyul" +0xd4f1: "pyulg" +0xd4f2: "pyulm" +0xd4f3: "pyulb" +0xd4f4: "pyuls" +0xd4f5: "pyult" +0xd4f6: "pyulp" +0xd4f7: "pyulh" +0xd4f8: "pyum" +0xd4f9: "pyub" +0xd4fa: "pyubs" +0xd4fb: "pyus" +0xd4fc: "pyuss" +0xd4fd: "pyung" +0xd4fe: "pyuj" +0xd4ff: "pyuc" +/* x0d5 */ +0xd500: "pyuk" +0xd501: "pyut" +0xd502: "pyup" +0xd503: "pyuh" +0xd504: "peu" +0xd505: "peug" +0xd506: "peugg" +0xd507: "peugs" +0xd508: "peun" +0xd509: "peunj" +0xd50a: "peunh" +0xd50b: "peud" +0xd50c: "peul" +0xd50d: "peulg" +0xd50e: "peulm" +0xd50f: "peulb" +0xd510: "peuls" +0xd511: "peult" +0xd512: "peulp" +0xd513: "peulh" +0xd514: "peum" +0xd515: "peub" +0xd516: "peubs" +0xd517: "peus" +0xd518: "peuss" +0xd519: "peung" +0xd51a: "peuj" +0xd51b: "peuc" +0xd51c: "peuk" +0xd51d: "peut" +0xd51e: "peup" +0xd51f: "peuh" +0xd520: "pyi" +0xd521: "pyig" +0xd522: "pyigg" +0xd523: "pyigs" +0xd524: "pyin" +0xd525: "pyinj" +0xd526: "pyinh" +0xd527: "pyid" +0xd528: "pyil" +0xd529: "pyilg" +0xd52a: "pyilm" +0xd52b: "pyilb" +0xd52c: "pyils" +0xd52d: "pyilt" +0xd52e: "pyilp" +0xd52f: "pyilh" +0xd530: "pyim" +0xd531: "pyib" +0xd532: "pyibs" +0xd533: "pyis" +0xd534: "pyiss" +0xd535: "pying" +0xd536: "pyij" +0xd537: "pyic" +0xd538: "pyik" +0xd539: "pyit" +0xd53a: "pyip" +0xd53b: "pyih" +0xd53c: "pi" +0xd53d: "pig" +0xd53e: "pigg" +0xd53f: "pigs" +0xd540: "pin" +0xd541: "pinj" +0xd542: "pinh" +0xd543: "pid" +0xd544: "pil" +0xd545: "pilg" +0xd546: "pilm" +0xd547: "pilb" +0xd548: "pils" +0xd549: "pilt" +0xd54a: "pilp" +0xd54b: "pilh" +0xd54c: "pim" +0xd54d: "pib" +0xd54e: "pibs" +0xd54f: "pis" +0xd550: "piss" +0xd551: "ping" +0xd552: "pij" +0xd553: "pic" +0xd554: "pik" +0xd555: "pit" +0xd556: "pip" +0xd557: "pih" +0xd558: "ha" +0xd559: "hag" +0xd55a: "hagg" +0xd55b: "hags" +0xd55c: "han" +0xd55d: "hanj" +0xd55e: "hanh" +0xd55f: "had" +0xd560: "hal" +0xd561: "halg" +0xd562: "halm" +0xd563: "halb" +0xd564: "hals" +0xd565: "halt" +0xd566: "halp" +0xd567: "halh" +0xd568: "ham" +0xd569: "hab" +0xd56a: "habs" +0xd56b: "has" +0xd56c: "hass" +0xd56d: "hang" +0xd56e: "haj" +0xd56f: "hac" +0xd570: "hak" +0xd571: "hat" +0xd572: "hap" +0xd573: "hah" +0xd574: "hae" +0xd575: "haeg" +0xd576: "haegg" +0xd577: "haegs" +0xd578: "haen" +0xd579: "haenj" +0xd57a: "haenh" +0xd57b: "haed" +0xd57c: "hael" +0xd57d: "haelg" +0xd57e: "haelm" +0xd57f: "haelb" +0xd580: "haels" +0xd581: "haelt" +0xd582: "haelp" +0xd583: "haelh" +0xd584: "haem" +0xd585: "haeb" +0xd586: "haebs" +0xd587: "haes" +0xd588: "haess" +0xd589: "haeng" +0xd58a: "haej" +0xd58b: "haec" +0xd58c: "haek" +0xd58d: "haet" +0xd58e: "haep" +0xd58f: "haeh" +0xd590: "hya" +0xd591: "hyag" +0xd592: "hyagg" +0xd593: "hyags" +0xd594: "hyan" +0xd595: "hyanj" +0xd596: "hyanh" +0xd597: "hyad" +0xd598: "hyal" +0xd599: "hyalg" +0xd59a: "hyalm" +0xd59b: "hyalb" +0xd59c: "hyals" +0xd59d: "hyalt" +0xd59e: "hyalp" +0xd59f: "hyalh" +0xd5a0: "hyam" +0xd5a1: "hyab" +0xd5a2: "hyabs" +0xd5a3: "hyas" +0xd5a4: "hyass" +0xd5a5: "hyang" +0xd5a6: "hyaj" +0xd5a7: "hyac" +0xd5a8: "hyak" +0xd5a9: "hyat" +0xd5aa: "hyap" +0xd5ab: "hyah" +0xd5ac: "hyae" +0xd5ad: "hyaeg" +0xd5ae: "hyaegg" +0xd5af: "hyaegs" +0xd5b0: "hyaen" +0xd5b1: "hyaenj" +0xd5b2: "hyaenh" +0xd5b3: "hyaed" +0xd5b4: "hyael" +0xd5b5: "hyaelg" +0xd5b6: "hyaelm" +0xd5b7: "hyaelb" +0xd5b8: "hyaels" +0xd5b9: "hyaelt" +0xd5ba: "hyaelp" +0xd5bb: "hyaelh" +0xd5bc: "hyaem" +0xd5bd: "hyaeb" +0xd5be: "hyaebs" +0xd5bf: "hyaes" +0xd5c0: "hyaess" +0xd5c1: "hyaeng" +0xd5c2: "hyaej" +0xd5c3: "hyaec" +0xd5c4: "hyaek" +0xd5c5: "hyaet" +0xd5c6: "hyaep" +0xd5c7: "hyaeh" +0xd5c8: "heo" +0xd5c9: "heog" +0xd5ca: "heogg" +0xd5cb: "heogs" +0xd5cc: "heon" +0xd5cd: "heonj" +0xd5ce: "heonh" +0xd5cf: "heod" +0xd5d0: "heol" +0xd5d1: "heolg" +0xd5d2: "heolm" +0xd5d3: "heolb" +0xd5d4: "heols" +0xd5d5: "heolt" +0xd5d6: "heolp" +0xd5d7: "heolh" +0xd5d8: "heom" +0xd5d9: "heob" +0xd5da: "heobs" +0xd5db: "heos" +0xd5dc: "heoss" +0xd5dd: "heong" +0xd5de: "heoj" +0xd5df: "heoc" +0xd5e0: "heok" +0xd5e1: "heot" +0xd5e2: "heop" +0xd5e3: "heoh" +0xd5e4: "he" +0xd5e5: "heg" +0xd5e6: "hegg" +0xd5e7: "hegs" +0xd5e8: "hen" +0xd5e9: "henj" +0xd5ea: "henh" +0xd5eb: "hed" +0xd5ec: "hel" +0xd5ed: "helg" +0xd5ee: "helm" +0xd5ef: "helb" +0xd5f0: "hels" +0xd5f1: "helt" +0xd5f2: "help" +0xd5f3: "helh" +0xd5f4: "hem" +0xd5f5: "heb" +0xd5f6: "hebs" +0xd5f7: "hes" +0xd5f8: "hess" +0xd5f9: "heng" +0xd5fa: "hej" +0xd5fb: "hec" +0xd5fc: "hek" +0xd5fd: "het" +0xd5fe: "hep" +0xd5ff: "heh" +/* x0d6 */ +0xd600: "hyeo" +0xd601: "hyeog" +0xd602: "hyeogg" +0xd603: "hyeogs" +0xd604: "hyeon" +0xd605: "hyeonj" +0xd606: "hyeonh" +0xd607: "hyeod" +0xd608: "hyeol" +0xd609: "hyeolg" +0xd60a: "hyeolm" +0xd60b: "hyeolb" +0xd60c: "hyeols" +0xd60d: "hyeolt" +0xd60e: "hyeolp" +0xd60f: "hyeolh" +0xd610: "hyeom" +0xd611: "hyeob" +0xd612: "hyeobs" +0xd613: "hyeos" +0xd614: "hyeoss" +0xd615: "hyeong" +0xd616: "hyeoj" +0xd617: "hyeoc" +0xd618: "hyeok" +0xd619: "hyeot" +0xd61a: "hyeop" +0xd61b: "hyeoh" +0xd61c: "hye" +0xd61d: "hyeg" +0xd61e: "hyegg" +0xd61f: "hyegs" +0xd620: "hyen" +0xd621: "hyenj" +0xd622: "hyenh" +0xd623: "hyed" +0xd624: "hyel" +0xd625: "hyelg" +0xd626: "hyelm" +0xd627: "hyelb" +0xd628: "hyels" +0xd629: "hyelt" +0xd62a: "hyelp" +0xd62b: "hyelh" +0xd62c: "hyem" +0xd62d: "hyeb" +0xd62e: "hyebs" +0xd62f: "hyes" +0xd630: "hyess" +0xd631: "hyeng" +0xd632: "hyej" +0xd633: "hyec" +0xd634: "hyek" +0xd635: "hyet" +0xd636: "hyep" +0xd637: "hyeh" +0xd638: "ho" +0xd639: "hog" +0xd63a: "hogg" +0xd63b: "hogs" +0xd63c: "hon" +0xd63d: "honj" +0xd63e: "honh" +0xd63f: "hod" +0xd640: "hol" +0xd641: "holg" +0xd642: "holm" +0xd643: "holb" +0xd644: "hols" +0xd645: "holt" +0xd646: "holp" +0xd647: "holh" +0xd648: "hom" +0xd649: "hob" +0xd64a: "hobs" +0xd64b: "hos" +0xd64c: "hoss" +0xd64d: "hong" +0xd64e: "hoj" +0xd64f: "hoc" +0xd650: "hok" +0xd651: "hot" +0xd652: "hop" +0xd653: "hoh" +0xd654: "hwa" +0xd655: "hwag" +0xd656: "hwagg" +0xd657: "hwags" +0xd658: "hwan" +0xd659: "hwanj" +0xd65a: "hwanh" +0xd65b: "hwad" +0xd65c: "hwal" +0xd65d: "hwalg" +0xd65e: "hwalm" +0xd65f: "hwalb" +0xd660: "hwals" +0xd661: "hwalt" +0xd662: "hwalp" +0xd663: "hwalh" +0xd664: "hwam" +0xd665: "hwab" +0xd666: "hwabs" +0xd667: "hwas" +0xd668: "hwass" +0xd669: "hwang" +0xd66a: "hwaj" +0xd66b: "hwac" +0xd66c: "hwak" +0xd66d: "hwat" +0xd66e: "hwap" +0xd66f: "hwah" +0xd670: "hwae" +0xd671: "hwaeg" +0xd672: "hwaegg" +0xd673: "hwaegs" +0xd674: "hwaen" +0xd675: "hwaenj" +0xd676: "hwaenh" +0xd677: "hwaed" +0xd678: "hwael" +0xd679: "hwaelg" +0xd67a: "hwaelm" +0xd67b: "hwaelb" +0xd67c: "hwaels" +0xd67d: "hwaelt" +0xd67e: "hwaelp" +0xd67f: "hwaelh" +0xd680: "hwaem" +0xd681: "hwaeb" +0xd682: "hwaebs" +0xd683: "hwaes" +0xd684: "hwaess" +0xd685: "hwaeng" +0xd686: "hwaej" +0xd687: "hwaec" +0xd688: "hwaek" +0xd689: "hwaet" +0xd68a: "hwaep" +0xd68b: "hwaeh" +0xd68c: "hoe" +0xd68d: "hoeg" +0xd68e: "hoegg" +0xd68f: "hoegs" +0xd690: "hoen" +0xd691: "hoenj" +0xd692: "hoenh" +0xd693: "hoed" +0xd694: "hoel" +0xd695: "hoelg" +0xd696: "hoelm" +0xd697: "hoelb" +0xd698: "hoels" +0xd699: "hoelt" +0xd69a: "hoelp" +0xd69b: "hoelh" +0xd69c: "hoem" +0xd69d: "hoeb" +0xd69e: "hoebs" +0xd69f: "hoes" +0xd6a0: "hoess" +0xd6a1: "hoeng" +0xd6a2: "hoej" +0xd6a3: "hoec" +0xd6a4: "hoek" +0xd6a5: "hoet" +0xd6a6: "hoep" +0xd6a7: "hoeh" +0xd6a8: "hyo" +0xd6a9: "hyog" +0xd6aa: "hyogg" +0xd6ab: "hyogs" +0xd6ac: "hyon" +0xd6ad: "hyonj" +0xd6ae: "hyonh" +0xd6af: "hyod" +0xd6b0: "hyol" +0xd6b1: "hyolg" +0xd6b2: "hyolm" +0xd6b3: "hyolb" +0xd6b4: "hyols" +0xd6b5: "hyolt" +0xd6b6: "hyolp" +0xd6b7: "hyolh" +0xd6b8: "hyom" +0xd6b9: "hyob" +0xd6ba: "hyobs" +0xd6bb: "hyos" +0xd6bc: "hyoss" +0xd6bd: "hyong" +0xd6be: "hyoj" +0xd6bf: "hyoc" +0xd6c0: "hyok" +0xd6c1: "hyot" +0xd6c2: "hyop" +0xd6c3: "hyoh" +0xd6c4: "hu" +0xd6c5: "hug" +0xd6c6: "hugg" +0xd6c7: "hugs" +0xd6c8: "hun" +0xd6c9: "hunj" +0xd6ca: "hunh" +0xd6cb: "hud" +0xd6cc: "hul" +0xd6cd: "hulg" +0xd6ce: "hulm" +0xd6cf: "hulb" +0xd6d0: "huls" +0xd6d1: "hult" +0xd6d2: "hulp" +0xd6d3: "hulh" +0xd6d4: "hum" +0xd6d5: "hub" +0xd6d6: "hubs" +0xd6d7: "hus" +0xd6d8: "huss" +0xd6d9: "hung" +0xd6da: "huj" +0xd6db: "huc" +0xd6dc: "huk" +0xd6dd: "hut" +0xd6de: "hup" +0xd6df: "huh" +0xd6e0: "hweo" +0xd6e1: "hweog" +0xd6e2: "hweogg" +0xd6e3: "hweogs" +0xd6e4: "hweon" +0xd6e5: "hweonj" +0xd6e6: "hweonh" +0xd6e7: "hweod" +0xd6e8: "hweol" +0xd6e9: "hweolg" +0xd6ea: "hweolm" +0xd6eb: "hweolb" +0xd6ec: "hweols" +0xd6ed: "hweolt" +0xd6ee: "hweolp" +0xd6ef: "hweolh" +0xd6f0: "hweom" +0xd6f1: "hweob" +0xd6f2: "hweobs" +0xd6f3: "hweos" +0xd6f4: "hweoss" +0xd6f5: "hweong" +0xd6f6: "hweoj" +0xd6f7: "hweoc" +0xd6f8: "hweok" +0xd6f9: "hweot" +0xd6fa: "hweop" +0xd6fb: "hweoh" +0xd6fc: "hwe" +0xd6fd: "hweg" +0xd6fe: "hwegg" +0xd6ff: "hwegs" +/* x0d7 */ +0xd700: "hwen" +0xd701: "hwenj" +0xd702: "hwenh" +0xd703: "hwed" +0xd704: "hwel" +0xd705: "hwelg" +0xd706: "hwelm" +0xd707: "hwelb" +0xd708: "hwels" +0xd709: "hwelt" +0xd70a: "hwelp" +0xd70b: "hwelh" +0xd70c: "hwem" +0xd70d: "hweb" +0xd70e: "hwebs" +0xd70f: "hwes" +0xd710: "hwess" +0xd711: "hweng" +0xd712: "hwej" +0xd713: "hwec" +0xd714: "hwek" +0xd715: "hwet" +0xd716: "hwep" +0xd717: "hweh" +0xd718: "hwi" +0xd719: "hwig" +0xd71a: "hwigg" +0xd71b: "hwigs" +0xd71c: "hwin" +0xd71d: "hwinj" +0xd71e: "hwinh" +0xd71f: "hwid" +0xd720: "hwil" +0xd721: "hwilg" +0xd722: "hwilm" +0xd723: "hwilb" +0xd724: "hwils" +0xd725: "hwilt" +0xd726: "hwilp" +0xd727: "hwilh" +0xd728: "hwim" +0xd729: "hwib" +0xd72a: "hwibs" +0xd72b: "hwis" +0xd72c: "hwiss" +0xd72d: "hwing" +0xd72e: "hwij" +0xd72f: "hwic" +0xd730: "hwik" +0xd731: "hwit" +0xd732: "hwip" +0xd733: "hwih" +0xd734: "hyu" +0xd735: "hyug" +0xd736: "hyugg" +0xd737: "hyugs" +0xd738: "hyun" +0xd739: "hyunj" +0xd73a: "hyunh" +0xd73b: "hyud" +0xd73c: "hyul" +0xd73d: "hyulg" +0xd73e: "hyulm" +0xd73f: "hyulb" +0xd740: "hyuls" +0xd741: "hyult" +0xd742: "hyulp" +0xd743: "hyulh" +0xd744: "hyum" +0xd745: "hyub" +0xd746: "hyubs" +0xd747: "hyus" +0xd748: "hyuss" +0xd749: "hyung" +0xd74a: "hyuj" +0xd74b: "hyuc" +0xd74c: "hyuk" +0xd74d: "hyut" +0xd74e: "hyup" +0xd74f: "hyuh" +0xd750: "heu" +0xd751: "heug" +0xd752: "heugg" +0xd753: "heugs" +0xd754: "heun" +0xd755: "heunj" +0xd756: "heunh" +0xd757: "heud" +0xd758: "heul" +0xd759: "heulg" +0xd75a: "heulm" +0xd75b: "heulb" +0xd75c: "heuls" +0xd75d: "heult" +0xd75e: "heulp" +0xd75f: "heulh" +0xd760: "heum" +0xd761: "heub" +0xd762: "heubs" +0xd763: "heus" +0xd764: "heuss" +0xd765: "heung" +0xd766: "heuj" +0xd767: "heuc" +0xd768: "heuk" +0xd769: "heut" +0xd76a: "heup" +0xd76b: "heuh" +0xd76c: "hyi" +0xd76d: "hyig" +0xd76e: "hyigg" +0xd76f: "hyigs" +0xd770: "hyin" +0xd771: "hyinj" +0xd772: "hyinh" +0xd773: "hyid" +0xd774: "hyil" +0xd775: "hyilg" +0xd776: "hyilm" +0xd777: "hyilb" +0xd778: "hyils" +0xd779: "hyilt" +0xd77a: "hyilp" +0xd77b: "hyilh" +0xd77c: "hyim" +0xd77d: "hyib" +0xd77e: "hyibs" +0xd77f: "hyis" +0xd780: "hyiss" +0xd781: "hying" +0xd782: "hyij" +0xd783: "hyic" +0xd784: "hyik" +0xd785: "hyit" +0xd786: "hyip" +0xd787: "hyih" +0xd788: "hi" +0xd789: "hig" +0xd78a: "higg" +0xd78b: "higs" +0xd78c: "hin" +0xd78d: "hinj" +0xd78e: "hinh" +0xd78f: "hid" +0xd790: "hil" +0xd791: "hilg" +0xd792: "hilm" +0xd793: "hilb" +0xd794: "hils" +0xd795: "hilt" +0xd796: "hilp" +0xd797: "hilh" +0xd798: "him" +0xd799: "hib" +0xd79a: "hibs" +0xd79b: "his" +0xd79c: "hiss" +0xd79d: "hing" +0xd79e: "hij" +0xd79f: "hic" +0xd7a0: "hik" +0xd7a1: "hit" +0xd7a2: "hip" +0xd7a3: "hih" +0xd7a4: "[?]" +0xd7a5: "[?]" +0xd7a6: "[?]" +0xd7a7: "[?]" +0xd7a8: "[?]" +0xd7a9: "[?]" +0xd7aa: "[?]" +0xd7ab: "[?]" +0xd7ac: "[?]" +0xd7ad: "[?]" +0xd7ae: "[?]" +0xd7af: "[?]" +0xd7b0: "[?]" +0xd7b1: "[?]" +0xd7b2: "[?]" +0xd7b3: "[?]" +0xd7b4: "[?]" +0xd7b5: "[?]" +0xd7b6: "[?]" +0xd7b7: "[?]" +0xd7b8: "[?]" +0xd7b9: "[?]" +0xd7ba: "[?]" +0xd7bb: "[?]" +0xd7bc: "[?]" +0xd7bd: "[?]" +0xd7be: "[?]" +0xd7bf: "[?]" +0xd7c0: "[?]" +0xd7c1: "[?]" +0xd7c2: "[?]" +0xd7c3: "[?]" +0xd7c4: "[?]" +0xd7c5: "[?]" +0xd7c6: "[?]" +0xd7c7: "[?]" +0xd7c8: "[?]" +0xd7c9: "[?]" +0xd7ca: "[?]" +0xd7cb: "[?]" +0xd7cc: "[?]" +0xd7cd: "[?]" +0xd7ce: "[?]" +0xd7cf: "[?]" +0xd7d0: "[?]" +0xd7d1: "[?]" +0xd7d2: "[?]" +0xd7d3: "[?]" +0xd7d4: "[?]" +0xd7d5: "[?]" +0xd7d6: "[?]" +0xd7d7: "[?]" +0xd7d8: "[?]" +0xd7d9: "[?]" +0xd7da: "[?]" +0xd7db: "[?]" +0xd7dc: "[?]" +0xd7dd: "[?]" +0xd7de: "[?]" +0xd7df: "[?]" +0xd7e0: "[?]" +0xd7e1: "[?]" +0xd7e2: "[?]" +0xd7e3: "[?]" +0xd7e4: "[?]" +0xd7e5: "[?]" +0xd7e6: "[?]" +0xd7e7: "[?]" +0xd7e8: "[?]" +0xd7e9: "[?]" +0xd7ea: "[?]" +0xd7eb: "[?]" +0xd7ec: "[?]" +0xd7ed: "[?]" +0xd7ee: "[?]" +0xd7ef: "[?]" +0xd7f0: "[?]" +0xd7f1: "[?]" +0xd7f2: "[?]" +0xd7f3: "[?]" +0xd7f4: "[?]" +0xd7f5: "[?]" +0xd7f6: "[?]" +0xd7f7: "[?]" +0xd7f8: "[?]" +0xd7f9: "[?]" +0xd7fa: "[?]" +0xd7fb: "[?]" +0xd7fc: "[?]" +0xd7fd: "[?]" +0xd7fe: "[?]" +/* x0f9 */ +0xf900: "Kay " +0xf901: "Kayng " +0xf902: "Ke " +0xf903: "Ko " +0xf904: "Kol " +0xf905: "Koc " +0xf906: "Kwi " +0xf907: "Kwi " +0xf908: "Kyun " +0xf909: "Kul " +0xf90a: "Kum " +0xf90b: "Na " +0xf90c: "Na " +0xf90d: "Na " +0xf90e: "La " +0xf90f: "Na " +0xf910: "Na " +0xf911: "Na " +0xf912: "Na " +0xf913: "Na " +0xf914: "Nak " +0xf915: "Nak " +0xf916: "Nak " +0xf917: "Nak " +0xf918: "Nak " +0xf919: "Nak " +0xf91a: "Nak " +0xf91b: "Nan " +0xf91c: "Nan " +0xf91d: "Nan " +0xf91e: "Nan " +0xf91f: "Nan " +0xf920: "Nan " +0xf921: "Nam " +0xf922: "Nam " +0xf923: "Nam " +0xf924: "Nam " +0xf925: "Nap " +0xf926: "Nap " +0xf927: "Nap " +0xf928: "Nang " +0xf929: "Nang " +0xf92a: "Nang " +0xf92b: "Nang " +0xf92c: "Nang " +0xf92d: "Nay " +0xf92e: "Nayng " +0xf92f: "No " +0xf930: "No " +0xf931: "No " +0xf932: "No " +0xf933: "No " +0xf934: "No " +0xf935: "No " +0xf936: "No " +0xf937: "No " +0xf938: "No " +0xf939: "No " +0xf93a: "No " +0xf93b: "Nok " +0xf93c: "Nok " +0xf93d: "Nok " +0xf93e: "Nok " +0xf93f: "Nok " +0xf940: "Nok " +0xf941: "Non " +0xf942: "Nong " +0xf943: "Nong " +0xf944: "Nong " +0xf945: "Nong " +0xf946: "Noy " +0xf947: "Noy " +0xf948: "Noy " +0xf949: "Noy " +0xf94a: "Nwu " +0xf94b: "Nwu " +0xf94c: "Nwu " +0xf94d: "Nwu " +0xf94e: "Nwu " +0xf94f: "Nwu " +0xf950: "Nwu " +0xf951: "Nwu " +0xf952: "Nuk " +0xf953: "Nuk " +0xf954: "Num " +0xf955: "Nung " +0xf956: "Nung " +0xf957: "Nung " +0xf958: "Nung " +0xf959: "Nung " +0xf95a: "Twu " +0xf95b: "La " +0xf95c: "Lak " +0xf95d: "Lak " +0xf95e: "Lan " +0xf95f: "Lyeng " +0xf960: "Lo " +0xf961: "Lyul " +0xf962: "Li " +0xf963: "Pey " +0xf964: "Pen " +0xf965: "Pyen " +0xf966: "Pwu " +0xf967: "Pwul " +0xf968: "Pi " +0xf969: "Sak " +0xf96a: "Sak " +0xf96b: "Sam " +0xf96c: "Sayk " +0xf96d: "Sayng " +0xf96e: "Sep " +0xf96f: "Sey " +0xf970: "Sway " +0xf971: "Sin " +0xf972: "Sim " +0xf973: "Sip " +0xf974: "Ya " +0xf975: "Yak " +0xf976: "Yak " +0xf977: "Yang " +0xf978: "Yang " +0xf979: "Yang " +0xf97a: "Yang " +0xf97b: "Yang " +0xf97c: "Yang " +0xf97d: "Yang " +0xf97e: "Yang " +0xf97f: "Ye " +0xf980: "Ye " +0xf981: "Ye " +0xf982: "Ye " +0xf983: "Ye " +0xf984: "Ye " +0xf985: "Ye " +0xf986: "Ye " +0xf987: "Ye " +0xf988: "Ye " +0xf989: "Ye " +0xf98a: "Yek " +0xf98b: "Yek " +0xf98c: "Yek " +0xf98d: "Yek " +0xf98e: "Yen " +0xf98f: "Yen " +0xf990: "Yen " +0xf991: "Yen " +0xf992: "Yen " +0xf993: "Yen " +0xf994: "Yen " +0xf995: "Yen " +0xf996: "Yen " +0xf997: "Yen " +0xf998: "Yen " +0xf999: "Yen " +0xf99a: "Yen " +0xf99b: "Yen " +0xf99c: "Yel " +0xf99d: "Yel " +0xf99e: "Yel " +0xf99f: "Yel " +0xf9a0: "Yel " +0xf9a1: "Yel " +0xf9a2: "Yem " +0xf9a3: "Yem " +0xf9a4: "Yem " +0xf9a5: "Yem " +0xf9a6: "Yem " +0xf9a7: "Yep " +0xf9a8: "Yeng " +0xf9a9: "Yeng " +0xf9aa: "Yeng " +0xf9ab: "Yeng " +0xf9ac: "Yeng " +0xf9ad: "Yeng " +0xf9ae: "Yeng " +0xf9af: "Yeng " +0xf9b0: "Yeng " +0xf9b1: "Yeng " +0xf9b2: "Yeng " +0xf9b3: "Yeng " +0xf9b4: "Yeng " +0xf9b5: "Yey " +0xf9b6: "Yey " +0xf9b7: "Yey " +0xf9b8: "Yey " +0xf9b9: "O " +0xf9ba: "Yo " +0xf9bb: "Yo " +0xf9bc: "Yo " +0xf9bd: "Yo " +0xf9be: "Yo " +0xf9bf: "Yo " +0xf9c0: "Yo " +0xf9c1: "Yo " +0xf9c2: "Yo " +0xf9c3: "Yo " +0xf9c4: "Yong " +0xf9c5: "Wun " +0xf9c6: "Wen " +0xf9c7: "Yu " +0xf9c8: "Yu " +0xf9c9: "Yu " +0xf9ca: "Yu " +0xf9cb: "Yu " +0xf9cc: "Yu " +0xf9cd: "Yu " +0xf9ce: "Yu " +0xf9cf: "Yu " +0xf9d0: "Yu " +0xf9d1: "Yuk " +0xf9d2: "Yuk " +0xf9d3: "Yuk " +0xf9d4: "Yun " +0xf9d5: "Yun " +0xf9d6: "Yun " +0xf9d7: "Yun " +0xf9d8: "Yul " +0xf9d9: "Yul " +0xf9da: "Yul " +0xf9db: "Yul " +0xf9dc: "Yung " +0xf9dd: "I " +0xf9de: "I " +0xf9df: "I " +0xf9e0: "I " +0xf9e1: "I " +0xf9e2: "I " +0xf9e3: "I " +0xf9e4: "I " +0xf9e5: "I " +0xf9e6: "I " +0xf9e7: "I " +0xf9e8: "I " +0xf9e9: "I " +0xf9ea: "I " +0xf9eb: "Ik " +0xf9ec: "Ik " +0xf9ed: "In " +0xf9ee: "In " +0xf9ef: "In " +0xf9f0: "In " +0xf9f1: "In " +0xf9f2: "In " +0xf9f3: "In " +0xf9f4: "Im " +0xf9f5: "Im " +0xf9f6: "Im " +0xf9f7: "Ip " +0xf9f8: "Ip " +0xf9f9: "Ip " +0xf9fa: "Cang " +0xf9fb: "Cek " +0xf9fc: "Ci " +0xf9fd: "Cip " +0xf9fe: "Cha " +0xf9ff: "Chek " +/* x0fa */ +0xfa00: "Chey " +0xfa01: "Thak " +0xfa02: "Thak " +0xfa03: "Thang " +0xfa04: "Thayk " +0xfa05: "Thong " +0xfa06: "Pho " +0xfa07: "Phok " +0xfa08: "Hang " +0xfa09: "Hang " +0xfa0a: "Hyen " +0xfa0b: "Hwak " +0xfa0c: "Wu " +0xfa0d: "Huo " +0xfa0e: "[?] " +0xfa0f: "[?] " +0xfa10: "Zhong " +0xfa11: "[?] " +0xfa12: "Qing " +0xfa13: "[?] " +0xfa14: "[?] " +0xfa15: "Xi " +0xfa16: "Zhu " +0xfa17: "Yi " +0xfa18: "Li " +0xfa19: "Shen " +0xfa1a: "Xiang " +0xfa1b: "Fu " +0xfa1c: "Jing " +0xfa1d: "Jing " +0xfa1e: "Yu " +0xfa1f: "[?] " +0xfa20: "Hagi " +0xfa21: "[?] " +0xfa22: "Zhu " +0xfa23: "[?] " +0xfa24: "[?] " +0xfa25: "Yi " +0xfa26: "Du " +0xfa27: "[?] " +0xfa28: "[?] " +0xfa29: "[?] " +0xfa2a: "Fan " +0xfa2b: "Si " +0xfa2c: "Guan " +0xfa2d: "[?]" +0xfa2e: "[?]" +0xfa2f: "[?]" +0xfa30: "[?]" +0xfa31: "[?]" +0xfa32: "[?]" +0xfa33: "[?]" +0xfa34: "[?]" +0xfa35: "[?]" +0xfa36: "[?]" +0xfa37: "[?]" +0xfa38: "[?]" +0xfa39: "[?]" +0xfa3a: "[?]" +0xfa3b: "[?]" +0xfa3c: "[?]" +0xfa3d: "[?]" +0xfa3e: "[?]" +0xfa3f: "[?]" +0xfa40: "[?]" +0xfa41: "[?]" +0xfa42: "[?]" +0xfa43: "[?]" +0xfa44: "[?]" +0xfa45: "[?]" +0xfa46: "[?]" +0xfa47: "[?]" +0xfa48: "[?]" +0xfa49: "[?]" +0xfa4a: "[?]" +0xfa4b: "[?]" +0xfa4c: "[?]" +0xfa4d: "[?]" +0xfa4e: "[?]" +0xfa4f: "[?]" +0xfa50: "[?]" +0xfa51: "[?]" +0xfa52: "[?]" +0xfa53: "[?]" +0xfa54: "[?]" +0xfa55: "[?]" +0xfa56: "[?]" +0xfa57: "[?]" +0xfa58: "[?]" +0xfa59: "[?]" +0xfa5a: "[?]" +0xfa5b: "[?]" +0xfa5c: "[?]" +0xfa5d: "[?]" +0xfa5e: "[?]" +0xfa5f: "[?]" +0xfa60: "[?]" +0xfa61: "[?]" +0xfa62: "[?]" +0xfa63: "[?]" +0xfa64: "[?]" +0xfa65: "[?]" +0xfa66: "[?]" +0xfa67: "[?]" +0xfa68: "[?]" +0xfa69: "[?]" +0xfa6a: "[?]" +0xfa6b: "[?]" +0xfa6c: "[?]" +0xfa6d: "[?]" +0xfa6e: "[?]" +0xfa6f: "[?]" +0xfa70: "[?]" +0xfa71: "[?]" +0xfa72: "[?]" +0xfa73: "[?]" +0xfa74: "[?]" +0xfa75: "[?]" +0xfa76: "[?]" +0xfa77: "[?]" +0xfa78: "[?]" +0xfa79: "[?]" +0xfa7a: "[?]" +0xfa7b: "[?]" +0xfa7c: "[?]" +0xfa7d: "[?]" +0xfa7e: "[?]" +0xfa7f: "[?]" +0xfa80: "[?]" +0xfa81: "[?]" +0xfa82: "[?]" +0xfa83: "[?]" +0xfa84: "[?]" +0xfa85: "[?]" +0xfa86: "[?]" +0xfa87: "[?]" +0xfa88: "[?]" +0xfa89: "[?]" +0xfa8a: "[?]" +0xfa8b: "[?]" +0xfa8c: "[?]" +0xfa8d: "[?]" +0xfa8e: "[?]" +0xfa8f: "[?]" +0xfa90: "[?]" +0xfa91: "[?]" +0xfa92: "[?]" +0xfa93: "[?]" +0xfa94: "[?]" +0xfa95: "[?]" +0xfa96: "[?]" +0xfa97: "[?]" +0xfa98: "[?]" +0xfa99: "[?]" +0xfa9a: "[?]" +0xfa9b: "[?]" +0xfa9c: "[?]" +0xfa9d: "[?]" +0xfa9e: "[?]" +0xfa9f: "[?]" +0xfaa0: "[?]" +0xfaa1: "[?]" +0xfaa2: "[?]" +0xfaa3: "[?]" +0xfaa4: "[?]" +0xfaa5: "[?]" +0xfaa6: "[?]" +0xfaa7: "[?]" +0xfaa8: "[?]" +0xfaa9: "[?]" +0xfaaa: "[?]" +0xfaab: "[?]" +0xfaac: "[?]" +0xfaad: "[?]" +0xfaae: "[?]" +0xfaaf: "[?]" +0xfab0: "[?]" +0xfab1: "[?]" +0xfab2: "[?]" +0xfab3: "[?]" +0xfab4: "[?]" +0xfab5: "[?]" +0xfab6: "[?]" +0xfab7: "[?]" +0xfab8: "[?]" +0xfab9: "[?]" +0xfaba: "[?]" +0xfabb: "[?]" +0xfabc: "[?]" +0xfabd: "[?]" +0xfabe: "[?]" +0xfabf: "[?]" +0xfac0: "[?]" +0xfac1: "[?]" +0xfac2: "[?]" +0xfac3: "[?]" +0xfac4: "[?]" +0xfac5: "[?]" +0xfac6: "[?]" +0xfac7: "[?]" +0xfac8: "[?]" +0xfac9: "[?]" +0xfaca: "[?]" +0xfacb: "[?]" +0xfacc: "[?]" +0xfacd: "[?]" +0xface: "[?]" +0xfacf: "[?]" +0xfad0: "[?]" +0xfad1: "[?]" +0xfad2: "[?]" +0xfad3: "[?]" +0xfad4: "[?]" +0xfad5: "[?]" +0xfad6: "[?]" +0xfad7: "[?]" +0xfad8: "[?]" +0xfad9: "[?]" +0xfada: "[?]" +0xfadb: "[?]" +0xfadc: "[?]" +0xfadd: "[?]" +0xfade: "[?]" +0xfadf: "[?]" +0xfae0: "[?]" +0xfae1: "[?]" +0xfae2: "[?]" +0xfae3: "[?]" +0xfae4: "[?]" +0xfae5: "[?]" +0xfae6: "[?]" +0xfae7: "[?]" +0xfae8: "[?]" +0xfae9: "[?]" +0xfaea: "[?]" +0xfaeb: "[?]" +0xfaec: "[?]" +0xfaed: "[?]" +0xfaee: "[?]" +0xfaef: "[?]" +0xfaf0: "[?]" +0xfaf1: "[?]" +0xfaf2: "[?]" +0xfaf3: "[?]" +0xfaf4: "[?]" +0xfaf5: "[?]" +0xfaf6: "[?]" +0xfaf7: "[?]" +0xfaf8: "[?]" +0xfaf9: "[?]" +0xfafa: "[?]" +0xfafb: "[?]" +0xfafc: "[?]" +0xfafd: "[?]" +0xfafe: "[?]" +/* x0fb */ +0xfb00: "ff" +0xfb01: "fi" +0xfb02: "fl" +0xfb03: "ffi" +0xfb04: "ffl" +0xfb05: "st" +0xfb06: "st" +0xfb07: "[?]" +0xfb08: "[?]" +0xfb09: "[?]" +0xfb0a: "[?]" +0xfb0b: "[?]" +0xfb0c: "[?]" +0xfb0d: "[?]" +0xfb0e: "[?]" +0xfb0f: "[?]" +0xfb10: "[?]" +0xfb11: "[?]" +0xfb12: "[?]" +0xfb13: "mn" +0xfb14: "me" +0xfb15: "mi" +0xfb16: "vn" +0xfb17: "mkh" +0xfb18: "[?]" +0xfb19: "[?]" +0xfb1a: "[?]" +0xfb1b: "[?]" +0xfb1c: "[?]" +0xfb1d: "yi" +0xfb1e: "" +0xfb1f: "ay" +0xfb20: "`" +0xfb21: "" +0xfb22: "d" +0xfb23: "h" +0xfb24: "k" +0xfb25: "l" +0xfb26: "m" +0xfb27: "m" +0xfb28: "t" +0xfb29: "+" +0xfb2a: "sh" +0xfb2b: "s" +0xfb2c: "sh" +0xfb2d: "s" +0xfb2e: "a" +0xfb2f: "a" +0xfb30: "" +0xfb31: "b" +0xfb32: "g" +0xfb33: "d" +0xfb34: "h" +0xfb35: "v" +0xfb36: "z" +0xfb37: "[?]" +0xfb38: "t" +0xfb39: "y" +0xfb3a: "k" +0xfb3b: "k" +0xfb3c: "l" +0xfb3d: "[?]" +0xfb3e: "l" +0xfb3f: "[?]" +0xfb40: "n" +0xfb41: "n" +0xfb42: "[?]" +0xfb43: "p" +0xfb44: "p" +0xfb45: "[?]" +0xfb46: "ts" +0xfb47: "ts" +0xfb48: "r" +0xfb49: "sh" +0xfb4a: "t" +0xfb4b: "vo" +0xfb4c: "b" +0xfb4d: "k" +0xfb4e: "p" +0xfb4f: "l" +0xfb50: "" +0xfb51: "" +0xfb52: "" +0xfb53: "" +0xfb54: "" +0xfb55: "" +0xfb56: "" +0xfb57: "" +0xfb58: "" +0xfb59: "" +0xfb5a: "" +0xfb5b: "" +0xfb5c: "" +0xfb5d: "" +0xfb5e: "" +0xfb5f: "" +0xfb60: "" +0xfb61: "" +0xfb62: "" +0xfb63: "" +0xfb64: "" +0xfb65: "" +0xfb66: "" +0xfb67: "" +0xfb68: "" +0xfb69: "" +0xfb6a: "" +0xfb6b: "" +0xfb6c: "" +0xfb6d: "" +0xfb6e: "" +0xfb6f: "" +0xfb70: "" +0xfb71: "" +0xfb72: "" +0xfb73: "" +0xfb74: "" +0xfb75: "" +0xfb76: "" +0xfb77: "" +0xfb78: "" +0xfb79: "" +0xfb7a: "" +0xfb7b: "" +0xfb7c: "" +0xfb7d: "" +0xfb7e: "" +0xfb7f: "" +0xfb80: "" +0xfb81: "" +0xfb82: "" +0xfb83: "" +0xfb84: "" +0xfb85: "" +0xfb86: "" +0xfb87: "" +0xfb88: "" +0xfb89: "" +0xfb8a: "" +0xfb8b: "" +0xfb8c: "" +0xfb8d: "" +0xfb8e: "" +0xfb8f: "" +0xfb90: "" +0xfb91: "" +0xfb92: "" +0xfb93: "" +0xfb94: "" +0xfb95: "" +0xfb96: "" +0xfb97: "" +0xfb98: "" +0xfb99: "" +0xfb9a: "" +0xfb9b: "" +0xfb9c: "" +0xfb9d: "" +0xfb9e: "" +0xfb9f: "" +0xfba0: "" +0xfba1: "" +0xfba2: "" +0xfba3: "" +0xfba4: "" +0xfba5: "" +0xfba6: "" +0xfba7: "" +0xfba8: "" +0xfba9: "" +0xfbaa: "" +0xfbab: "" +0xfbac: "" +0xfbad: "" +0xfbae: "" +0xfbaf: "" +0xfbb0: "" +0xfbb1: "" +0xfbb2: "[?]" +0xfbb3: "[?]" +0xfbb4: "[?]" +0xfbb5: "[?]" +0xfbb6: "[?]" +0xfbb7: "[?]" +0xfbb8: "[?]" +0xfbb9: "[?]" +0xfbba: "[?]" +0xfbbb: "[?]" +0xfbbc: "[?]" +0xfbbd: "[?]" +0xfbbe: "[?]" +0xfbbf: "[?]" +0xfbc0: "[?]" +0xfbc1: "[?]" +0xfbc2: "[?]" +0xfbc3: "[?]" +0xfbc4: "[?]" +0xfbc5: "[?]" +0xfbc6: "[?]" +0xfbc7: "[?]" +0xfbc8: "[?]" +0xfbc9: "[?]" +0xfbca: "[?]" +0xfbcb: "[?]" +0xfbcc: "[?]" +0xfbcd: "[?]" +0xfbce: "[?]" +0xfbcf: "[?]" +0xfbd0: "[?]" +0xfbd1: "[?]" +0xfbd2: "[?]" +0xfbd3: "" +0xfbd4: "" +0xfbd5: "" +0xfbd6: "" +0xfbd7: "" +0xfbd8: "" +0xfbd9: "" +0xfbda: "" +0xfbdb: "" +0xfbdc: "" +0xfbdd: "" +0xfbde: "" +0xfbdf: "" +0xfbe0: "" +0xfbe1: "" +0xfbe2: "" +0xfbe3: "" +0xfbe4: "" +0xfbe5: "" +0xfbe6: "" +0xfbe7: "" +0xfbe8: "" +0xfbe9: "" +0xfbea: "" +0xfbeb: "" +0xfbec: "" +0xfbed: "" +0xfbee: "" +0xfbef: "" +0xfbf0: "" +0xfbf1: "" +0xfbf2: "" +0xfbf3: "" +0xfbf4: "" +0xfbf5: "" +0xfbf6: "" +0xfbf7: "" +0xfbf8: "" +0xfbf9: "" +0xfbfa: "" +0xfbfb: "" +0xfbfc: "" +0xfbfd: "" +0xfbfe: "" +0xfbff: "" +/* x0fc */ +0xfc00: "" +0xfc01: "" +0xfc02: "" +0xfc03: "" +0xfc04: "" +0xfc05: "" +0xfc06: "" +0xfc07: "" +0xfc08: "" +0xfc09: "" +0xfc0a: "" +0xfc0b: "" +0xfc0c: "" +0xfc0d: "" +0xfc0e: "" +0xfc0f: "" +0xfc10: "" +0xfc11: "" +0xfc12: "" +0xfc13: "" +0xfc14: "" +0xfc15: "" +0xfc16: "" +0xfc17: "" +0xfc18: "" +0xfc19: "" +0xfc1a: "" +0xfc1b: "" +0xfc1c: "" +0xfc1d: "" +0xfc1e: "" +0xfc1f: "" +0xfc20: "" +0xfc21: "" +0xfc22: "" +0xfc23: "" +0xfc24: "" +0xfc25: "" +0xfc26: "" +0xfc27: "" +0xfc28: "" +0xfc29: "" +0xfc2a: "" +0xfc2b: "" +0xfc2c: "" +0xfc2d: "" +0xfc2e: "" +0xfc2f: "" +0xfc30: "" +0xfc31: "" +0xfc32: "" +0xfc33: "" +0xfc34: "" +0xfc35: "" +0xfc36: "" +0xfc37: "" +0xfc38: "" +0xfc39: "" +0xfc3a: "" +0xfc3b: "" +0xfc3c: "" +0xfc3d: "" +0xfc3e: "" +0xfc3f: "" +0xfc40: "" +0xfc41: "" +0xfc42: "" +0xfc43: "" +0xfc44: "" +0xfc45: "" +0xfc46: "" +0xfc47: "" +0xfc48: "" +0xfc49: "" +0xfc4a: "" +0xfc4b: "" +0xfc4c: "" +0xfc4d: "" +0xfc4e: "" +0xfc4f: "" +0xfc50: "" +0xfc51: "" +0xfc52: "" +0xfc53: "" +0xfc54: "" +0xfc55: "" +0xfc56: "" +0xfc57: "" +0xfc58: "" +0xfc59: "" +0xfc5a: "" +0xfc5b: "" +0xfc5c: "" +0xfc5d: "" +0xfc5e: "" +0xfc5f: "" +0xfc60: "" +0xfc61: "" +0xfc62: "" +0xfc63: "" +0xfc64: "" +0xfc65: "" +0xfc66: "" +0xfc67: "" +0xfc68: "" +0xfc69: "" +0xfc6a: "" +0xfc6b: "" +0xfc6c: "" +0xfc6d: "" +0xfc6e: "" +0xfc6f: "" +0xfc70: "" +0xfc71: "" +0xfc72: "" +0xfc73: "" +0xfc74: "" +0xfc75: "" +0xfc76: "" +0xfc77: "" +0xfc78: "" +0xfc79: "" +0xfc7a: "" +0xfc7b: "" +0xfc7c: "" +0xfc7d: "" +0xfc7e: "" +0xfc7f: "" +0xfc80: "" +0xfc81: "" +0xfc82: "" +0xfc83: "" +0xfc84: "" +0xfc85: "" +0xfc86: "" +0xfc87: "" +0xfc88: "" +0xfc89: "" +0xfc8a: "" +0xfc8b: "" +0xfc8c: "" +0xfc8d: "" +0xfc8e: "" +0xfc8f: "" +0xfc90: "" +0xfc91: "" +0xfc92: "" +0xfc93: "" +0xfc94: "" +0xfc95: "" +0xfc96: "" +0xfc97: "" +0xfc98: "" +0xfc99: "" +0xfc9a: "" +0xfc9b: "" +0xfc9c: "" +0xfc9d: "" +0xfc9e: "" +0xfc9f: "" +0xfca0: "" +0xfca1: "" +0xfca2: "" +0xfca3: "" +0xfca4: "" +0xfca5: "" +0xfca6: "" +0xfca7: "" +0xfca8: "" +0xfca9: "" +0xfcaa: "" +0xfcab: "" +0xfcac: "" +0xfcad: "" +0xfcae: "" +0xfcaf: "" +0xfcb0: "" +0xfcb1: "" +0xfcb2: "" +0xfcb3: "" +0xfcb4: "" +0xfcb5: "" +0xfcb6: "" +0xfcb7: "" +0xfcb8: "" +0xfcb9: "" +0xfcba: "" +0xfcbb: "" +0xfcbc: "" +0xfcbd: "" +0xfcbe: "" +0xfcbf: "" +0xfcc0: "" +0xfcc1: "" +0xfcc2: "" +0xfcc3: "" +0xfcc4: "" +0xfcc5: "" +0xfcc6: "" +0xfcc7: "" +0xfcc8: "" +0xfcc9: "" +0xfcca: "" +0xfccb: "" +0xfccc: "" +0xfccd: "" +0xfcce: "" +0xfccf: "" +0xfcd0: "" +0xfcd1: "" +0xfcd2: "" +0xfcd3: "" +0xfcd4: "" +0xfcd5: "" +0xfcd6: "" +0xfcd7: "" +0xfcd8: "" +0xfcd9: "" +0xfcda: "" +0xfcdb: "" +0xfcdc: "" +0xfcdd: "" +0xfcde: "" +0xfcdf: "" +0xfce0: "" +0xfce1: "" +0xfce2: "" +0xfce3: "" +0xfce4: "" +0xfce5: "" +0xfce6: "" +0xfce7: "" +0xfce8: "" +0xfce9: "" +0xfcea: "" +0xfceb: "" +0xfcec: "" +0xfced: "" +0xfcee: "" +0xfcef: "" +0xfcf0: "" +0xfcf1: "" +0xfcf2: "" +0xfcf3: "" +0xfcf4: "" +0xfcf5: "" +0xfcf6: "" +0xfcf7: "" +0xfcf8: "" +0xfcf9: "" +0xfcfa: "" +0xfcfb: "" +0xfcfc: "" +0xfcfd: "" +0xfcfe: "" +0xfcff: "" +/* x0fd */ +0xfd00: "" +0xfd01: "" +0xfd02: "" +0xfd03: "" +0xfd04: "" +0xfd05: "" +0xfd06: "" +0xfd07: "" +0xfd08: "" +0xfd09: "" +0xfd0a: "" +0xfd0b: "" +0xfd0c: "" +0xfd0d: "" +0xfd0e: "" +0xfd0f: "" +0xfd10: "" +0xfd11: "" +0xfd12: "" +0xfd13: "" +0xfd14: "" +0xfd15: "" +0xfd16: "" +0xfd17: "" +0xfd18: "" +0xfd19: "" +0xfd1a: "" +0xfd1b: "" +0xfd1c: "" +0xfd1d: "" +0xfd1e: "" +0xfd1f: "" +0xfd20: "" +0xfd21: "" +0xfd22: "" +0xfd23: "" +0xfd24: "" +0xfd25: "" +0xfd26: "" +0xfd27: "" +0xfd28: "" +0xfd29: "" +0xfd2a: "" +0xfd2b: "" +0xfd2c: "" +0xfd2d: "" +0xfd2e: "" +0xfd2f: "" +0xfd30: "" +0xfd31: "" +0xfd32: "" +0xfd33: "" +0xfd34: "" +0xfd35: "" +0xfd36: "" +0xfd37: "" +0xfd38: "" +0xfd39: "" +0xfd3a: "" +0xfd3b: "" +0xfd3c: "" +0xfd3d: "" +0xfd3e: "" +0xfd3f: "" +0xfd40: "[?]" +0xfd41: "[?]" +0xfd42: "[?]" +0xfd43: "[?]" +0xfd44: "[?]" +0xfd45: "[?]" +0xfd46: "[?]" +0xfd47: "[?]" +0xfd48: "[?]" +0xfd49: "[?]" +0xfd4a: "[?]" +0xfd4b: "[?]" +0xfd4c: "[?]" +0xfd4d: "[?]" +0xfd4e: "[?]" +0xfd4f: "[?]" +0xfd50: "" +0xfd51: "" +0xfd52: "" +0xfd53: "" +0xfd54: "" +0xfd55: "" +0xfd56: "" +0xfd57: "" +0xfd58: "" +0xfd59: "" +0xfd5a: "" +0xfd5b: "" +0xfd5c: "" +0xfd5d: "" +0xfd5e: "" +0xfd5f: "" +0xfd60: "" +0xfd61: "" +0xfd62: "" +0xfd63: "" +0xfd64: "" +0xfd65: "" +0xfd66: "" +0xfd67: "" +0xfd68: "" +0xfd69: "" +0xfd6a: "" +0xfd6b: "" +0xfd6c: "" +0xfd6d: "" +0xfd6e: "" +0xfd6f: "" +0xfd70: "" +0xfd71: "" +0xfd72: "" +0xfd73: "" +0xfd74: "" +0xfd75: "" +0xfd76: "" +0xfd77: "" +0xfd78: "" +0xfd79: "" +0xfd7a: "" +0xfd7b: "" +0xfd7c: "" +0xfd7d: "" +0xfd7e: "" +0xfd7f: "" +0xfd80: "" +0xfd81: "" +0xfd82: "" +0xfd83: "" +0xfd84: "" +0xfd85: "" +0xfd86: "" +0xfd87: "" +0xfd88: "" +0xfd89: "" +0xfd8a: "" +0xfd8b: "" +0xfd8c: "" +0xfd8d: "" +0xfd8e: "" +0xfd8f: "" +0xfd90: "[?]" +0xfd91: "[?]" +0xfd92: "" +0xfd93: "" +0xfd94: "" +0xfd95: "" +0xfd96: "" +0xfd97: "" +0xfd98: "" +0xfd99: "" +0xfd9a: "" +0xfd9b: "" +0xfd9c: "" +0xfd9d: "" +0xfd9e: "" +0xfd9f: "" +0xfda0: "" +0xfda1: "" +0xfda2: "" +0xfda3: "" +0xfda4: "" +0xfda5: "" +0xfda6: "" +0xfda7: "" +0xfda8: "" +0xfda9: "" +0xfdaa: "" +0xfdab: "" +0xfdac: "" +0xfdad: "" +0xfdae: "" +0xfdaf: "" +0xfdb0: "" +0xfdb1: "" +0xfdb2: "" +0xfdb3: "" +0xfdb4: "" +0xfdb5: "" +0xfdb6: "" +0xfdb7: "" +0xfdb8: "" +0xfdb9: "" +0xfdba: "" +0xfdbb: "" +0xfdbc: "" +0xfdbd: "" +0xfdbe: "" +0xfdbf: "" +0xfdc0: "" +0xfdc1: "" +0xfdc2: "" +0xfdc3: "" +0xfdc4: "" +0xfdc5: "" +0xfdc6: "" +0xfdc7: "" +0xfdc8: "[?]" +0xfdc9: "[?]" +0xfdca: "[?]" +0xfdcb: "[?]" +0xfdcc: "[?]" +0xfdcd: "[?]" +0xfdce: "[?]" +0xfdcf: "[?]" +0xfdd0: "[?]" +0xfdd1: "[?]" +0xfdd2: "[?]" +0xfdd3: "[?]" +0xfdd4: "[?]" +0xfdd5: "[?]" +0xfdd6: "[?]" +0xfdd7: "[?]" +0xfdd8: "[?]" +0xfdd9: "[?]" +0xfdda: "[?]" +0xfddb: "[?]" +0xfddc: "[?]" +0xfddd: "[?]" +0xfdde: "[?]" +0xfddf: "[?]" +0xfde0: "[?]" +0xfde1: "[?]" +0xfde2: "[?]" +0xfde3: "[?]" +0xfde4: "[?]" +0xfde5: "[?]" +0xfde6: "[?]" +0xfde7: "[?]" +0xfde8: "[?]" +0xfde9: "[?]" +0xfdea: "[?]" +0xfdeb: "[?]" +0xfdec: "[?]" +0xfded: "[?]" +0xfdee: "[?]" +0xfdef: "[?]" +0xfdf0: "" +0xfdf1: "" +0xfdf2: "" +0xfdf3: "" +0xfdf4: "" +0xfdf5: "" +0xfdf6: "" +0xfdf7: "" +0xfdf8: "" +0xfdf9: "" +0xfdfa: "" +0xfdfb: "" +0xfdfc: "[?]" +0xfdfd: "[?]" +0xfdfe: "[?]" +/* x0fe */ +0xfe00: "[?]" +0xfe01: "[?]" +0xfe02: "[?]" +0xfe03: "[?]" +0xfe04: "[?]" +0xfe05: "[?]" +0xfe06: "[?]" +0xfe07: "[?]" +0xfe08: "[?]" +0xfe09: "[?]" +0xfe0a: "[?]" +0xfe0b: "[?]" +0xfe0c: "[?]" +0xfe0d: "[?]" +0xfe0e: "[?]" +0xfe0f: "[?]" +0xfe10: "[?]" +0xfe11: "[?]" +0xfe12: "[?]" +0xfe13: "[?]" +0xfe14: "[?]" +0xfe15: "[?]" +0xfe16: "[?]" +0xfe17: "[?]" +0xfe18: "[?]" +0xfe19: "[?]" +0xfe1a: "[?]" +0xfe1b: "[?]" +0xfe1c: "[?]" +0xfe1d: "[?]" +0xfe1e: "[?]" +0xfe1f: "[?]" +0xfe20: "" +0xfe21: "" +0xfe22: "" +0xfe23: "~" +0xfe24: "[?]" +0xfe25: "[?]" +0xfe26: "[?]" +0xfe27: "[?]" +0xfe28: "[?]" +0xfe29: "[?]" +0xfe2a: "[?]" +0xfe2b: "[?]" +0xfe2c: "[?]" +0xfe2d: "[?]" +0xfe2e: "[?]" +0xfe2f: "[?]" +0xfe30: ".." +0xfe31: "--" +0xfe32: "-" +0xfe33: "_" +0xfe34: "_" +0xfe35: "(" +0xfe36: ") " +0xfe37: "{" +0xfe38: "} " +0xfe39: "[" +0xfe3a: "] " +0xfe3b: "[(" +0xfe3c: ")] " +0xfe3d: "<<" +0xfe3e: ">> " +0xfe3f: "<" +0xfe40: "> " +0xfe41: "[" +0xfe42: "] " +0xfe43: "{" +0xfe44: "}" +0xfe45: "[?]" +0xfe46: "[?]" +0xfe47: "[?]" +0xfe48: "[?]" +0xfe49: "" +0xfe4a: "" +0xfe4b: "" +0xfe4c: "" +0xfe4d: "" +0xfe4e: "" +0xfe4f: "" +0xfe50: "," +0xfe51: "," +0xfe52: "." +0xfe53: "" +0xfe54: ";" +0xfe55: ":" +0xfe56: "?" +0xfe57: "!" +0xfe58: "-" +0xfe59: "(" +0xfe5a: ")" +0xfe5b: "{" +0xfe5c: "}" +0xfe5d: "{" +0xfe5e: "}" +0xfe5f: "#" +0xfe60: "&" +0xfe61: "*" +0xfe62: "+" +0xfe63: "-" +0xfe64: "<" +0xfe65: ">" +0xfe66: "=" +0xfe67: "" +0xfe68: "\\" +0xfe69: "$" +0xfe6a: "%" +0xfe6b: "@" +0xfe6c: "[?]" +0xfe6d: "[?]" +0xfe6e: "[?]" +0xfe6f: "[?]" +0xfe70: "" +0xfe71: "" +0xfe72: "" +0xfe73: "[?]" +0xfe74: "" +0xfe75: "[?]" +0xfe76: "" +0xfe77: "" +0xfe78: "" +0xfe79: "" +0xfe7a: "" +0xfe7b: "" +0xfe7c: "" +0xfe7d: "" +0xfe7e: "" +0xfe7f: "" +0xfe80: "" +0xfe81: "" +0xfe82: "" +0xfe83: "" +0xfe84: "" +0xfe85: "" +0xfe86: "" +0xfe87: "" +0xfe88: "" +0xfe89: "" +0xfe8a: "" +0xfe8b: "" +0xfe8c: "" +0xfe8d: "" +0xfe8e: "" +0xfe8f: "" +0xfe90: "" +0xfe91: "" +0xfe92: "" +0xfe93: "" +0xfe94: "" +0xfe95: "" +0xfe96: "" +0xfe97: "" +0xfe98: "" +0xfe99: "" +0xfe9a: "" +0xfe9b: "" +0xfe9c: "" +0xfe9d: "" +0xfe9e: "" +0xfe9f: "" +0xfea0: "" +0xfea1: "" +0xfea2: "" +0xfea3: "" +0xfea4: "" +0xfea5: "" +0xfea6: "" +0xfea7: "" +0xfea8: "" +0xfea9: "" +0xfeaa: "" +0xfeab: "" +0xfeac: "" +0xfead: "" +0xfeae: "" +0xfeaf: "" +0xfeb0: "" +0xfeb1: "" +0xfeb2: "" +0xfeb3: "" +0xfeb4: "" +0xfeb5: "" +0xfeb6: "" +0xfeb7: "" +0xfeb8: "" +0xfeb9: "" +0xfeba: "" +0xfebb: "" +0xfebc: "" +0xfebd: "" +0xfebe: "" +0xfebf: "" +0xfec0: "" +0xfec1: "" +0xfec2: "" +0xfec3: "" +0xfec4: "" +0xfec5: "" +0xfec6: "" +0xfec7: "" +0xfec8: "" +0xfec9: "" +0xfeca: "" +0xfecb: "" +0xfecc: "" +0xfecd: "" +0xfece: "" +0xfecf: "" +0xfed0: "" +0xfed1: "" +0xfed2: "" +0xfed3: "" +0xfed4: "" +0xfed5: "" +0xfed6: "" +0xfed7: "" +0xfed8: "" +0xfed9: "" +0xfeda: "" +0xfedb: "" +0xfedc: "" +0xfedd: "" +0xfede: "" +0xfedf: "" +0xfee0: "" +0xfee1: "" +0xfee2: "" +0xfee3: "" +0xfee4: "" +0xfee5: "" +0xfee6: "" +0xfee7: "" +0xfee8: "" +0xfee9: "" +0xfeea: "" +0xfeeb: "" +0xfeec: "" +0xfeed: "" +0xfeee: "" +0xfeef: "" +0xfef0: "" +0xfef1: "" +0xfef2: "" +0xfef3: "" +0xfef4: "" +0xfef5: "" +0xfef6: "" +0xfef7: "" +0xfef8: "" +0xfef9: "" +0xfefa: "" +0xfefb: "" +0xfefc: "" +0xfefd: "[?]" +0xfefe: "[?]" +0xfeff: "" +/* x0ff */ +0xff00: "[?]" +0xff01: "!" +0xff02: "\"" +0xff03: "#" +0xff04: "$" +0xff05: "%" +0xff06: "&" +0xff07: "'" +0xff08: "(" +0xff09: ")" +0xff0a: "*" +0xff0b: "+" +0xff0c: "," +0xff0d: "-" +0xff0e: "." +0xff0f: "/" +0xff10: "0" +0xff11: "1" +0xff12: "2" +0xff13: "3" +0xff14: "4" +0xff15: "5" +0xff16: "6" +0xff17: "7" +0xff18: "8" +0xff19: "9" +0xff1a: ":" +0xff1b: ";" +0xff1c: "<" +0xff1d: "=" +0xff1e: ">" +0xff1f: "?" +0xff20: "@" +0xff21: "A" +0xff22: "B" +0xff23: "C" +0xff24: "D" +0xff25: "E" +0xff26: "F" +0xff27: "G" +0xff28: "H" +0xff29: "I" +0xff2a: "J" +0xff2b: "K" +0xff2c: "L" +0xff2d: "M" +0xff2e: "N" +0xff2f: "O" +0xff30: "P" +0xff31: "Q" +0xff32: "R" +0xff33: "S" +0xff34: "T" +0xff35: "U" +0xff36: "V" +0xff37: "W" +0xff38: "X" +0xff39: "Y" +0xff3a: "Z" +0xff3b: "[" +0xff3c: "\\" +0xff3d: "]" +0xff3e: "^" +0xff3f: "_" +0xff40: "`" +0xff41: "a" +0xff42: "b" +0xff43: "c" +0xff44: "d" +0xff45: "e" +0xff46: "f" +0xff47: "g" +0xff48: "h" +0xff49: "i" +0xff4a: "j" +0xff4b: "k" +0xff4c: "l" +0xff4d: "m" +0xff4e: "n" +0xff4f: "o" +0xff50: "p" +0xff51: "q" +0xff52: "r" +0xff53: "s" +0xff54: "t" +0xff55: "u" +0xff56: "v" +0xff57: "w" +0xff58: "x" +0xff59: "y" +0xff5a: "z" +0xff5b: "{" +0xff5c: "|" +0xff5d: "}" +0xff5e: "~" +0xff5f: "[?]" +0xff60: "[?]" +0xff61: "." +0xff62: "[" +0xff63: "]" +0xff64: "," +0xff65: "*" +0xff66: "wo" +0xff67: "a" +0xff68: "i" +0xff69: "u" +0xff6a: "e" +0xff6b: "o" +0xff6c: "ya" +0xff6d: "yu" +0xff6e: "yo" +0xff6f: "tu" +0xff70: "+" +0xff71: "a" +0xff72: "i" +0xff73: "u" +0xff74: "e" +0xff75: "o" +0xff76: "ka" +0xff77: "ki" +0xff78: "ku" +0xff79: "ke" +0xff7a: "ko" +0xff7b: "sa" +0xff7c: "si" +0xff7d: "su" +0xff7e: "se" +0xff7f: "so" +0xff80: "ta" +0xff81: "ti" +0xff82: "tu" +0xff83: "te" +0xff84: "to" +0xff85: "na" +0xff86: "ni" +0xff87: "nu" +0xff88: "ne" +0xff89: "no" +0xff8a: "ha" +0xff8b: "hi" +0xff8c: "hu" +0xff8d: "he" +0xff8e: "ho" +0xff8f: "ma" +0xff90: "mi" +0xff91: "mu" +0xff92: "me" +0xff93: "mo" +0xff94: "ya" +0xff95: "yu" +0xff96: "yo" +0xff97: "ra" +0xff98: "ri" +0xff99: "ru" +0xff9a: "re" +0xff9b: "ro" +0xff9c: "wa" +0xff9d: "n" +0xff9e: ":" +0xff9f: ";" +0xffa0: "" +0xffa1: "g" +0xffa2: "gg" +0xffa3: "gs" +0xffa4: "n" +0xffa5: "nj" +0xffa6: "nh" +0xffa7: "d" +0xffa8: "dd" +0xffa9: "r" +0xffaa: "lg" +0xffab: "lm" +0xffac: "lb" +0xffad: "ls" +0xffae: "lt" +0xffaf: "lp" +0xffb0: "rh" +0xffb1: "m" +0xffb2: "b" +0xffb3: "bb" +0xffb4: "bs" +0xffb5: "s" +0xffb6: "ss" +0xffb7: "" +0xffb8: "j" +0xffb9: "jj" +0xffba: "c" +0xffbb: "k" +0xffbc: "t" +0xffbd: "p" +0xffbe: "h" +0xffbf: "[?]" +0xffc0: "[?]" +0xffc1: "[?]" +0xffc2: "a" +0xffc3: "ae" +0xffc4: "ya" +0xffc5: "yae" +0xffc6: "eo" +0xffc7: "e" +0xffc8: "[?]" +0xffc9: "[?]" +0xffca: "yeo" +0xffcb: "ye" +0xffcc: "o" +0xffcd: "wa" +0xffce: "wae" +0xffcf: "oe" +0xffd0: "[?]" +0xffd1: "[?]" +0xffd2: "yo" +0xffd3: "u" +0xffd4: "weo" +0xffd5: "we" +0xffd6: "wi" +0xffd7: "yu" +0xffd8: "[?]" +0xffd9: "[?]" +0xffda: "eu" +0xffdb: "yi" +0xffdc: "i" +0xffdd: "[?]" +0xffde: "[?]" +0xffdf: "[?]" +0xffe0: "/C" +0xffe1: "PS" +0xffe2: "!" +0xffe3: "-" +0xffe4: "|" +0xffe5: "Y=" +0xffe6: "W=" +0xffe7: "[?]" +0xffe8: "|" +0xffe9: "-" +0xffea: "|" +0xffeb: "-" +0xffec: "|" +0xffed: "#" +0xffee: "O" +0xffef: "[?]" +0xfff0: "[?]" +0xfff1: "[?]" +0xfff2: "[?]" +0xfff3: "[?]" +0xfff4: "[?]" +0xfff5: "[?]" +0xfff6: "[?]" +0xfff7: "[?]" +0xfff8: "[?]" +0xfff9: "{" +0xfffa: "|" +0xfffb: "}" +0xfffc: "" +0xfffd: "" +0xfffe: "" +0xffff: "" diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode.go b/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode.go new file mode 100644 index 00000000000..fa414bb0954 --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode.go @@ -0,0 +1,63 @@ +// Package unidecode implements a unicode transliterator +// which replaces non-ASCII characters with their ASCII +// approximations. +package unidecode + +import ( + "unicode" + + "gopkgs.com/pool.v1" +) + +const pooledCapacity = 64 + +var ( + slicePool = pool.New(0) +) + +// Unidecode implements a unicode transliterator, which +// replaces non-ASCII characters with their ASCII +// counterparts. +// Given an unicode encoded string, returns +// another string with non-ASCII characters replaced +// with their closest ASCII counterparts. +// e.g. Unicode("áéíóú") => "aeiou" +func Unidecode(s string) string { + if !decoded { + mutex.Lock() + if !decoded { + decodeTransliterations() + decoded = true + } + mutex.Unlock() + } + l := len(s) + var r []rune + if l > pooledCapacity { + r = make([]rune, 0, len(s)) + } else { + if x := slicePool.Get(); x != nil { + r = x.([]rune)[:0] + } else { + r = make([]rune, 0, pooledCapacity) + } + } + for _, c := range s { + if c <= unicode.MaxASCII { + r = append(r, c) + continue + } + if c > unicode.MaxRune || c > transCount { + /* Ignore reserved chars */ + continue + } + if d := transliterations[c]; d != nil { + r = append(r, d...) + } + } + res := string(r) + if l <= pooledCapacity { + slicePool.Put(r) + } + return res +} diff --git a/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode_test.go b/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode_test.go new file mode 100644 index 00000000000..7ef0de9f2fc --- /dev/null +++ b/Godeps/_workspace/src/github.com/dalu/unidecode/unidecode_test.go @@ -0,0 +1,57 @@ +package unidecode + +import ( + "testing" +) + +func testTransliteration(original string, decoded string, t *testing.T) { + if r := Unidecode(original); r != decoded { + t.Errorf("Expected '%s', got '%s'\n", decoded, r) + } +} + +func TestASCII(t *testing.T) { + s := "ABCDEF" + testTransliteration(s, s, t) +} + +func TestKnosos(t *testing.T) { + o := "Κνωσός" + d := "Knosos" + testTransliteration(o, d, t) +} + +func TestBeiJing(t *testing.T) { + o := "\u5317\u4EB0" + d := "Bei Jing " + testTransliteration(o, d, t) +} + +func TestEmoji(t *testing.T) { + o := "Hey Luna t belle 😵😂" + d := "Hey Luna t belle " + testTransliteration(o, d, t) +} + +func BenchmarkUnidecode(b *testing.B) { + cases := []string{ + "ABCDEF", + "Κνωσός", + "\u5317\u4EB0", + } + for ii := 0; ii < b.N; ii++ { + for _, v := range cases { + _ = Unidecode(v) + } + } +} + +func BenchmarkDecodeTable(b *testing.B) { + for ii := 0; ii < b.N; ii++ { + decodeTransliterations() + } +} + +func init() { + decodeTransliterations() +} diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/README.md b/Godeps/_workspace/src/github.com/go-xorm/core/README.md new file mode 100644 index 00000000000..0ae94a584ab --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-xorm/core/README.md @@ -0,0 +1,114 @@ +Core is a lightweight wrapper of sql.DB. + +# Open +```Go +db, _ := core.Open(db, connstr) +``` + +# SetMapper +```Go +db.SetMapper(SameMapper()) +``` + +## Scan usage + +### Scan +```Go +rows, _ := db.Query() +for rows.Next() { + rows.Scan() +} +``` + +### ScanMap +```Go +rows, _ := db.Query() +for rows.Next() { + rows.ScanMap() +``` + +### ScanSlice + +You can use `[]string`, `[][]byte`, `[]interface{}`, `[]*string`, `[]sql.NullString` to ScanSclice. Notice, slice's length should be equal or less than select columns. + +```Go +rows, _ := db.Query() +cols, _ := rows.Columns() +for rows.Next() { + var s = make([]string, len(cols)) + rows.ScanSlice(&s) +} +``` + +```Go +rows, _ := db.Query() +cols, _ := rows.Columns() +for rows.Next() { + var s = make([]*string, len(cols)) + rows.ScanSlice(&s) +} +``` + +### ScanStruct +```Go +rows, _ := db.Query() +for rows.Next() { + rows.ScanStructByName() + rows.ScanStructByIndex() +} +``` + +## Query usage +```Go +rows, err := db.Query("select * from table where name = ?", name) + +user = User{ + Name:"lunny", +} +rows, err := db.QueryStruct("select * from table where name = ?Name", + &user) + +var user = map[string]interface{}{ + "name": "lunny", +} +rows, err = db.QueryMap("select * from table where name = ?name", + &user) +``` + +## QueryRow usage +```Go +row := db.QueryRow("select * from table where name = ?", name) + +user = User{ + Name:"lunny", +} +row := db.QueryRowStruct("select * from table where name = ?Name", + &user) + +var user = map[string]interface{}{ + "name": "lunny", +} +row = db.QueryRowMap("select * from table where name = ?name", + &user) +``` + +## Exec usage +```Go +db.Exec("insert into user (`name`, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", name, title, age, alias...) + +user = User{ + Name:"lunny", + Title:"test", + Age: 18, +} +result, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) values (?Name,?Title,?Age,?Alias,?NickName,?Created)", + &user) + +var user = map[string]interface{}{ + "Name": "lunny", + "Title": "test", + "Age": 18, +} +result, err = db.ExecMap("insert into user (`name`, title, age, alias, nick_name,created) values (?Name,?Title,?Age,?Alias,?NickName,?Created)", + &user) +``` \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/column.go b/Godeps/_workspace/src/github.com/go-xorm/core/column.go index 18921ca7c4c..52468aa20e6 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/column.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/column.go @@ -121,6 +121,21 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { col.fieldPath = strings.Split(col.FieldName, ".") } + if dataStruct.Type().Kind() == reflect.Map { + var keyValue reflect.Value + + if len(col.fieldPath) == 1 { + keyValue = reflect.ValueOf(col.FieldName) + } else if len(col.fieldPath) == 2 { + keyValue = reflect.ValueOf(col.fieldPath[1]) + } else { + return nil, fmt.Errorf("Unsupported mutliderive %v", col.FieldName) + } + + fieldValue = dataStruct.MapIndex(keyValue) + return &fieldValue, nil + } + if len(col.fieldPath) == 1 { fieldValue = dataStruct.FieldByName(col.FieldName) } else if len(col.fieldPath) == 2 { diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/dialect.go b/Godeps/_workspace/src/github.com/go-xorm/core/dialect.go index 05375642610..43a22670913 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/dialect.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/dialect.go @@ -47,15 +47,13 @@ type Dialect interface { SupportInsertMany() bool SupportEngine() bool SupportCharset() bool + SupportDropIfExists() bool IndexOnTable() bool ShowCreateNull() bool IndexCheckSql(tableName, idxName string) (string, []interface{}) TableCheckSql(tableName string) (string, []interface{}) - //ColumnCheckSql(tableName, colName string) (string, []interface{}) - //IsTableExist(tableName string) (bool, error) - //IsIndexExist(tableName string, idx *Index) (bool, error) IsColumnExist(tableName string, col *Column) (bool, error) CreateTableSql(table *Table, tableName, storeEngine, charset string) string @@ -65,15 +63,13 @@ type Dialect interface { ModifyColumnSql(tableName string, col *Column) string + //CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error + //MustDropTable(tableName string) error + GetColumns(tableName string) ([]string, map[string]*Column, error) GetTables() ([]*Table, error) GetIndexes(tableName string) (map[string]*Index, error) - // Get data from db cell to a struct's field - //GetData(col *Column, fieldValue *reflect.Value, cellData interface{}) error - // Set field data to db - //SetData(col *Column, fieldValue *refelct.Value) (interface{}, error) - Filters() []Filter } @@ -144,6 +140,10 @@ func (db *Base) RollBackStr() string { return "ROLL BACK" } +func (db *Base) SupportDropIfExists() bool { + return true +} + func (db *Base) DropTableSql(tableName string) string { return fmt.Sprintf("DROP TABLE IF EXISTS `%s`", tableName) } @@ -170,35 +170,52 @@ func (db *Base) IsColumnExist(tableName string, col *Column) (bool, error) { return db.HasRecords(query, db.DbName, tableName, col.Name) } +/* +func (db *Base) CreateTableIfNotExists(table *Table, tableName, storeEngine, charset string) error { + sql, args := db.dialect.TableCheckSql(tableName) + rows, err := db.DB().Query(sql, args...) + if db.Logger != nil { + db.Logger.Info("[sql]", sql, args) + } + if err != nil { + return err + } + defer rows.Close() + + if rows.Next() { + return nil + } + + sql = db.dialect.CreateTableSql(table, tableName, storeEngine, charset) + _, err = db.DB().Exec(sql) + if db.Logger != nil { + db.Logger.Info("[sql]", sql) + } + return err +}*/ + func (db *Base) CreateIndexSql(tableName string, index *Index) string { quote := db.dialect.Quote var unique string var idxName string if index.Type == UniqueType { unique = " UNIQUE" - idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name) - } else { - idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) } - return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v);", unique, + idxName = index.XName(tableName) + return fmt.Sprintf("CREATE%s INDEX %v ON %v (%v)", unique, quote(idxName), quote(tableName), quote(strings.Join(index.Cols, quote(",")))) } func (db *Base) DropIndexSql(tableName string, index *Index) string { quote := db.dialect.Quote - //var unique string - var idxName string = index.Name - if !strings.HasPrefix(idxName, "UQE_") && - !strings.HasPrefix(idxName, "IDX_") { - if index.Type == UniqueType { - idxName = fmt.Sprintf("UQE_%v_%v", tableName, index.Name) - } else { - idxName = fmt.Sprintf("IDX_%v_%v", tableName, index.Name) - } + var name string + if index.IsRegular { + name = index.XName(tableName) + } else { + name = index.Name } - return fmt.Sprintf("DROP INDEX %v ON %s", - quote(idxName), quote(tableName)) + return fmt.Sprintf("DROP INDEX %v ON %s", quote(name), quote(tableName)) } func (db *Base) ModifyColumnSql(tableName string, col *Column) string { diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/index.go b/Godeps/_workspace/src/github.com/go-xorm/core/index.go index e8f447d7031..73b95175adc 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/index.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/index.go @@ -1,7 +1,9 @@ package core import ( + "fmt" "sort" + "strings" ) const ( @@ -11,9 +13,21 @@ const ( // database index type Index struct { - Name string - Type int - Cols []string + IsRegular bool + Name string + Type int + Cols []string +} + +func (index *Index) XName(tableName string) string { + if !strings.HasPrefix(index.Name, "UQE_") && + !strings.HasPrefix(index.Name, "IDX_") { + if index.Type == UniqueType { + return fmt.Sprintf("UQE_%v_%v", tableName, index.Name) + } + return fmt.Sprintf("IDX_%v_%v", tableName, index.Name) + } + return index.Name } // add columns which will be composite index @@ -24,6 +38,9 @@ func (index *Index) AddColumn(cols ...string) { } func (index *Index) Equal(dst *Index) bool { + if index.Type != dst.Type { + return false + } if len(index.Cols) != len(dst.Cols) { return false } @@ -40,5 +57,5 @@ func (index *Index) Equal(dst *Index) bool { // new an index func NewIndex(name string, indexType int) *Index { - return &Index{name, indexType, make([]string, 0)} + return &Index{true, name, indexType, make([]string, 0)} } diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/mapper.go b/Godeps/_workspace/src/github.com/go-xorm/core/mapper.go index c00dc395211..bb72a156624 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/mapper.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/mapper.go @@ -9,7 +9,6 @@ import ( type IMapper interface { Obj2Table(string) string Table2Obj(string) string - TableName(string) string } type CacheMapper struct { @@ -56,10 +55,6 @@ func (m *CacheMapper) Table2Obj(t string) string { return o } -func (m *CacheMapper) TableName(t string) string { - return t -} - // SameMapper implements IMapper and provides same name between struct and // database table type SameMapper struct { @@ -73,10 +68,6 @@ func (m SameMapper) Table2Obj(t string) string { return t } -func (m SameMapper) TableName(t string) string { - return t -} - // SnakeMapper implements IMapper and provides name transaltion between // struct and database table type SnakeMapper struct { @@ -97,25 +88,6 @@ func snakeCasedName(name string) string { return string(newstr) } -/*func pascal2Sql(s string) (d string) { - d = "" - lastIdx := 0 - for i := 0; i < len(s); i++ { - if s[i] >= 'A' && s[i] <= 'Z' { - if lastIdx < i { - d += s[lastIdx+1 : i] - } - if i != 0 { - d += "_" - } - d += string(s[i] + 32) - lastIdx = i - } - } - d += s[lastIdx+1:] - return -}*/ - func (mapper SnakeMapper) Obj2Table(name string) string { return snakeCasedName(name) } @@ -148,9 +120,103 @@ func (mapper SnakeMapper) Table2Obj(name string) string { return titleCasedName(name) } -func (mapper SnakeMapper) TableName(t string) string { - return t +// GonicMapper implements IMapper. It will consider initialisms when mapping names. +// E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid +type GonicMapper map[string]bool + +func isASCIIUpper(r rune) bool { + return 'A' <= r && r <= 'Z' } + +func toASCIIUpper(r rune) rune { + if 'a' <= r && r <= 'z' { + r -= ('a' - 'A') + } + return r +} + +func gonicCasedName(name string) string { + newstr := make([]rune, 0, len(name)+3) + for idx, chr := range name { + if isASCIIUpper(chr) && idx > 0 { + if !isASCIIUpper(newstr[len(newstr)-1]) { + newstr = append(newstr, '_') + } + } + + if !isASCIIUpper(chr) && idx > 1 { + l := len(newstr) + if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) { + newstr = append(newstr, newstr[l-1]) + newstr[l-1] = '_' + } + } + + newstr = append(newstr, chr) + } + return strings.ToLower(string(newstr)) +} + +func (mapper GonicMapper) Obj2Table(name string) string { + return gonicCasedName(name) +} + +func (mapper GonicMapper) Table2Obj(name string) string { + newstr := make([]rune, 0) + + name = strings.ToLower(name) + parts := strings.Split(name, "_") + + for _, p := range parts { + _, isInitialism := mapper[strings.ToUpper(p)] + for i, r := range p { + if i == 0 || isInitialism { + r = toASCIIUpper(r) + } + newstr = append(newstr, r) + } + } + + return string(newstr) +} + +// A GonicMapper that contains a list of common initialisms taken from golang/lint +var LintGonicMapper = GonicMapper{ + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SSH": true, + "TLS": true, + "TTL": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XSRF": true, + "XSS": true, +} + // provide prefix table name support type PrefixMapper struct { Mapper IMapper @@ -165,10 +231,6 @@ func (mapper PrefixMapper) Table2Obj(name string) string { return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):]) } -func (mapper PrefixMapper) TableName(name string) string { - return mapper.Prefix + name -} - func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper { return PrefixMapper{mapper, prefix} } @@ -187,10 +249,6 @@ func (mapper SuffixMapper) Table2Obj(name string) string { return mapper.Mapper.Table2Obj(name[:len(name)-len(mapper.Suffix)]) } -func (mapper SuffixMapper) TableName(name string) string { - return name + mapper.Suffix -} - func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper { return SuffixMapper{mapper, suffix} } diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/mapper_test.go b/Godeps/_workspace/src/github.com/go-xorm/core/mapper_test.go new file mode 100644 index 00000000000..043087a2af1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/go-xorm/core/mapper_test.go @@ -0,0 +1,45 @@ +package core + +import ( + "testing" +) + +func TestGonicMapperFromObj(t *testing.T) { + testCases := map[string]string{ + "HTTPLib": "http_lib", + "id": "id", + "ID": "id", + "IDa": "i_da", + "iDa": "i_da", + "IDAa": "id_aa", + "aID": "a_id", + "aaID": "aa_id", + "aaaID": "aaa_id", + "MyREalFunkYLONgNAME": "my_r_eal_funk_ylo_ng_name", + } + + for in, expected := range testCases { + out := gonicCasedName(in) + if out != expected { + t.Errorf("Given %s, expected %s but got %s", in, expected, out) + } + } +} + +func TestGonicMapperToObj(t *testing.T) { + testCases := map[string]string{ + "http_lib": "HTTPLib", + "id": "ID", + "ida": "Ida", + "id_aa": "IDAa", + "aa_id": "AaID", + "my_r_eal_funk_ylo_ng_name": "MyREalFunkYloNgName", + } + + for in, expected := range testCases { + out := LintGonicMapper.Table2Obj(in) + if out != expected { + t.Errorf("Given %s, expected %s but got %s", in, expected, out) + } + } +} diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/pk.go b/Godeps/_workspace/src/github.com/go-xorm/core/pk.go index 61d1371e67c..1810dd944be 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/pk.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/pk.go @@ -1,7 +1,8 @@ package core import ( - "encoding/json" + "bytes" + "encoding/gob" ) type PK []interface{} @@ -12,14 +13,14 @@ func NewPK(pks ...interface{}) *PK { } func (p *PK) ToString() (string, error) { - bs, err := json.Marshal(*p) - if err != nil { - return "", nil - } - - return string(bs), nil + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + err := enc.Encode(*p) + return buf.String(), err } func (p *PK) FromString(content string) error { - return json.Unmarshal([]byte(content), p) + dec := gob.NewDecoder(bytes.NewBufferString(content)) + err := dec.Decode(p) + return err } diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/pk_test.go b/Godeps/_workspace/src/github.com/go-xorm/core/pk_test.go index 5245e574800..05486086e6a 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/pk_test.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/pk_test.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "reflect" "testing" ) @@ -19,4 +20,14 @@ func TestPK(t *testing.T) { t.Error(err) } fmt.Println(s) + + if len(*p) != len(*s) { + t.Fatal("p", *p, "should be equal", *s) + } + + for i, ori := range *p { + if ori != (*s)[i] { + t.Fatal("ori", ori, reflect.ValueOf(ori), "should be equal", (*s)[i], reflect.ValueOf((*s)[i])) + } + } } diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/table.go b/Godeps/_workspace/src/github.com/go-xorm/core/table.go index f7c9d464842..aba1f96e748 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/table.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/table.go @@ -65,13 +65,18 @@ func (table *Table) GetColumnIdx(name string, idx int) *Column { // if has primary key, return column func (table *Table) PKColumns() []*Column { - columns := make([]*Column, 0) - for _, name := range table.PrimaryKeys { - columns = append(columns, table.GetColumn(name)) + columns := make([]*Column, len(table.PrimaryKeys)) + for i, name := range table.PrimaryKeys { + columns[i] = table.GetColumn(name) } return columns } +func (table *Table) ColumnType(name string) reflect.Type { + t, _ := table.Type.FieldByName(name) + return t.Type +} + func (table *Table) AutoIncrColumn() *Column { return table.GetColumn(table.AutoIncrement) } diff --git a/Godeps/_workspace/src/github.com/go-xorm/core/type.go b/Godeps/_workspace/src/github.com/go-xorm/core/type.go index ee7656824c1..73b9921ee63 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/core/type.go +++ b/Godeps/_workspace/src/github.com/go-xorm/core/type.go @@ -70,6 +70,7 @@ var ( NVarchar = "NVARCHAR" TinyText = "TINYTEXT" Text = "TEXT" + Clob = "CLOB" MediumText = "MEDIUMTEXT" LongText = "LONGTEXT" Uuid = "UUID" @@ -120,6 +121,7 @@ var ( MediumText: TEXT_TYPE, LongText: TEXT_TYPE, Uuid: TEXT_TYPE, + Clob: TEXT_TYPE, Date: TIME_TYPE, DateTime: TIME_TYPE, @@ -250,7 +252,7 @@ func Type2SQLType(t reflect.Type) (st SQLType) { case reflect.String: st = SQLType{Varchar, 255, 0} case reflect.Struct: - if t == reflect.TypeOf(c_TIME_DEFAULT) { + if t.ConvertibleTo(reflect.TypeOf(c_TIME_DEFAULT)) { st = SQLType{DateTime, 0, 0} } else { // TODO need to handle association struct @@ -303,7 +305,7 @@ func SQLType2Type(st SQLType) reflect.Type { return reflect.TypeOf(float32(1)) case Double: return reflect.TypeOf(float64(1)) - case Char, Varchar, NVarchar, TinyText, Text, MediumText, LongText, Enum, Set, Uuid: + case Char, Varchar, NVarchar, TinyText, Text, MediumText, LongText, Enum, Set, Uuid, Clob: return reflect.TypeOf("") case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary: return reflect.TypeOf([]byte{}) diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/README.md b/Godeps/_workspace/src/github.com/go-xorm/xorm/README.md index 158f5c11dda..fe8aca3c374 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/README.md +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/README.md @@ -82,11 +82,13 @@ Or # Cases +* [Wego](http://github.com/go-tango/wego) + * [Docker.cn](https://docker.cn/) * [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) -* [Gorevel](http://http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel) +* [Gorevel](http://gorevel.cn/) - [github.com/goofcc/gorevel](http://github.com/goofcc/gorevel) * [Gowalker](http://gowalker.org) - [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md b/Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md index 5def1c38a57..5a167f9b148 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/README_CN.md @@ -44,16 +44,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 更新日志 -* **v0.4.0 RC1** - 新特性: - * 移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) - * 在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) - * 移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) - - 改进: - * Prepared statement 缓存 - * 添加 Incr API - * 指定时区位置 +* **v0.4.2** + 新特性: + * deleted标记 + * bug fixed [更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16) @@ -78,6 +72,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 案例 +* [Wego](http://github.com/go-tango/wego) + * [Docker.cn](https://docker.cn/) * [Gogs](http://try.gogits.org) - [github.com/gogits/gogs](http://github.com/gogits/gogs) diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION b/Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION index af81cfc7d96..4e64e0e9e47 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/VERSION @@ -1 +1 @@ -xorm v0.4.1 +xorm v0.4.2.0225 diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go index adc1d2d54e1..722088ca775 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/doc.go @@ -63,21 +63,22 @@ There are 7 major ORM methods and many helpful methods to use to operate databas // SELECT * FROM user 4. Query multiple records and record by record handle, there two methods, one is Iterate, -another is Raws +another is Rows err := engine.Iterate(...) // SELECT * FROM user - raws, err := engine.Raws(...) + rows, err := engine.Rows(...) // SELECT * FROM user + defer rows.Close() bean := new(Struct) - for raws.Next() { - err = raws.Scan(bean) + for rows.Next() { + err = rows.Scan(bean) } 5. Update one or more records - affected, err := engine.Update(&user) + affected, err := engine.Id(...).Update(&user) // UPDATE user SET ... 6. Delete one or more records, Delete MUST has conditon @@ -150,6 +151,6 @@ Attention: the above 7 methods should be the last chainable method. engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find() //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id -More usage, please visit https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md +More usage, please visit http://xorm.io/docs */ package xorm diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go index 700f46a1606..8f0c805dc34 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/engine.go @@ -344,7 +344,7 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { if col := table.GetColumn(name); col != nil { col.Indexes[index.Name] = true } else { - return nil, fmt.Errorf("Unknown col "+name+" in indexes %v", index) + return nil, fmt.Errorf("Unknown col "+name+" in indexes %v of table", index, table.ColumnsSeq()) } } } @@ -352,6 +352,9 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { return tables, nil } +/* +dump database all table structs and data to a file +*/ func (engine *Engine) DumpAllToFile(fp string) error { f, err := os.Create(fp) if err != nil { @@ -361,6 +364,9 @@ func (engine *Engine) DumpAllToFile(fp string) error { return engine.DumpAll(f) } +/* +dump database all table structs and data to w +*/ func (engine *Engine) DumpAll(w io.Writer) error { tables, err := engine.DBMetas() if err != nil { @@ -558,6 +564,13 @@ func (engine *Engine) Decr(column string, arg ...interface{}) *Session { return session.Decr(column, arg...) } +// Method SetExpr provides a update string like "column = {expression}" +func (engine *Engine) SetExpr(column string, expression string) *Session { + session := engine.NewSession() + session.IsAutoClose = true + return session.SetExpr(column, expression) +} + // Temporarily change the Get, Find, Update's table func (engine *Engine) Table(tableNameOrBean interface{}) *Session { session := engine.NewSession() @@ -766,7 +779,12 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { col.IsPrimaryKey = true col.Nullable = false case k == "NULL": - col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") + if j == 0 { + col.Nullable = true + } else { + col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") + } + // TODO: for postgres how add autoincr? /*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"): col.IsAutoIncrement = true @@ -915,7 +933,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { table.AddColumn(col) - if fieldType.Kind() == reflect.Int64 && (col.FieldName == "Id" || strings.HasSuffix(col.FieldName, ".Id")) { + if fieldType.Kind() == reflect.Int64 && (strings.ToUpper(col.FieldName) == "ID" || strings.HasSuffix(strings.ToUpper(col.FieldName), ".ID")) { idFieldColName = col.Name } } // end for @@ -959,40 +977,25 @@ func (engine *Engine) mapping(beans ...interface{}) (e error) { // If a table has any reocrd func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) { - v := rValue(bean) - t := v.Type() - if t.Kind() != reflect.Struct { - return false, errors.New("bean should be a struct or struct's point") - } - engine.autoMapType(v) session := engine.NewSession() defer session.Close() - rows, err := session.Count(bean) - return rows == 0, err + return session.IsTableEmpty(bean) } // If a table is exist -func (engine *Engine) IsTableExist(bean interface{}) (bool, error) { - v := rValue(bean) - var tableName string - if v.Type().Kind() == reflect.String { - tableName = bean.(string) - } else if v.Type().Kind() == reflect.Struct { - table := engine.autoMapType(v) - tableName = table.Name - } else { - return false, errors.New("bean should be a struct or struct's point") - } - +func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) { session := engine.NewSession() defer session.Close() - has, err := session.isTableExist(tableName) - return has, err + return session.IsTableExist(beanOrTableName) } func (engine *Engine) IdOf(bean interface{}) core.PK { - table := engine.TableInfo(bean) - v := reflect.Indirect(reflect.ValueOf(bean)) + return engine.IdOfV(reflect.ValueOf(bean)) +} + +func (engine *Engine) IdOfV(rv reflect.Value) core.PK { + v := reflect.Indirect(rv) + table := engine.autoMapType(v) pk := make([]interface{}, len(table.PrimaryKeys)) for i, col := range table.PKColumns() { pkField := v.FieldByName(col.FieldName) @@ -1109,7 +1112,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { session := engine.NewSession() session.Statement.RefTable = table defer session.Close() - isExist, err := session.isColumnExist(table.Name, col) + isExist, err := session.Engine.dialect.IsColumnExist(table.Name, col) if err != nil { return err } @@ -1222,8 +1225,9 @@ func (engine *Engine) CreateTables(beans ...interface{}) error { func (engine *Engine) DropTables(beans ...interface{}) error { session := engine.NewSession() - err := session.Begin() defer session.Close() + + err := session.Begin() if err != nil { return err } @@ -1258,13 +1262,6 @@ func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice [ return session.Query(sql, paramStr...) } -// Exec a raw sql and return records as []map[string]string -func (engine *Engine) Q(sql string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) { - session := engine.NewSession() - defer session.Close() - return session.Q(sql, paramStr...) -} - // Insert one or more records func (engine *Engine) Insert(beans ...interface{}) (int64, error) { session := engine.NewSession() @@ -1371,18 +1368,11 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) { scanner.Split(semiColSpliter) - session := engine.NewSession() - defer session.Close() - err := session.newDb() - if err != nil { - return results, err - } - for scanner.Scan() { query := scanner.Text() query = strings.Trim(query, " \t") if len(query) > 0 { - result, err := session.Db.Exec(query) + result, err := engine.DB().Exec(query) results = append(results, result) if err != nil { lastError = err @@ -1409,7 +1399,15 @@ func (engine *Engine) NowTime(sqlTypeName string) interface{} { return engine.FormatTime(sqlTypeName, t) } +func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) { + t := time.Now() + return engine.FormatTime(sqlTypeName, t), t +} + func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) { + if engine.dialect.DBType() == core.ORACLE { + return t + } switch sqlTypeName { case core.Time: s := engine.TZTime(t).Format("2006-01-02 15:04:05") //time.RFC3339 @@ -1419,6 +1417,8 @@ func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{} case core.DateTime, core.TimeStamp: if engine.dialect.DBType() == "ql" { v = engine.TZTime(t) + } else if engine.dialect.DBType() == "sqlite3" { + v = engine.TZTime(t).UTC().Format("2006-01-02 15:04:05") } else { v = engine.TZTime(t).Format("2006-01-02 15:04:05") } @@ -1430,6 +1430,8 @@ func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{} } else { v = engine.TZTime(t).Format(time.RFC3339Nano) } + case core.BigInt, core.Int: + v = engine.TZTime(t).Unix() default: v = engine.TZTime(t) } diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go index 4d20141cad3..7eaa1dd1c21 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/helpers.go @@ -11,6 +11,43 @@ import ( "github.com/go-xorm/core" ) +func isZero(k interface{}) bool { + switch k.(type) { + case int: + return k.(int) == 0 + case int8: + return k.(int8) == 0 + case int16: + return k.(int16) == 0 + case int32: + return k.(int32) == 0 + case int64: + return k.(int64) == 0 + case uint: + return k.(uint) == 0 + case uint8: + return k.(uint8) == 0 + case uint16: + return k.(uint16) == 0 + case uint32: + return k.(uint32) == 0 + case uint64: + return k.(uint64) == 0 + case string: + return k.(string) == "" + } + return false +} + +func isPKZero(pk core.PK) bool { + for _, k := range pk { + if isZero(k) { + return true + } + } + return false +} + func indexNoCase(s, sep string) int { return strings.Index(strings.ToLower(s), strings.ToLower(sep)) } @@ -163,3 +200,182 @@ func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { return resultsSlice, nil } + +func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { + result := make(map[string][]byte) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for ii, key := range fields { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) + //if row is null then ignore + if rawValue.Interface() == nil { + //fmt.Println("ignore ...", key, rawValue) + continue + } + + if data, err := value2Bytes(&rawValue); err == nil { + result[key] = data + } else { + return nil, err // !nashtsai! REVIEW, should return err or just error log? + } + } + return result, nil +} + +func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { + result := make(map[string]string) + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { + var scanResultContainer interface{} + scanResultContainers[i] = &scanResultContainer + } + if err := rows.Scan(scanResultContainers...); err != nil { + return nil, err + } + + for ii, key := range fields { + rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) + //if row is null then ignore + if rawValue.Interface() == nil { + //fmt.Println("ignore ...", key, rawValue) + continue + } + + if data, err := value2String(&rawValue); err == nil { + result[key] = data + } else { + return nil, err // !nashtsai! REVIEW, should return err or just error log? + } + } + return result, nil +} + +func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) { + rows, err := tx.Query(sqlStr, params...) + if err != nil { + return nil, err + } + defer rows.Close() + + return rows2Strings(rows) +} + +func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) { + s, err := db.Prepare(sqlStr) + if err != nil { + return nil, err + } + defer s.Close() + rows, err := s.Query(params...) + if err != nil { + return nil, err + } + defer rows.Close() + return rows2Strings(rows) +} + +func setColumnTime(bean interface{}, col *core.Column, t time.Time) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Struct: + v.Set(reflect.ValueOf(t).Convert(v.Type())) + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t.Unix()) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t.Unix())) + } + } +} + +func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { + colNames := make([]string, 0) + args := make([]interface{}, 0) + + for _, col := range table.Columns() { + lColName := strings.ToLower(col.Name) + if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { + if _, ok := session.Statement.columnMap[lColName]; !ok { + continue + } + } + if col.MapType == core.ONLYFROMDB { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + continue + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + } + } + + if col.IsDeleted { + continue + } + + if session.Statement.ColumnStr != "" { + if _, ok := session.Statement.columnMap[lColName]; !ok { + continue + } + } + if session.Statement.OmitStr != "" { + if _, ok := session.Statement.columnMap[lColName]; ok { + continue + } + } + + if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.Statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + if includeQuote { + colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?") + } else { + colNames = append(colNames, col.Name) + } + } + return colNames, args, nil +} diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go index ceb7c5de917..6fe50fc8c0d 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/mssql_dialect.go @@ -270,7 +270,7 @@ func (db *mssql) IsReserved(name string) bool { } func (db *mssql) Quote(name string) string { - return "[" + name + "]" + return "\"" + name + "\"" } func (db *mssql) QuoteStr() string { diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go index 4d32186b6ab..602cd0ecff7 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/mysql_dialect.go @@ -218,6 +218,9 @@ func (db *mysql) SqlType(c *core.Column) string { res += ")" case core.NVarchar: res = core.Varchar + case core.Uuid: + res = core.Varchar + c.Length = 40 default: res = t } @@ -317,7 +320,6 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column if err != nil { return nil, nil, err } - //fmt.Println(columnName, isNullable, colType, colKey, extra, colDefault) col.Name = strings.Trim(columnName, "` ") if "YES" == isNullable { col.Nullable = true @@ -467,15 +469,17 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { } colName = strings.Trim(colName, "` ") - + var isRegular bool if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { indexName = indexName[5+len(tableName) : len(indexName)] + isRegular = true } var index *core.Index var ok bool if index, ok = indexes[indexName]; !ok { index = new(core.Index) + index.IsRegular = isRegular index.Type = indexType index.Name = indexName indexes[indexName] = index diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go index f599dce93ed..be71c288d0c 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/oracle_dialect.go @@ -509,7 +509,7 @@ func (db *oracle) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial: - return "NUMBER" + res = "NUMBER" case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea: return core.Blob case core.Time, core.DateTime, core.TimeStamp: @@ -521,7 +521,7 @@ func (db *oracle) SqlType(c *core.Column) string { case core.Text, core.MediumText, core.LongText: res = "CLOB" case core.Char, core.Varchar, core.TinyText: - return "VARCHAR2" + res = "VARCHAR2" default: res = t } @@ -536,6 +536,10 @@ func (db *oracle) SqlType(c *core.Column) string { return res } +func (db *oracle) AutoIncrStr() string { + return "AUTO_INCREMENT" +} + func (db *oracle) SupportInsertMany() bool { return true } @@ -553,10 +557,6 @@ func (db *oracle) QuoteStr() string { return "\"" } -func (db *oracle) AutoIncrStr() string { - return "" -} - func (db *oracle) SupportEngine() bool { return false } @@ -565,19 +565,94 @@ func (db *oracle) SupportCharset() bool { return false } +func (db *oracle) SupportDropIfExists() bool { + return false +} + func (db *oracle) IndexOnTable() bool { return false } +func (db *oracle) DropTableSql(tableName string) string { + return fmt.Sprintf("DROP TABLE `%s`", tableName) +} + +func (b *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string { + var sql string + sql = "CREATE TABLE " + if tableName == "" { + tableName = table.Name + } + + sql += b.Quote(tableName) + " (" + + pkList := table.PrimaryKeys + + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + /*if col.IsPrimaryKey && len(pkList) == 1 { + sql += col.String(b.dialect) + } else {*/ + sql += col.StringNoPk(b) + //} + sql = strings.TrimSpace(sql) + sql += ", " + } + + if len(pkList) > 0 { + sql += "PRIMARY KEY ( " + sql += b.Quote(strings.Join(pkList, b.Quote(","))) + sql += " ), " + } + + sql = sql[:len(sql)-2] + ")" + if b.SupportEngine() && storeEngine != "" { + sql += " ENGINE=" + storeEngine + } + if b.SupportCharset() { + if len(charset) == 0 { + charset = b.URI().Charset + } + if len(charset) > 0 { + sql += " DEFAULT CHARSET " + charset + } + } + return sql +} + func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} + args := []interface{}{tableName, idxName} return `SELECT INDEX_NAME FROM USER_INDEXES ` + - `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args + `WHERE TABLE_NAME = :1 AND INDEX_NAME = :2`, args } func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName)} - return `SELECT table_name FROM user_tables WHERE table_name = ?`, args + args := []interface{}{tableName} + return `SELECT table_name FROM user_tables WHERE table_name = :1`, args +} + +func (db *oracle) MustDropTable(tableName string) error { + sql, args := db.TableCheckSql(tableName) + if db.Logger != nil { + db.Logger.Info("[sql]", sql, args) + } + + rows, err := db.DB().Query(sql, args...) + if err != nil { + return err + } + defer rows.Close() + + if !rows.Next() { + return nil + } + + sql = "Drop Table \"" + tableName + "\"" + if db.Logger != nil { + db.Logger.Info("[sql]", sql) + } + _, err = db.DB().Exec(sql) + return err } /*func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { @@ -587,9 +662,9 @@ func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { }*/ func (db *oracle) IsColumnExist(tableName string, col *core.Column) (bool, error) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(col.Name)} - query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + - " AND column_name = ?" + args := []interface{}{tableName, col.Name} + query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" + + " AND column_name = :2" rows, err := db.DB().Query(query, args...) if db.Logger != nil { db.Logger.Info("[sql]", query, args) @@ -606,7 +681,7 @@ func (db *oracle) IsColumnExist(tableName string, col *core.Column) (bool, error } func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { - args := []interface{}{strings.ToUpper(tableName)} + args := []interface{}{tableName} s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" @@ -625,7 +700,7 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum col := new(core.Column) col.Indexes = make(map[string]bool) - var colName, colDefault, nullable, dataType, dataPrecision, dataScale string + var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string var dataLen int err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, @@ -634,36 +709,66 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum return nil, nil, err } - col.Name = strings.Trim(colName, `" `) - col.Default = colDefault + col.Name = strings.Trim(*colName, `" `) + if colDefault != nil { + col.Default = *colDefault + col.DefaultIsEmpty = false + } - if nullable == "Y" { + if *nullable == "Y" { col.Nullable = true } else { col.Nullable = false } - switch dataType { + var ignore bool + + var dt string + var len1, len2 int + dts := strings.Split(*dataType, "(") + dt = dts[0] + if len(dts) > 1 { + lens := strings.Split(dts[1][:len(dts[1])-1], ",") + if len(lens) > 1 { + len1, _ = strconv.Atoi(lens[0]) + len2, _ = strconv.Atoi(lens[1]) + } else { + len1, _ = strconv.Atoi(lens[0]) + } + } + + switch dt { case "VARCHAR2": - col.SQLType = core.SQLType{core.Varchar, 0, 0} + col.SQLType = core.SQLType{core.Varchar, len1, len2} case "TIMESTAMP WITH TIME ZONE": col.SQLType = core.SQLType{core.TimeStampz, 0, 0} + case "NUMBER": + col.SQLType = core.SQLType{core.Double, len1, len2} + case "LONG", "LONG RAW": + col.SQLType = core.SQLType{core.Text, 0, 0} + case "RAW": + col.SQLType = core.SQLType{core.Binary, 0, 0} + case "ROWID": + col.SQLType = core.SQLType{core.Varchar, 18, 0} + case "AQ$_SUBSCRIBERS": + ignore = true default: - col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0} + col.SQLType = core.SQLType{strings.ToUpper(dt), len1, len2} } + + if ignore { + continue + } + if _, ok := core.SqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) + return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v %v", *dataType, col.SQLType)) } col.Length = dataLen if col.SQLType.IsText() || col.SQLType.IsTime() { - if col.Default != "" { + if !col.DefaultIsEmpty { col.Default = "'" + col.Default + "'" - } else { - if col.DefaultIsEmpty { - col.Default = "''" - } } } cols[col.Name] = col diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go index c566b125bbc..0def55757c8 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/rows.go @@ -25,11 +25,6 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { rows.session = session rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type() - err := rows.session.newDb() - if err != nil { - return nil, err - } - defer rows.session.Statement.Init() var sqlStr string @@ -47,8 +42,8 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { } rows.session.Engine.logSQL(sqlStr, args) - - rows.stmt, err = rows.session.Db.Prepare(sqlStr) + var err error + rows.stmt, err = rows.session.DB().Prepare(sqlStr) if err != nil { rows.lastError = err defer rows.Close() diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/session.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/session.go index e5ec7fd0701..0d11d99fd0f 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/session.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/session.go @@ -17,7 +17,7 @@ import ( // Struct Session keep a pointer to sql.DB and provides all execution of all // kind of database operations. type Session struct { - Db *core.DB + db *core.DB Engine *Engine Tx *core.Tx Statement Statement @@ -66,9 +66,9 @@ func (session *Session) Close() { v.Close() } - if session.Db != nil { + if session.db != nil { //session.Engine.Pool.ReleaseDB(session.Engine, session.Db) - session.Db = nil + session.db = nil session.Tx = nil session.stmtCache = nil session.Init() @@ -158,6 +158,12 @@ func (session *Session) Decr(column string, arg ...interface{}) *Session { return session } +// Method SetExpr provides a query string like "column = {expression}" +func (session *Session) SetExpr(column string, expression string) *Session { + session.Statement.SetExpr(column, expression) + return session +} + // Method Cols provides some columns to special func (session *Session) Cols(columns ...string) *Session { session.Statement.Cols(columns...) @@ -280,26 +286,18 @@ func (session *Session) Having(conditions string) *Session { return session } -func (session *Session) newDb() error { - if session.Db == nil { - /*db, err := session.Engine.Pool.RetrieveDB(session.Engine) - if err != nil { - return err - }*/ - session.Db = session.Engine.db +func (session *Session) DB() *core.DB { + if session.db == nil { + session.db = session.Engine.db session.stmtCache = make(map[uint32]*core.Stmt, 0) } - return nil + return session.db } // Begin a transaction func (session *Session) Begin() error { - err := session.newDb() - if err != nil { - return err - } if session.IsAutoCommit { - tx, err := session.Db.Begin() + tx, err := session.DB().Begin() if err != nil { return err } @@ -450,6 +448,13 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er return session.Engine.LogSQLExecutionTime(sqlStr, args, func() (sql.Result, error) { if session.IsAutoCommit { + //oci8 can not auto commit (github.com/mattn/go-oci8) + if session.Engine.dialect.DBType() == core.ORACLE { + session.Begin() + r, err := session.Tx.Exec(sqlStr, args...) + session.Commit() + return r, err + } return session.innerExec(sqlStr, args...) } return session.Tx.Exec(sqlStr, args...) @@ -458,10 +463,6 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er // Exec raw sql func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { - err := session.newDb() - if err != nil { - return nil, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -474,10 +475,6 @@ func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, er func (session *Session) CreateTable(bean interface{}) error { session.Statement.RefTable = session.Engine.TableInfo(bean) - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -490,10 +487,6 @@ func (session *Session) CreateTable(bean interface{}) error { func (session *Session) CreateIndexes(bean interface{}) error { session.Statement.RefTable = session.Engine.TableInfo(bean) - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -501,7 +494,7 @@ func (session *Session) CreateIndexes(bean interface{}) error { sqls := session.Statement.genIndexSQL() for _, sqlStr := range sqls { - _, err = session.exec(sqlStr) + _, err := session.exec(sqlStr) if err != nil { return err } @@ -513,10 +506,6 @@ func (session *Session) CreateIndexes(bean interface{}) error { func (session *Session) CreateUniques(bean interface{}) error { session.Statement.RefTable = session.Engine.TableInfo(bean) - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -524,7 +513,7 @@ func (session *Session) CreateUniques(bean interface{}) error { sqls := session.Statement.genUniqueSQL() for _, sqlStr := range sqls { - _, err = session.exec(sqlStr) + _, err := session.exec(sqlStr) if err != nil { return err } @@ -541,10 +530,6 @@ func (session *Session) createOneTable() error { // to be deleted func (session *Session) createAll() error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -562,10 +547,6 @@ func (session *Session) createAll() error { // drop indexes func (session *Session) DropIndexes(bean interface{}) error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -573,7 +554,7 @@ func (session *Session) DropIndexes(bean interface{}) error { sqls := session.Statement.genDelIndexSQL() for _, sqlStr := range sqls { - _, err = session.exec(sqlStr) + _, err := session.exec(sqlStr) if err != nil { return err } @@ -581,31 +562,29 @@ func (session *Session) DropIndexes(bean interface{}) error { return nil } -// DropTable drop a table and all indexes of the table -func (session *Session) DropTable(bean interface{}) error { - err := session.newDb() +// drop table will drop table if exist, if drop failed, it will return error +func (session *Session) DropTable(beanOrTableName interface{}) error { + tableName, err := session.Engine.tableName(beanOrTableName) if err != nil { return err } - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() + var needDrop = true + if !session.Engine.dialect.SupportDropIfExists() { + sqlStr, args := session.Engine.dialect.TableCheckSql(tableName) + results, err := session.query(sqlStr, args...) + if err != nil { + return err + } + needDrop = len(results) > 0 } - t := reflect.Indirect(reflect.ValueOf(bean)).Type() - defer session.resetStatement() - if t.Kind() == reflect.String { - session.Statement.AltTableName = bean.(string) - } else if t.Kind() == reflect.Struct { - session.Statement.RefTable = session.Engine.TableInfo(bean) - } else { - return errors.New("Unsupported type") + if needDrop { + sqlStr := session.Engine.Dialect().DropTableSql(tableName) + _, err = session.exec(sqlStr) + return err } - - sqlStr := session.Statement.genDropSQL() - _, err = session.exec(sqlStr) - return err + return nil } func (statement *Statement) JoinColumns(cols []*core.Column) string { @@ -642,11 +621,6 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, ErrCacheFailed } - // TODO: remove this after support multi pk cache - /*if len(session.Statement.RefTable.PrimaryKeys) != 1 { - return false, ErrCacheFailed - }*/ - for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -662,7 +636,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf table := session.Statement.RefTable if err != nil { var res = make([]string, len(table.PrimaryKeys)) - rows, err := session.Db.Query(newsql, args...) + rows, err := session.DB().Query(newsql, args...) if err != nil { return false, err } @@ -747,11 +721,6 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } - // TODO: remove this after multi pk supported - /*if len(session.Statement.RefTable.PrimaryKeys) != 1 { - return ErrCacheFailed - }*/ - for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -765,7 +734,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in cacher := session.Engine.getCacher2(table) ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args) if err != nil { - rows, err := session.Db.Query(newsql, args...) + rows, err := session.DB().Query(newsql, args...) if err != nil { return err } @@ -909,32 +878,27 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean)))) } } else if sliceValue.Kind() == reflect.Map { - var key core.PK - if table.PrimaryKeys[0] != "" { - key = ids[j] - } - + var key core.PK = ids[j] + keyType := sliceValue.Type().Key() + var ikey interface{} if len(key) == 1 { - ikey, err := strconv.ParseInt(fmt.Sprintf("%v", key[0]), 10, 64) + ikey, err = Atot(fmt.Sprintf("%v", key[0]), keyType) if err != nil { return err } - if t.Kind() == reflect.Ptr { - sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean)) - } else { - sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean))) - } } else { - return errors.New("table have multiple primary keys") + if keyType.Kind() != reflect.Slice { + return errors.New("table have multiple primary keys, key is not core.PK or slice") + } + ikey = key + } + + if t.Kind() == reflect.Ptr { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean)) + } else { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean))) } } - /*} else { - session.Engine.LogDebug("[xorm:cacheFind] cache delete:", tableName, ides[j]) - cacher.DelBean(tableName, ids[j]) - - session.Engine.LogDebug("[xorm:cacheFind] cache clear:", tableName) - cacher.ClearIds(tableName) - }*/ } return nil @@ -981,7 +945,7 @@ func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { var has bool stmt, has = session.stmtCache[crc] if !has { - stmt, err = session.Db.Prepare(sqlStr) + stmt, err = session.DB().Prepare(sqlStr) if err != nil { return nil, err } @@ -993,11 +957,6 @@ func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { // get retrieve one record from database, bean's non-empty fields // will be as conditions func (session *Session) Get(bean interface{}) (bool, error) { - err := session.newDb() - if err != nil { - return false, err - } - defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1030,6 +989,7 @@ func (session *Session) Get(bean interface{}) (bool, error) { } var rawRows *core.Rows + var err error session.queryPreprocess(&sqlStr, args...) if session.IsAutoCommit { stmt, err := session.doPrepare(sqlStr) @@ -1059,11 +1019,6 @@ func (session *Session) Get(bean interface{}) (bool, error) { // Count counts the records. bean's non-empty fields // are conditions. func (session *Session) Count(bean interface{}) (int64, error) { - err := session.newDb() - if err != nil { - return 0, err - } - defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1095,14 +1050,79 @@ func (session *Session) Count(bean interface{}) (int64, error) { return int64(total), err } +func Atot(s string, tp reflect.Type) (interface{}, error) { + var err error + var result interface{} + switch tp.Kind() { + case reflect.Int: + result, err = strconv.Atoi(s) + if err != nil { + return nil, errors.New("convert " + s + " as int: " + err.Error()) + } + case reflect.Int8: + x, err := strconv.Atoi(s) + if err != nil { + return nil, errors.New("convert " + s + " as int16: " + err.Error()) + } + result = int8(x) + case reflect.Int16: + x, err := strconv.Atoi(s) + if err != nil { + return nil, errors.New("convert " + s + " as int16: " + err.Error()) + } + result = int16(x) + case reflect.Int32: + x, err := strconv.Atoi(s) + if err != nil { + return nil, errors.New("convert " + s + " as int32: " + err.Error()) + } + result = int32(x) + case reflect.Int64: + result, err = strconv.ParseInt(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as int64: " + err.Error()) + } + case reflect.Uint: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as uint: " + err.Error()) + } + result = uint(x) + case reflect.Uint8: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as uint8: " + err.Error()) + } + result = uint8(x) + case reflect.Uint16: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as uint16: " + err.Error()) + } + result = uint16(x) + case reflect.Uint32: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as uint32: " + err.Error()) + } + result = uint32(x) + case reflect.Uint64: + result, err = strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, errors.New("convert " + s + " as uint64: " + err.Error()) + } + case reflect.String: + result = s + default: + panic("unsupported convert type") + } + return result, nil +} + // Find retrieve records from table, condiBeans's non-empty fields // are conditions. beans could be []Struct, []*Struct, map[int64]Struct // map[int64]*Struct func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1143,10 +1163,9 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } else { // !oinume! Add " IS NULL" to WHERE whatever condiBean is given. // See https://github.com/go-xorm/xorm/issues/179 - for _, col := range table.Columns() { - if col.IsDeleted && !session.Statement.unscoped { // tag "deleted" is enabled - session.Statement.ConditionStr = fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00') ", session.Engine.Quote(col.Name), session.Engine.Quote(col.Name)) - } + if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled + session.Statement.ConditionStr = fmt.Sprintf("(%v IS NULL or %v = '0001-01-01 00:00:00') ", + session.Engine.Quote(col.Name), session.Engine.Quote(col.Name)) } } @@ -1156,11 +1175,19 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) var columnStr string = session.Statement.ColumnStr if session.Statement.JoinStr == "" { if columnStr == "" { - columnStr = session.Statement.genColumnStr() + if session.Statement.GroupByStr != "" { + columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) + } else { + columnStr = session.Statement.genColumnStr() + } } } else { if columnStr == "" { - columnStr = "*" + if session.Statement.GroupByStr != "" { + columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) + } else { + columnStr = "*" + } } } @@ -1178,6 +1205,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) args = session.Statement.RawParams } + var err error if session.Statement.JoinStr == "" { if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache && @@ -1259,7 +1287,9 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return err } - for i, results := range resultsSlice { + keyType := sliceValue.Type().Key() + + for _, results := range resultsSlice { var newValue reflect.Value if sliceElementType.Kind() == reflect.Ptr { newValue = reflect.New(sliceElementType.Elem()) @@ -1270,18 +1300,29 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) if err != nil { return err } - var key int64 + var key interface{} // if there is only one pk, we can put the id as map key. - // TODO: should know if the column is ints if len(table.PrimaryKeys) == 1 { - x, err := strconv.ParseInt(string(results[table.PrimaryKeys[0]]), 10, 64) + key, err = Atot(string(results[table.PrimaryKeys[0]]), keyType) if err != nil { - return errors.New("pk " + table.PrimaryKeys[0] + " as int64: " + err.Error()) + return err } - key = x } else { - key = int64(i) + if keyType.Kind() != reflect.Slice { + panic("don't support multiple primary key's map has non-slice key type") + } else { + keys := core.PK{} + for _, pk := range table.PrimaryKeys { + skey, err := Atot(string(results[pk]), keyType) + if err != nil { + return err + } + keys = append(keys, skey) + } + key = keys + } } + if sliceElementType.Kind() == reflect.Ptr { sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValue.Interface())) } else { @@ -1308,23 +1349,16 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) // Test if database is ok func (session *Session) Ping() error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() } - return session.Db.Ping() + return session.DB().Ping() } +/* func (session *Session) isColumnExist(tableName string, col *core.Column) (bool, error) { - err := session.newDb() - if err != nil { - return false, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1333,13 +1367,29 @@ func (session *Session) isColumnExist(tableName string, col *core.Column) (bool, //sqlStr, args := session.Engine.dialect.ColumnCheckSql(tableName, colName) //results, err := session.query(sqlStr, args...) //return len(results) > 0, err +}*/ + +func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) { + v := rValue(beanOrTableName) + if v.Type().Kind() == reflect.String { + return beanOrTableName.(string), nil + } else if v.Type().Kind() == reflect.Struct { + table := engine.autoMapType(v) + return table.Name, nil + } + return "", errors.New("bean should be a struct or struct's point") } -func (session *Session) isTableExist(tableName string) (bool, error) { - err := session.newDb() +func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) { + tableName, err := session.Engine.tableName(beanOrTableName) if err != nil { return false, err } + + return session.isTableExist(tableName) +} + +func (session *Session) isTableExist(tableName string) (bool, error) { defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1349,11 +1399,38 @@ func (session *Session) isTableExist(tableName string) (bool, error) { return len(results) > 0, err } -func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) { - err := session.newDb() - if err != nil { - return false, err +func (session *Session) IsTableEmpty(bean interface{}) (bool, error) { + v := rValue(bean) + t := v.Type() + + if t.Kind() == reflect.String { + return session.isTableEmpty(bean.(string)) + } else if t.Kind() == reflect.Struct { + session.Engine.autoMapType(v) + rows, err := session.Count(bean) + return rows == 0, err } + return false, errors.New("bean should be a struct or struct's point") +} + +func (session *Session) isTableEmpty(tableName string) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var total int64 + sql := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName)) + err := session.DB().QueryRow(sql).Scan(&total) + session.Engine.logSQL(sql) + if err != nil { + return true, err + } + + return total == 0, nil +} + +func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) { defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1371,6 +1448,11 @@ func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bo // find if index is exist according cols func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + indexes, err := session.Engine.dialect.GetIndexes(tableName) if err != nil { return false, err @@ -1389,10 +1471,6 @@ func (session *Session) isIndexExist2(tableName string, cols []string, unique bo } func (session *Session) addColumn(colName string) error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1400,47 +1478,35 @@ func (session *Session) addColumn(colName string) error { col := session.Statement.RefTable.GetColumn(colName) sql, args := session.Statement.genAddColumnStr(col) - _, err = session.exec(sql, args...) + _, err := session.exec(sql, args...) return err } func (session *Session) addIndex(tableName, idxName string) error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() } index := session.Statement.RefTable.Indexes[idxName] sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) - //genAddIndexStr(indexName(tableName, idxName), cols) - _, err = session.exec(sqlStr) + + _, err := session.exec(sqlStr) return err } func (session *Session) addUnique(tableName, uqeName string) error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() } index := session.Statement.RefTable.Indexes[uqeName] sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) - _, err = session.exec(sqlStr) + _, err := session.exec(sqlStr) return err } // To be deleted func (session *Session) dropAll() error { - err := session.newDb() - if err != nil { - return err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1449,7 +1515,7 @@ func (session *Session) dropAll() error { for _, table := range session.Engine.Tables { session.Statement.Init() session.Statement.RefTable = table - sqlStr := session.Statement.genDropSQL() + sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName()) _, err := session.exec(sqlStr) if err != nil { return err @@ -1458,62 +1524,6 @@ func (session *Session) dropAll() error { return nil } -func row2mapStr(rows *core.Rows, fields []string) (resultsMap map[string]string, err error) { - result := make(map[string]string) - scanResultContainers := make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer - } - if err := rows.Scan(scanResultContainers...); err != nil { - return nil, err - } - - for ii, key := range fields { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) - //if row is null then ignore - if rawValue.Interface() == nil { - //fmt.Println("ignore ...", key, rawValue) - continue - } - - if data, err := value2String(&rawValue); err == nil { - result[key] = data - } else { - return nil, err // !nashtsai! REVIEW, should return err or just error log? - } - } - return result, nil -} - -func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { - result := make(map[string][]byte) - scanResultContainers := make([]interface{}, len(fields)) - for i := 0; i < len(fields); i++ { - var scanResultContainer interface{} - scanResultContainers[i] = &scanResultContainer - } - if err := rows.Scan(scanResultContainers...); err != nil { - return nil, err - } - - for ii, key := range fields { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) - //if row is null then ignore - if rawValue.Interface() == nil { - //fmt.Println("ignore ...", key, rawValue) - continue - } - - if data, err := value2Bytes(&rawValue); err == nil { - result[key] = data - } else { - return nil, err // !nashtsai! REVIEW, should return err or just error log? - } - } - return result, nil -} - func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value { var col *core.Column if col = table.GetColumnIdx(key, idx); col == nil { @@ -1566,7 +1576,6 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i } func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) error { - scanResults := make([]interface{}, fieldsCount) for i := 0; i < len(fields); i++ { var cell interface{} @@ -1686,42 +1695,73 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount fieldValue.SetUint(uint64(vv.Int())) } case reflect.Struct: - if fieldType == core.TimeType { + if fieldType.ConvertibleTo(core.TimeType) { if rawValueType == core.TimeType { hasAssigned = true - t := vv.Interface().(time.Time) + t := vv.Convert(core.TimeType).Interface().(time.Time) z, _ := t.Zone() if len(z) == 0 || t.Year() == 0 { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location session.Engine.LogDebug("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) - tt := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.Local) - vv = reflect.ValueOf(tt) } // !nashtsai! convert to engine location - t = vv.Interface().(time.Time).In(session.Engine.TZLocation) - vv = reflect.ValueOf(t) - fieldValue.Set(vv) + t = t.In(session.Engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) // t = fieldValue.Interface().(time.Time) // z, _ = t.Zone() // session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + } else if rawValueType == core.IntType || rawValueType == core.Int64Type || + rawValueType == core.Int32Type { + hasAssigned = true + t := time.Unix(vv.Int(), 0).In(session.Engine.TZLocation) + vv = reflect.ValueOf(t) + fieldValue.Set(vv) } } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { - var x int64 - if rawValueType.Kind() == reflect.Int64 { - x = vv.Int() + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + switch rawValueType.Kind() { + case reflect.Int64: + pk[0] = vv.Int() + case reflect.Int: + pk[0] = int(vv.Int()) + case reflect.Int32: + pk[0] = int32(vv.Int()) + case reflect.Int16: + pk[0] = int16(vv.Int()) + case reflect.Int8: + pk[0] = int8(vv.Int()) + case reflect.Uint64: + pk[0] = vv.Uint() + case reflect.Uint: + pk[0] = uint(vv.Uint()) + case reflect.Uint32: + pk[0] = uint32(vv.Uint()) + case reflect.Uint16: + pk[0] = uint16(vv.Uint()) + case reflect.Uint8: + pk[0] = uint8(vv.Uint()) + case reflect.String: + pk[0] = vv.String() + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily structInter := reflect.New(fieldValue.Type()) newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } @@ -1882,7 +1922,7 @@ func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSl session.queryPreprocess(&sqlStr, paramStr...) if session.IsAutoCommit { - return session.innerQuery(session.Db, sqlStr, paramStr...) + return session.innerQuery(session.DB(), sqlStr, paramStr...) } return session.txQuery(session.Tx, sqlStr, paramStr...) } @@ -1898,7 +1938,6 @@ func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{ } func (session *Session) innerQuery(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { - stmt, rows, err := session.Engine.LogSQLQueryTime(sqlStr, params, func() (*core.Stmt, *core.Rows, error) { stmt, err := db.Prepare(sqlStr) if err != nil { @@ -1922,10 +1961,6 @@ func (session *Session) innerQuery(db *core.DB, sqlStr string, params ...interfa // Exec a raw sql and return records as []map[string][]byte func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - err = session.newDb() - if err != nil { - return nil, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1941,56 +1976,15 @@ func (session *Session) query2(sqlStr string, paramStr ...interface{}) (resultsS session.queryPreprocess(&sqlStr, paramStr...) if session.IsAutoCommit { - return query2(session.Db, sqlStr, paramStr...) + return query2(session.DB(), sqlStr, paramStr...) } return txQuery2(session.Tx, sqlStr, paramStr...) } -func txQuery2(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) { - rows, err := tx.Query(sqlStr, params...) - if err != nil { - return nil, err - } - defer rows.Close() - - return rows2Strings(rows) -} - -func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string]string, err error) { - s, err := db.Prepare(sqlStr) - if err != nil { - return nil, err - } - defer s.Close() - rows, err := s.Query(params...) - if err != nil { - return nil, err - } - defer rows.Close() - return rows2Strings(rows) -} - -// Exec a raw sql and return records as []map[string]string -func (session *Session) Q(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) { - err = session.newDb() - if err != nil { - return nil, err - } - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - return session.query2(sqlStr, paramStr...) -} - // insert one or more beans func (session *Session) Insert(beans ...interface{}) (int64, error) { var affected int64 = 0 - var err error = nil - err = session.newDb() - if err != nil { - return 0, err - } + var err error defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -1999,20 +1993,22 @@ func (session *Session) Insert(beans ...interface{}) (int64, error) { for _, bean := range beans { sliceValue := reflect.Indirect(reflect.ValueOf(bean)) if sliceValue.Kind() == reflect.Slice { - if session.Engine.SupportInsertMany() { - cnt, err := session.innerInsertMulti(bean) - if err != nil { - return affected, err - } - affected += cnt - } else { - size := sliceValue.Len() - for i := 0; i < size; i++ { - cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) + size := sliceValue.Len() + if size > 0 { + if session.Engine.SupportInsertMany() { + cnt, err := session.innerInsertMulti(bean) if err != nil { return affected, err } affected += cnt + } else { + for i := 0; i < size; i++ { + cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) + if err != nil { + return affected, err + } + affected += cnt + } } } } else { @@ -2071,13 +2067,28 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.MapType == core.ONLYFROMDB { continue } + if col.IsDeleted { + continue + } if session.Statement.ColumnStr != "" { if _, ok := session.Statement.columnMap[col.Name]; !ok { continue } } + if session.Statement.OmitStr != "" { + if _, ok := session.Statement.columnMap[col.Name]; ok { + continue + } + } if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - args = append(args, session.Engine.NowTime(col.SQLType.Name)) + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) } else { arg, err := session.value2Interface(col, fieldValue) if err != nil { @@ -2099,13 +2110,28 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.MapType == core.ONLYFROMDB { continue } + if col.IsDeleted { + continue + } if session.Statement.ColumnStr != "" { if _, ok := session.Statement.columnMap[col.Name]; !ok { continue } } + if session.Statement.OmitStr != "" { + if _, ok := session.Statement.columnMap[col.Name]; ok { + continue + } + } if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - args = append(args, session.Engine.NowTime(col.SQLType.Name)) + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) } else { arg, err := session.value2Interface(col, fieldValue) if err != nil { @@ -2173,16 +2199,20 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error // Insert multiple records func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { - err := session.newDb() - if err != nil { - return 0, err + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() == reflect.Slice { + if sliceValue.Len() > 0 { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + return session.innerInsertMulti(rowsSlicePtr) + } else { + return 0, nil + } + } else { + return 0, ErrParamsType } - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.innerInsertMulti(rowsSlicePtr) } func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) { @@ -2352,28 +2382,96 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, fieldValue.SetUint(x) //Currently only support Time type case reflect.Struct: - if fieldType == core.TimeType { + if fieldType.ConvertibleTo(core.TimeType) { x, err := session.byte2Time(col, data) if err != nil { return err } v = x - fieldValue.Set(reflect.ValueOf(v)) + fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + switch rawValueType.Kind() { + case reflect.Int64: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Int: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int(x) + case reflect.Int32: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int32(x) + case reflect.Int16: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int16(x) + case reflect.Int8: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = int8(x) + case reflect.Uint64: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = x + case reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint(x) + case reflect.Uint32: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint32(x) + case reflect.Uint16: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint16(x) + case reflect.Uint8: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + pk[0] = uint8(x) + case reflect.String: + pk[0] = string(data) + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily structInter := reflect.New(fieldValue.Type()) newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } @@ -2637,17 +2735,95 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, structInter := reflect.New(fieldType.Elem()) table := session.Engine.autoMapType(structInter.Elem()) if table != nil { - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") } - if x != 0 { + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + switch rawValueType.Kind() { + case reflect.Int64: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Int: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int(x) + case reflect.Int32: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int32(x) + case reflect.Int16: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = int16(x) + case reflect.Int8: + x, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Uint64: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = x + case reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint(x) + case reflect.Uint32: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint32(x) + case reflect.Uint16: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint16(x) + case reflect.Uint8: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + + pk[0] = uint8(x) + case reflect.String: + pk[0] = string(data) + default: + panic("unsupported primary key type cascade") + } + + if !isPKZero(pk) { // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne // property to be fetched lazily newsession := session.Engine.NewSession() defer newsession.Close() - has, err := newsession.Id(x).Get(structInter.Interface()) + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { return err } @@ -2804,8 +2980,29 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return 0, err } - colPlaces := strings.Repeat("?, ", len(colNames)) - colPlaces = colPlaces[0 : len(colPlaces)-2] + // insert expr columns, override if exists + exprColumns := session.Statement.getExpr() + exprColVals := make([]string, 0, len(exprColumns)) + for _, v := range exprColumns { + // remove the expr columns + for i, colName := range colNames { + if colName == v.colName { + colNames = append(colNames[:i], colNames[i+1:]...) + args = append(args[:i], args[i+1:]...) + } + } + + // append expr column to the end + colNames = append(colNames, v.colName) + exprColVals = append(exprColVals, v.expr) + } + + colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns)) + if len(exprColVals) > 0 { + colPlaces = colPlaces + strings.Join(exprColVals, ", ") + } else { + colPlaces = colPlaces[0 : len(colPlaces)-2] + } sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)", session.Engine.QuoteStr(), @@ -2970,10 +3167,6 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { // The in parameter bean must a struct or a point to struct. The return // parameter is inserted and error func (session *Session) InsertOne(bean interface{}) (int64, error) { - err := session.newDb() - if err != nil { - return 0, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -3068,7 +3261,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { session.Engine.LogDebug("[cacheUpdate] get cache sql", newsql, args[nStart:]) ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) if err != nil { - rows, err := session.Db.Query(newsql, args[nStart:]...) + rows, err := session.DB().Query(newsql, args[nStart:]...) if err != nil { return err } @@ -3167,10 +3360,6 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { // You should call UseBool if you have bool to use. // 2.float32 & float64 may be not inexact as conditions func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) { - err := session.newDb() - if err != nil { - return 0, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -3192,6 +3381,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } // -- + var err error if t.Kind() == reflect.Struct { table = session.Engine.TableInfo(bean) session.Statement.RefTable = table @@ -3199,7 +3389,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if session.Statement.ColumnStr == "" { colNames, args = buildUpdates(session.Engine, table, bean, false, false, false, false, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap, true) + session.Statement.mustColumnMap, session.Statement.columnMap, true) } else { colNames, args, err = genCols(table, session, bean, true, true) if err != nil { @@ -3225,7 +3415,15 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if session.Statement.UseAutoTime && table.Updated != "" { colNames = append(colNames, session.Engine.Quote(table.Updated)+" = ?") - args = append(args, session.Engine.NowTime(table.UpdatedColumn().SQLType.Name)) + col := table.UpdatedColumn() + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) } //for update action to like "column = column + ?" @@ -3240,6 +3438,11 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" - ?") args = append(args, v.arg) } + //for update action to like "column = expression" + exprColumns := session.Statement.getExpr() + for _, v := range exprColumns { + colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr) + } var condiColNames []string var condiArgs []interface{} @@ -3289,6 +3492,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } + if st.LimitN > 0 { + condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) + } + sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v", session.Engine.Quote(session.Statement.TableName()), strings.Join(colNames, ", "), @@ -3315,6 +3522,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 } } + if st.LimitN > 0 { + condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) + } + sqlStr = fmt.Sprintf("UPDATE %v SET %v %v", session.Engine.Quote(session.Statement.TableName()), strings.Join(colNames, ", "), @@ -3329,7 +3540,9 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if err != nil { return 0, err } else if doIncVer { - verValue.SetInt(verValue.Int() + 1) + if verValue != nil && verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(verValue.Int() + 1) + } } if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { @@ -3426,10 +3639,6 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { // Delete records, bean's non-empty fields are conditions func (session *Session) Delete(bean interface{}) (int64, error) { - err := session.newDb() - if err != nil { - return 0, err - } defer session.resetStatement() if session.IsAutoClose { defer session.Close() @@ -3502,7 +3711,15 @@ func (session *Session) Delete(bean interface{}) (int64, error) { session.Statement.Params = append(session.Statement.Params, "") paramsLen := len(session.Statement.Params) copy(session.Statement.Params[1:paramsLen], session.Statement.Params[0:paramsLen-1]) - session.Statement.Params[0] = session.Engine.NowTime(deletedColumn.SQLType.Name) + + val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name) + session.Statement.Params[0] = val + + var colName = deletedColumn.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) } args = append(session.Statement.Params, args...) @@ -3534,7 +3751,6 @@ func (session *Session) Delete(bean interface{}) (int64, error) { copy(afterClosures, session.afterClosures) session.afterDeleteBeans[bean] = &afterClosures } - } else { if _, ok := interface{}(bean).(AfterInsertProcessor); ok { session.afterDeleteBeans[bean] = nil @@ -3716,79 +3932,3 @@ func (session *Session) Unscoped() *Session { session.Statement.Unscoped() return session } - -func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { - colNames := make([]string, 0) - args := make([]interface{}, 0) - - for _, col := range table.Columns() { - lColName := strings.ToLower(col.Name) - if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { - if _, ok := session.Statement.columnMap[lColName]; !ok { - continue - } - } - if col.MapType == core.ONLYFROMDB { - continue - } - - fieldValuePtr, err := col.ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - continue - } - fieldValue := *fieldValuePtr - - if col.IsAutoIncrement { - switch fieldValue.Type().Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: - if fieldValue.Int() == 0 { - continue - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: - if fieldValue.Uint() == 0 { - continue - } - case reflect.String: - if len(fieldValue.String()) == 0 { - continue - } - } - } - - if col.IsDeleted { - continue - } - - if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[lColName]; !ok { - continue - } - } - if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[lColName]; ok { - continue - } - } - - if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - args = append(args, session.Engine.NowTime(col.SQLType.Name)) - } else if col.IsVersion && session.Statement.checkVersion { - args = append(args, 1) - //} else if !col.DefaultIsEmpty { - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return colNames, args, err - } - args = append(args, arg) - } - - if includeQuote { - colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?") - } else { - colNames = append(colNames, col.Name) - } - } - return colNames, args, nil -} diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go index 1a0282999c3..cb9e7f54a78 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/sqlite3_dialect.go @@ -1,6 +1,7 @@ package xorm import ( + "database/sql" "errors" "fmt" "strings" @@ -152,7 +153,7 @@ func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName st func (db *sqlite3) SqlType(c *core.Column) string { switch t := c.SQLType.Name; t { case core.Date, core.DateTime, core.TimeStamp, core.Time: - return core.Numeric + return core.DateTime case core.TimeStampz: return core.Text case core.Char, core.Varchar, core.NVarchar, core.TinyText, core.Text, core.MediumText, core.LongText: @@ -297,6 +298,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu col := new(core.Column) col.Indexes = make(map[string]bool) col.Nullable = true + col.DefaultIsEmpty = true for idx, field := range fields { if idx == 0 { col.Name = strings.Trim(field, "`[] ") @@ -315,8 +317,14 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu } else { col.Nullable = true } + case "DEFAULT": + col.Default = fields[idx+1] + col.DefaultIsEmpty = false } } + if !col.SQLType.IsNumeric() && !col.DefaultIsEmpty { + col.Default = "'" + col.Default + "'" + } cols[col.Name] = col colSeq = append(colSeq, col.Name) } @@ -366,15 +374,16 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) indexes := make(map[string]*core.Index, 0) for rows.Next() { - var sql string - err = rows.Scan(&sql) + var tmpSql sql.NullString + err = rows.Scan(&tmpSql) if err != nil { return nil, err } - if sql == "" { + if !tmpSql.Valid { continue } + sql := tmpSql.String index := new(core.Index) nNStart := strings.Index(sql, "INDEX") @@ -384,7 +393,6 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) } indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []") - //fmt.Println(indexName) if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { index.Name = indexName[5+len(tableName) : len(indexName)] } else { diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go index 6c586ae6686..b8f859714ea 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/statement.go @@ -26,6 +26,11 @@ type decrParam struct { arg interface{} } +type exprParam struct { + colName string + expr string +} + // statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table @@ -63,6 +68,7 @@ type Statement struct { inColumns map[string]*inParam incrColumns map[string]incrParam decrColumns map[string]decrParam + exprColumns map[string]exprParam } // init @@ -98,6 +104,7 @@ func (statement *Statement) Init() { statement.inColumns = make(map[string]*inParam) statement.incrColumns = make(map[string]incrParam) statement.decrColumns = make(map[string]decrParam) + statement.exprColumns = make(map[string]exprParam) } // add the raw sql statement @@ -153,9 +160,6 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { t := v.Type() if t.Kind() == reflect.String { statement.AltTableName = tableNameOrBean.(string) - if statement.AltTableName[0] == '~' { - statement.AltTableName = statement.Engine.TableMapper.TableName(statement.AltTableName[1:]) - } } else if t.Kind() == reflect.Struct { statement.RefTable = statement.Engine.autoMapType(v) } @@ -282,7 +286,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { func buildUpdates(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, - mustColumnMap map[string]bool, update bool) ([]string, []interface{}) { + mustColumnMap map[string]bool, columnMap map[string]bool, update bool) ([]string, []interface{}) { colNames := make([]string, 0) var args = make([]interface{}, 0) @@ -302,6 +306,9 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if col.IsDeleted { continue } + if use, ok := columnMap[col.Name]; ok && !use { + continue + } if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { continue @@ -414,13 +421,16 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if table, ok := engine.Tables[fieldValue.Type()]; ok { if len(table.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) - if pkField.Int() != 0 { + // fix non-int pk issues + //if pkField.Int() != 0 { + if pkField.IsValid() && !isZero(pkField.Interface()) { val = pkField.Interface() } else { continue } } else { //TODO: how to handler? + panic("not supported") } } else { val = fieldValue.Interface() @@ -579,24 +589,29 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, t := int64(fieldValue.Uint()) val = reflect.ValueOf(&t).Interface() case reflect.Struct: - if fieldType == reflect.TypeOf(time.Now()) { - t := fieldValue.Interface().(time.Time) + if fieldType.ConvertibleTo(core.TimeType) { + t := fieldValue.Convert(core.TimeType).Interface().(time.Time) if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { continue } val = engine.FormatTime(col.SQLType.Name, t) + } else if _, ok := reflect.New(fieldType).Interface().(core.Conversion); ok { + continue } else { engine.autoMapType(fieldValue) if table, ok := engine.Tables[fieldValue.Type()]; ok { if len(table.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) - if pkField.Int() != 0 { + // fix non-int pk issues + //if pkField.Int() != 0 { + if pkField.IsValid() && !isZero(pkField.Interface()) { val = pkField.Interface() } else { continue } } else { //TODO: how to handler? + panic("not supported") } } else { val = fieldValue.Interface() @@ -716,6 +731,13 @@ func (statement *Statement) Decr(column string, arg ...interface{}) *Statement { return statement } +// Generate "Update ... Set column = {expression}" statment +func (statement *Statement) SetExpr(column string, expression string) *Statement { + k := strings.ToLower(column) + statement.exprColumns[k] = exprParam{column, expression} + return statement +} + // Generate "Update ... Set column = column + arg" statment func (statement *Statement) getInc() map[string]incrParam { return statement.incrColumns @@ -726,6 +748,11 @@ func (statement *Statement) getDec() map[string]decrParam { return statement.decrColumns } +// Generate "Update ... Set column = {expression}" statment +func (statement *Statement) getExpr() map[string]exprParam { + return statement.exprColumns +} + // Generate "Where column IN (?) " statment func (statement *Statement) In(column string, args ...interface{}) *Statement { k := strings.ToLower(column) @@ -941,15 +968,9 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co l := len(t) if l > 1 { table := t[0] - if table[0] == '~' { - table = statement.Engine.TableMapper.TableName(table[1:]) - } joinTable = statement.Engine.Quote(table) + " AS " + statement.Engine.Quote(t[1]) } else if l == 1 { table := t[0] - if table[0] == '~' { - table = statement.Engine.TableMapper.TableName(table[1:]) - } joinTable = statement.Engine.Quote(table) } case []interface{}: @@ -962,9 +983,6 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co t := v.Type() if t.Kind() == reflect.String { table = f.(string) - if table[0] == '~' { - table = statement.Engine.TableMapper.TableName(table[1:]) - } } else if t.Kind() == reflect.Struct { r := statement.Engine.autoMapType(v) table = r.Name @@ -977,9 +995,6 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co } default: t := fmt.Sprintf("%v", tablename) - if t[0] == '~' { - t = statement.Engine.TableMapper.TableName(t[1:]) - } joinTable = statement.Engine.Quote(t) } if statement.JoinStr != "" { @@ -1105,9 +1120,10 @@ func (s *Statement) genDelIndexSQL() []string { return sqls } +/* func (s *Statement) genDropSQL() string { - return s.Engine.dialect.DropTableSql(s.TableName()) + ";" -} + return s.Engine.dialect.MustDropTa(s.TableName()) + ";" +}*/ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { var table *core.Table @@ -1126,13 +1142,21 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) statement.BeanArgs = args var columnStr string = statement.ColumnStr - if statement.JoinStr == "" { - if columnStr == "" { - columnStr = statement.genColumnStr() + if len(statement.JoinStr) == 0 { + if len(columnStr) == 0 { + if statement.GroupByStr != "" { + columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + } else { + columnStr = statement.genColumnStr() + } } } else { - if columnStr == "" { - columnStr = "*" + if len(columnStr) == 0 { + if statement.GroupByStr != "" { + columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + } else { + columnStr = "*" + } } } @@ -1178,14 +1202,16 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{} id = "" } statement.attachInSql() - return statement.genSelectSql(fmt.Sprintf("count(%v) AS %v", id, statement.Engine.Quote("total"))), append(statement.Params, statement.BeanArgs...) + return statement.genSelectSql(fmt.Sprintf("count(%v)", id)), append(statement.Params, statement.BeanArgs...) } func (statement *Statement) genSelectSql(columnStr string) (a string) { - if statement.GroupByStr != "" { - columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) - statement.GroupByStr = columnStr - } + /*if statement.GroupByStr != "" { + if columnStr == "" { + columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) + } + //statement.GroupByStr = columnStr + }*/ var distinct string if statement.IsDistinct { distinct = "DISTINCT " @@ -1210,7 +1236,11 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } var fromStr string = " FROM " + statement.Engine.Quote(statement.TableName()) if statement.TableAlias != "" { - fromStr += " AS " + statement.Engine.Quote(statement.TableAlias) + if statement.Engine.dialect.DBType() == core.ORACLE { + fromStr += " " + statement.Engine.Quote(statement.TableAlias) + } else { + fromStr += " AS " + statement.Engine.Quote(statement.TableAlias) + } } if statement.JoinStr != "" { fromStr = fmt.Sprintf("%v %v", fromStr, statement.JoinStr) @@ -1233,8 +1263,16 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { column = statement.RefTable.ColumnsSeq()[0] } } - mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s))", - column, statement.Start, column, fromStr, whereStr) + var orderStr string + if len(statement.OrderStr) > 0 { + orderStr = " ORDER BY " + statement.OrderStr + } + var groupStr string + if len(statement.GroupByStr) > 0 { + groupStr = " GROUP BY " + statement.GroupByStr + } + mssqlCondi = fmt.Sprintf("(%s NOT IN (SELECT TOP %d %s%s%s%s%s))", + column, statement.Start, column, fromStr, whereStr, orderStr, groupStr) } } @@ -1258,12 +1296,16 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { if statement.OrderStr != "" { a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) } - if statement.Engine.dialect.DBType() != core.MSSQL { + if statement.Engine.dialect.DBType() != core.MSSQL && statement.Engine.dialect.DBType() != core.ORACLE { if statement.Start > 0 { a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) } else if statement.LimitN > 0 { a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) } + } else if statement.Engine.dialect.DBType() == core.ORACLE { + if statement.Start != 0 || statement.LimitN != 0 { + a = fmt.Sprintf("SELECT %v FROM (SELECT %v,ROWNUM RN FROM (%v) at WHERE ROWNUM <= %d) aat WHERE RN > %d", columnStr, columnStr, a, statement.Start+statement.LimitN, statement.Start) + } } return diff --git a/Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go b/Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go index 76496ddd33a..71644e6c039 100644 --- a/Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go +++ b/Godeps/_workspace/src/github.com/go-xorm/xorm/xorm.go @@ -13,7 +13,7 @@ import ( ) const ( - Version string = "0.4.1" + Version string = "0.4.2.0225" ) func regDrvsNDialects() bool { @@ -84,17 +84,16 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { TZLocation: time.Local, } + engine.dialect.SetLogger(engine.Logger) + engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) - //engine.Filters = dialect.Filters() - //engine.Cacher = NewLRUCacher() - //err = engine.SetPool(NewSysConnectPool()) - runtime.SetFinalizer(engine, close) - return engine, err + + return engine, nil } // clone an engine func (engine *Engine) Clone() (*Engine, error) { - return NewEngine(engine.dialect.DriverName(), engine.dialect.DataSourceName()) + return NewEngine(engine.DriverName(), engine.DataSourceName()) } diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/.gitignore b/Godeps/_workspace/src/github.com/macaron-contrib/session/.gitignore new file mode 100644 index 00000000000..9297dbcd7c4 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/.gitignore @@ -0,0 +1,2 @@ +ledis/tmp.db +nodb/tmp.db \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md b/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md index 496ce64ce0d..01de811eacc 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/README.md @@ -1,7 +1,7 @@ session [![Build Status](https://drone.io/github.com/macaron-contrib/session/status.png)](https://drone.io/github.com/macaron-contrib/session/latest) [![](http://gocover.io/_badge/github.com/macaron-contrib/session)](http://gocover.io/github.com/macaron-contrib/session) ======= -Middleware session provides session management for [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase and Ledis. +Middleware session provides session management for [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase, Ledis and Nodb. ### Installation @@ -12,6 +12,10 @@ Middleware session provides session management for [Macaron](https://github.com/ - [API Reference](https://gowalker.org/github.com/macaron-contrib/session) - [Documentation](http://macaron.gogs.io/docs/middlewares/session) +## Credits + +This package is forked from [beego/session](https://github.com/astaxie/beego/tree/master/session) with reconstruction(over 80%). + ## License This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go index 4dfb906ec7e..cab807d00bd 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/file.go @@ -28,17 +28,17 @@ import ( "github.com/Unknwon/com" ) -// FileSessionStore represents a file session store implementation. -type FileSessionStore struct { +// FileStore represents a file session store implementation. +type FileStore struct { p *FileProvider sid string lock sync.RWMutex data map[interface{}]interface{} } -// NewFileSessionStore creates and returns a file session store. -func NewFileSessionStore(p *FileProvider, sid string, kv map[interface{}]interface{}) *FileSessionStore { - return &FileSessionStore{ +// NewFileStore creates and returns a file session store. +func NewFileStore(p *FileProvider, sid string, kv map[interface{}]interface{}) *FileStore { + return &FileStore{ p: p, sid: sid, data: kv, @@ -46,7 +46,7 @@ func NewFileSessionStore(p *FileProvider, sid string, kv map[interface{}]interfa } // Set sets value to given key in session. -func (s *FileSessionStore) Set(key, val interface{}) error { +func (s *FileStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -55,7 +55,7 @@ func (s *FileSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *FileSessionStore) Get(key interface{}) interface{} { +func (s *FileStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() @@ -63,7 +63,7 @@ func (s *FileSessionStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *FileSessionStore) Delete(key interface{}) error { +func (s *FileStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -72,12 +72,12 @@ func (s *FileSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *FileSessionStore) ID() string { +func (s *FileStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (s *FileSessionStore) Release() error { +func (s *FileStore) Release() error { data, err := EncodeGob(s.data) if err != nil { return err @@ -87,7 +87,7 @@ func (s *FileSessionStore) Release() error { } // Flush deletes all session data. -func (s *FileSessionStore) Flush() error { +func (s *FileStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -97,7 +97,6 @@ func (s *FileSessionStore) Flush() error { // FileProvider represents a file session provider implementation. type FileProvider struct { - lock sync.RWMutex maxlifetime int64 rootPath string } @@ -115,9 +114,6 @@ func (p *FileProvider) filepath(sid string) string { // Read returns raw session store by session ID. func (p *FileProvider) Read(sid string) (_ RawStore, err error) { - p.lock.Lock() - defer p.lock.Unlock() - filename := p.filepath(sid) if err = os.MkdirAll(path.Dir(filename), os.ModePerm); err != nil { return nil, err @@ -151,22 +147,16 @@ func (p *FileProvider) Read(sid string) (_ RawStore, err error) { return nil, err } } - return NewFileSessionStore(p, sid, kv), nil + return NewFileStore(p, sid, kv), nil } // Exist returns true if session with given ID exists. func (p *FileProvider) Exist(sid string) bool { - p.lock.Lock() - defer p.lock.Unlock() - return com.IsFile(p.filepath(sid)) } // Destory deletes a session by session ID. func (p *FileProvider) Destory(sid string) error { - p.lock.Lock() - defer p.lock.Unlock() - return os.Remove(p.filepath(sid)) } @@ -201,12 +191,9 @@ func (p *FileProvider) regenerate(oldsid, sid string) (err error) { // Regenerate regenerates a session store from old session ID to new one. func (p *FileProvider) Regenerate(oldsid, sid string) (_ RawStore, err error) { - p.lock.Lock() if err := p.regenerate(oldsid, sid); err != nil { - p.lock.Unlock() return nil, err } - p.lock.Unlock() return p.Read(sid) } @@ -236,9 +223,6 @@ func (p *FileProvider) GC() { return } - p.lock.Lock() - defer p.lock.Unlock() - if err := filepath.Walk(p.rootPath, func(path string, fi os.FileInfo, err error) error { if err != nil { return err diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go index 7893769b525..afde7134cbd 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go @@ -16,26 +16,39 @@ package session import ( + "fmt" + "strings" "sync" + "github.com/Unknwon/com" "github.com/siddontang/ledisdb/config" "github.com/siddontang/ledisdb/ledis" + "gopkg.in/ini.v1" "github.com/macaron-contrib/session" ) -var c *ledis.DB +// LedisStore represents a ledis session store implementation. +type LedisStore struct { + c *ledis.DB + sid string + expire int64 + lock sync.RWMutex + data map[interface{}]interface{} +} -// LedisSessionStore represents a ledis session store implementation. -type LedisSessionStore struct { - sid string - lock sync.RWMutex - data map[interface{}]interface{} - maxlifetime int64 +// NewLedisStore creates and returns a ledis session store. +func NewLedisStore(c *ledis.DB, sid string, expire int64, kv map[interface{}]interface{}) *LedisStore { + return &LedisStore{ + c: c, + expire: expire, + sid: sid, + data: kv, + } } // Set sets value to given key in session. -func (s *LedisSessionStore) Set(key, val interface{}) error { +func (s *LedisStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -44,7 +57,7 @@ func (s *LedisSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *LedisSessionStore) Get(key interface{}) interface{} { +func (s *LedisStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() @@ -52,7 +65,7 @@ func (s *LedisSessionStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *LedisSessionStore) Delete(key interface{}) error { +func (s *LedisStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -61,25 +74,26 @@ func (s *LedisSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *LedisSessionStore) ID() string { +func (s *LedisStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (s *LedisSessionStore) Release() error { +func (s *LedisStore) Release() error { data, err := session.EncodeGob(s.data) if err != nil { return err } - if err = c.Set([]byte(s.sid), data); err != nil { + + if err = s.c.Set([]byte(s.sid), data); err != nil { return err } - _, err = c.Expire([]byte(s.sid), s.maxlifetime) + _, err = s.c.Expire([]byte(s.sid), s.expire) return err } // Flush deletes all session data. -func (s *LedisSessionStore) Flush() error { +func (s *LedisStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -89,30 +103,54 @@ func (s *LedisSessionStore) Flush() error { // LedisProvider represents a ledis session provider implementation. type LedisProvider struct { - maxlifetime int64 - savePath string + c *ledis.DB + expire int64 } -// Init initializes memory session provider. -func (p *LedisProvider) Init(maxlifetime int64, savePath string) error { - p.maxlifetime = maxlifetime - p.savePath = savePath - cfg := new(config.Config) - cfg.DataDir = p.savePath - var err error - nowLedis, err := ledis.Open(cfg) - c, err = nowLedis.Select(0) +// Init initializes ledis session provider. +// configs: data_dir=./app.db,db=0 +func (p *LedisProvider) Init(expire int64, configs string) error { + p.expire = expire + + cfg, err := ini.Load([]byte(strings.Replace(configs, ",", "\n", -1))) if err != nil { - println(err) - return nil + return err } - return nil + + db := 0 + opt := new(config.Config) + for k, v := range cfg.Section("").KeysHash() { + switch k { + case "data_dir": + opt.DataDir = v + case "db": + db = com.StrTo(v).MustInt() + default: + return fmt.Errorf("session/ledis: unsupported option '%s'", k) + } + } + + l, err := ledis.Open(opt) + if err != nil { + return fmt.Errorf("session/ledis: error opening db: %v", err) + } + p.c, err = l.Select(db) + return err } // Read returns raw session store by session ID. func (p *LedisProvider) Read(sid string) (session.RawStore, error) { - kvs, err := c.Get([]byte(sid)) + if !p.Exist(sid) { + if err := p.c.Set([]byte(sid), []byte("")); err != nil { + return nil, err + } + } + var kv map[interface{}]interface{} + kvs, err := p.c.Get([]byte(sid)) + if err != nil { + return nil, err + } if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { @@ -121,41 +159,40 @@ func (p *LedisProvider) Read(sid string) (session.RawStore, error) { return nil, err } } - ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime} - return ls, nil + + return NewLedisStore(p.c, sid, p.expire, kv), nil } // Exist returns true if session with given ID exists. func (p *LedisProvider) Exist(sid string) bool { - count, _ := c.Exists([]byte(sid)) - if count == 0 { - return false - } else { - return true - } + count, err := p.c.Exists([]byte(sid)) + return err == nil && count > 0 } // Destory deletes a session by session ID. func (p *LedisProvider) Destory(sid string) error { - _, err := c.Del([]byte(sid)) + _, err := p.c.Del([]byte(sid)) return err } // Regenerate regenerates a session store from old session ID to new one. -func (p *LedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - count, _ := c.Exists([]byte(sid)) - if count == 0 { - // oldsid doesn't exists, set the new sid directly - // ignore error here, since if it return error - // the existed value will be 0 - c.Set([]byte(sid), []byte("")) - c.Expire([]byte(sid), p.maxlifetime) - } else { - data, _ := c.Get([]byte(oldsid)) - c.Set([]byte(sid), data) - c.Expire([]byte(sid), p.maxlifetime) +func (p *LedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) } - kvs, err := c.Get([]byte(sid)) + + kvs := make([]byte, 0) + if p.Exist(oldsid) { + if kvs, err = p.c.Get([]byte(oldsid)); err != nil { + return nil, err + } else if _, err = p.c.Del([]byte(oldsid)); err != nil { + return nil, err + } + } + if err = p.c.SetEX([]byte(sid), p.expire, kvs); err != nil { + return nil, err + } + var kv map[interface{}]interface{} if len(kvs) == 0 { kv = make(map[interface{}]interface{}) @@ -165,18 +202,20 @@ func (p *LedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) return nil, err } } - ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime} - return ls, nil + + return NewLedisStore(p.c, sid, p.expire, kv), nil } // Count counts and returns number of sessions. func (p *LedisProvider) Count() int { - // FIXME - return 0 + // FIXME: how come this library does not have DbSize() method? + return -1 } // GC calls GC to clean expired sessions. -func (p *LedisProvider) GC() {} +func (p *LedisProvider) GC() { + // FIXME: wtf??? +} func init() { session.Register("ledis", &LedisProvider{}) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go new file mode 100644 index 00000000000..dac42a364b7 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis_test.go @@ -0,0 +1,105 @@ +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_LedisProvider(t *testing.T) { + Convey("Test ledis session provider", t, func() { + opt := session.Options{ + Provider: "ledis", + ProviderConfig: "data_dir=./tmp.db", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + Convey("Regenrate empty session", func() { + m.Get("/empty", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + }) + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/empty", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") + m.ServeHTTP(resp, req) + }) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go index e06895202f0..b4fcdde62bd 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go @@ -16,6 +16,7 @@ package session import ( + "fmt" "strings" "sync" @@ -24,20 +25,35 @@ import ( "github.com/macaron-contrib/session" ) -var ( - client *memcache.Client -) +// MemcacheStore represents a memcache session store implementation. +type MemcacheStore struct { + c *memcache.Client + sid string + expire int32 + lock sync.RWMutex + data map[interface{}]interface{} +} -// MemcacheSessionStore represents a memcache session store implementation. -type MemcacheSessionStore struct { - sid string - lock sync.RWMutex - data map[interface{}]interface{} - maxlifetime int64 +// NewMemcacheStore creates and returns a memcache session store. +func NewMemcacheStore(c *memcache.Client, sid string, expire int32, kv map[interface{}]interface{}) *MemcacheStore { + return &MemcacheStore{ + c: c, + sid: sid, + expire: expire, + data: kv, + } +} + +func NewItem(sid string, data []byte, expire int32) *memcache.Item { + return &memcache.Item{ + Key: sid, + Value: data, + Expiration: expire, + } } // Set sets value to given key in session. -func (s *MemcacheSessionStore) Set(key, val interface{}) error { +func (s *MemcacheStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -46,7 +62,7 @@ func (s *MemcacheSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *MemcacheSessionStore) Get(key interface{}) interface{} { +func (s *MemcacheStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() @@ -54,7 +70,7 @@ func (s *MemcacheSessionStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *MemcacheSessionStore) Delete(key interface{}) error { +func (s *MemcacheStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -63,26 +79,22 @@ func (s *MemcacheSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *MemcacheSessionStore) ID() string { +func (s *MemcacheStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (s *MemcacheSessionStore) Release() error { +func (s *MemcacheStore) Release() error { data, err := session.EncodeGob(s.data) if err != nil { return err } - return client.Set(&memcache.Item{ - Key: s.sid, - Value: data, - Expiration: int32(s.maxlifetime), - }) + return s.c.Set(NewItem(s.sid, data, s.expire)) } // Flush deletes all session data. -func (s *MemcacheSessionStore) Flush() error { +func (s *MemcacheStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -90,41 +102,75 @@ func (s *MemcacheSessionStore) Flush() error { return nil } -// MemProvider represents a memcache session provider implementation. -type MemProvider struct { - maxlifetime int64 - conninfo []string - poolsize int - password string +// MemcacheProvider represents a memcache session provider implementation. +type MemcacheProvider struct { + c *memcache.Client + expire int32 } -// Init initializes memory session provider. -// connStrs can be multiple connection strings separate by ; -// e.g. 127.0.0.1:9090 -func (p *MemProvider) Init(maxlifetime int64, connStrs string) error { - p.maxlifetime = maxlifetime - p.conninfo = strings.Split(connStrs, ";") - client = memcache.New(p.conninfo...) - return nil -} - -func (p *MemProvider) connectInit() error { - client = memcache.New(p.conninfo...) +// Init initializes memcache session provider. +// connStrs: 127.0.0.1:9090;127.0.0.1:9091 +func (p *MemcacheProvider) Init(expire int64, connStrs string) error { + p.expire = int32(expire) + p.c = memcache.New(strings.Split(connStrs, ";")...) return nil } // Read returns raw session store by session ID. -func (p *MemProvider) Read(sid string) (session.RawStore, error) { - if client == nil { - if err := p.connectInit(); err != nil { +func (p *MemcacheProvider) Read(sid string) (session.RawStore, error) { + if !p.Exist(sid) { + if err := p.c.Set(NewItem(sid, []byte(""), p.expire)); err != nil { return nil, err } } - item, err := client.Get(sid) + var kv map[interface{}]interface{} + item, err := p.c.Get(sid) if err != nil { return nil, err } + if len(item.Value) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(item.Value) + if err != nil { + return nil, err + } + } + + return NewMemcacheStore(p.c, sid, p.expire, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *MemcacheProvider) Exist(sid string) bool { + _, err := p.c.Get(sid) + return err == nil +} + +// Destory deletes a session by session ID. +func (p *MemcacheProvider) Destory(sid string) error { + return p.c.Delete(sid) +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *MemcacheProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + item := NewItem(sid, []byte(""), p.expire) + if p.Exist(oldsid) { + item, err = p.c.Get(oldsid) + if err != nil { + return nil, err + } else if err = p.c.Delete(oldsid); err != nil { + return nil, err + } + item.Key = sid + } + if err = p.c.Set(item); err != nil { + return nil, err + } var kv map[interface{}]interface{} if len(item.Value) == 0 { @@ -136,86 +182,18 @@ func (p *MemProvider) Read(sid string) (session.RawStore, error) { } } - rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime} - return rs, nil -} - -// Exist returns true if session with given ID exists. -func (p *MemProvider) Exist(sid string) bool { - if client == nil { - if err := p.connectInit(); err != nil { - return false - } - } - - if item, err := client.Get(sid); err != nil || len(item.Value) == 0 { - return false - } else { - return true - } -} - -// Destory deletes a session by session ID. -func (p *MemProvider) Destory(sid string) error { - if client == nil { - if err := p.connectInit(); err != nil { - return err - } - } - - return client.Delete(sid) -} - -// Regenerate regenerates a session store from old session ID to new one. -func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - if client == nil { - if err := p.connectInit(); err != nil { - return nil, err - } - } - - var contain []byte - if item, err := client.Get(sid); err != nil || len(item.Value) == 0 { - // oldsid doesn't exists, set the new sid directly - // ignore error here, since if it return error - // the existed value will be 0 - item.Key = sid - item.Value = []byte("") - item.Expiration = int32(p.maxlifetime) - client.Set(item) - } else { - client.Delete(oldsid) - item.Key = sid - item.Value = item.Value - item.Expiration = int32(p.maxlifetime) - client.Set(item) - contain = item.Value - } - - var kv map[interface{}]interface{} - if len(contain) == 0 { - kv = make(map[interface{}]interface{}) - } else { - var err error - kv, err = session.DecodeGob(contain) - if err != nil { - return nil, err - } - } - - rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime} - return rs, nil + return NewMemcacheStore(p.c, sid, p.expire, kv), nil } // Count counts and returns number of sessions. -func (p *MemProvider) Count() int { - // FIXME - return 0 +func (p *MemcacheProvider) Count() int { + // FIXME: how come this library does not have Stats method? + return -1 } // GC calls GC to clean expired sessions. -func (p *MemProvider) GC() {} +func (p *MemcacheProvider) GC() {} func init() { - session.Register("memcache", &MemProvider{}) + session.Register("memcache", &MemcacheProvider{}) } diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go new file mode 100644 index 00000000000..beb272d52b1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache_test.go @@ -0,0 +1,107 @@ +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_MemcacheProvider(t *testing.T) { + Convey("Test memcache session provider", t, func() { + opt := session.Options{ + Provider: "memcache", + ProviderConfig: "127.0.0.1:9090", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + }) + + Convey("Regenrate empty session", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") + m.ServeHTTP(resp, req) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go index 040b8e697dc..e717635b951 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/memory.go @@ -22,17 +22,17 @@ import ( "time" ) -// MemSessionStore represents a in-memory session store implementation. -type MemSessionStore struct { +// MemStore represents a in-memory session store implementation. +type MemStore struct { sid string lock sync.RWMutex data map[interface{}]interface{} lastAccess time.Time } -// NewMemSessionStore creates and returns a memory session store. -func NewMemSessionStore(sid string) *MemSessionStore { - return &MemSessionStore{ +// NewMemStore creates and returns a memory session store. +func NewMemStore(sid string) *MemStore { + return &MemStore{ sid: sid, data: make(map[interface{}]interface{}), lastAccess: time.Now(), @@ -40,7 +40,7 @@ func NewMemSessionStore(sid string) *MemSessionStore { } // Set sets value to given key in session. -func (s *MemSessionStore) Set(key, val interface{}) error { +func (s *MemStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -49,15 +49,15 @@ func (s *MemSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *MemSessionStore) Get(key interface{}) interface{} { +func (s *MemStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() return s.data[key] } -// Delete delete a key from session. -func (s *MemSessionStore) Delete(key interface{}) error { +// Delete deletes a key from session. +func (s *MemStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -66,17 +66,17 @@ func (s *MemSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *MemSessionStore) ID() string { +func (s *MemStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (_ *MemSessionStore) Release() error { +func (_ *MemStore) Release() error { return nil } // Flush deletes all session data. -func (s *MemSessionStore) Flush() error { +func (s *MemStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -105,7 +105,7 @@ func (p *MemProvider) update(sid string) error { defer p.lock.Unlock() if e, ok := p.data[sid]; ok { - e.Value.(*MemSessionStore).lastAccess = time.Now() + e.Value.(*MemStore).lastAccess = time.Now() p.list.MoveToFront(e) return nil } @@ -122,14 +122,14 @@ func (p *MemProvider) Read(sid string) (_ RawStore, err error) { if err = p.update(sid); err != nil { return nil, err } - return e.Value.(*MemSessionStore), nil + return e.Value.(*MemStore), nil } // Create a new session. p.lock.Lock() defer p.lock.Unlock() - s := NewMemSessionStore(sid) + s := NewMemStore(sid) p.data[sid] = p.list.PushBack(s) return s, nil } @@ -173,7 +173,7 @@ func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { return nil, err } - s.(*MemSessionStore).sid = sid + s.(*MemStore).sid = sid p.data[sid] = p.list.PushBack(s) return s, nil } @@ -193,11 +193,11 @@ func (p *MemProvider) GC() { break } - if (e.Value.(*MemSessionStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { + if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { p.lock.RUnlock() p.lock.Lock() p.list.Remove(e) - delete(p.data, e.Value.(*MemSessionStore).sid) + delete(p.data, e.Value.(*MemStore).sid) p.lock.Unlock() p.lock.RLock() } else { diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go index 908de4f1b52..7997e03c0d8 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go @@ -17,6 +17,8 @@ package session import ( "database/sql" + "fmt" + "log" "sync" "time" @@ -25,16 +27,25 @@ import ( "github.com/macaron-contrib/session" ) -// MysqlSessionStore represents a mysql session store implementation. -type MysqlSessionStore struct { +// MysqlStore represents a mysql session store implementation. +type MysqlStore struct { c *sql.DB sid string lock sync.RWMutex data map[interface{}]interface{} } +// NewMysqlStore creates and returns a mysql session store. +func NewMysqlStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *MysqlStore { + return &MysqlStore{ + c: c, + sid: sid, + data: kv, + } +} + // Set sets value to given key in session. -func (s *MysqlSessionStore) Set(key, val interface{}) error { +func (s *MysqlStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -43,7 +54,7 @@ func (s *MysqlSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *MysqlSessionStore) Get(key interface{}) interface{} { +func (s *MysqlStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() @@ -51,7 +62,7 @@ func (s *MysqlSessionStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *MysqlSessionStore) Delete(key interface{}) error { +func (s *MysqlStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -60,24 +71,24 @@ func (s *MysqlSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *MysqlSessionStore) ID() string { +func (s *MysqlStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (s *MysqlSessionStore) Release() error { - defer s.c.Close() +func (s *MysqlStore) Release() error { data, err := session.EncodeGob(s.data) if err != nil { return err } - _, err = s.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?", + + _, err = s.c.Exec("UPDATE session SET data=?, expiry=? WHERE `key`=?", data, time.Now().Unix(), s.sid) return err } // Flush deletes all session data. -func (s *MysqlSessionStore) Flush() error { +func (s *MysqlStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -87,113 +98,96 @@ func (s *MysqlSessionStore) Flush() error { // MysqlProvider represents a mysql session provider implementation. type MysqlProvider struct { - maxlifetime int64 - connStr string + c *sql.DB + expire int64 } -func (p *MysqlProvider) connectInit() *sql.DB { - db, e := sql.Open("mysql", p.connStr) - if e != nil { - return nil +// Init initializes mysql session provider. +// connStr: username:password@protocol(address)/dbname?param=value +func (p *MysqlProvider) Init(expire int64, connStr string) (err error) { + p.expire = expire + + p.c, err = sql.Open("mysql", connStr) + if err != nil { + return err } - return db -} - -// Init initializes memory session provider. -func (p *MysqlProvider) Init(maxlifetime int64, connStr string) error { - p.maxlifetime = maxlifetime - p.connStr = connStr - return nil + return p.c.Ping() } // Read returns raw session store by session ID. func (p *MysqlProvider) Read(sid string) (session.RawStore, error) { - c := p.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", sid) - var sessiondata []byte - err := row.Scan(&sessiondata) + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", + _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", sid, "", time.Now().Unix()) } + if err != nil { + return nil, err + } + var kv map[interface{}]interface{} - if len(sessiondata) == 0 { + if len(data) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob(sessiondata) + kv, err = session.DecodeGob(data) if err != nil { return nil, err } } - rs := &MysqlSessionStore{c: c, sid: sid, data: kv} - return rs, nil + + return NewMysqlStore(p.c, sid, kv), nil } // Exist returns true if session with given ID exists. func (p *MysqlProvider) Exist(sid string) bool { - c := p.connectInit() - defer c.Close() - - row := c.QueryRow("select session_data from session where session_key=?", sid) - var sessiondata []byte - err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - return false - } else { - return true + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) + if err != nil && err != sql.ErrNoRows { + panic("session/mysql: error checking existence: " + err.Error()) } + return err != sql.ErrNoRows } // Destory deletes a session by session ID. -func (p *MysqlProvider) Destory(sid string) (err error) { - c := p.connectInit() - if _, err = c.Exec("DELETE FROM session where session_key=?", sid); err != nil { - return err - } - return c.Close() +func (p *MysqlProvider) Destory(sid string) error { + _, err := p.c.Exec("DELETE FROM session WHERE `key`=?", sid) + return err } // Regenerate regenerates a session store from old session ID to new one. -func (p *MysqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - c := p.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", oldsid) - var sessiondata []byte - err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix()) +func (p *MysqlProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) } - c.Exec("update session set `session_key`=? where session_key=?", sid, oldsid) - var kv map[interface{}]interface{} - if len(sessiondata) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob(sessiondata) - if err != nil { + + if !p.Exist(oldsid) { + if _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", + oldsid, "", time.Now().Unix()); err != nil { return nil, err } } - rs := &MysqlSessionStore{c: c, sid: sid, data: kv} - return rs, nil + + if _, err = p.c.Exec("UPDATE session SET `key`=? WHERE `key`=?", sid, oldsid); err != nil { + return nil, err + } + + return p.Read(sid) } // Count counts and returns number of sessions. -func (p *MysqlProvider) Count() int { - c := p.connectInit() - defer c.Close() - - var total int - err := c.QueryRow("SELECT count(*) as num from session").Scan(&total) - if err != nil { - return 0 +func (p *MysqlProvider) Count() (total int) { + if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { + panic("session/mysql: error counting records: " + err.Error()) } return total } // GC calls GC to clean expired sessions. -func (mp *MysqlProvider) GC() { - c := mp.connectInit() - c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) - c.Close() +func (p *MysqlProvider) GC() { + if _, err := p.c.Exec("DELETE FROM session WHERE UNIX_TIMESTAMP(NOW()) - expiry > ?", p.expire); err != nil { + log.Printf("session/mysql: error garbage collecting: %v", err) + } } func init() { diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go new file mode 100644 index 00000000000..15b3996a228 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql_test.go @@ -0,0 +1,138 @@ +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_MysqlProvider(t *testing.T) { + Convey("Test mysql session provider", t, func() { + opt := session.Options{ + Provider: "mysql", + ProviderConfig: "root:@tcp(localhost:3306)/macaron?charset=utf8", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + So(raw.Release(), ShouldBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + }) + + Convey("Regenrate empty session", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf48; Path=/;") + m.ServeHTTP(resp, req) + }) + + Convey("GC session", func() { + m := macaron.New() + opt2 := opt + opt2.Gclifetime = 1 + m.Use(session.Sessioner(opt2)) + + m.Get("/", func(sess session.Store) { + sess.Set("uname", "unknwon") + So(sess.ID(), ShouldNotBeEmpty) + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Flush(), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + time.Sleep(2 * time.Second) + sess.GC() + So(sess.Count(), ShouldEqual, 0) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go new file mode 100644 index 00000000000..7f017bf0457 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.go @@ -0,0 +1,203 @@ +// Copyright 2015 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "fmt" + "sync" + + "github.com/lunny/nodb" + "github.com/lunny/nodb/config" + + "github.com/macaron-contrib/session" +) + +// NodbStore represents a nodb session store implementation. +type NodbStore struct { + c *nodb.DB + sid string + expire int64 + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewNodbStore creates and returns a ledis session store. +func NewNodbStore(c *nodb.DB, sid string, expire int64, kv map[interface{}]interface{}) *NodbStore { + return &NodbStore{ + c: c, + expire: expire, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *NodbStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *NodbStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *NodbStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *NodbStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (s *NodbStore) Release() error { + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + if err = s.c.Set([]byte(s.sid), data); err != nil { + return err + } + _, err = s.c.Expire([]byte(s.sid), s.expire) + return err +} + +// Flush deletes all session data. +func (s *NodbStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// NodbProvider represents a ledis session provider implementation. +type NodbProvider struct { + c *nodb.DB + expire int64 +} + +// Init initializes nodb session provider. +func (p *NodbProvider) Init(expire int64, configs string) error { + p.expire = expire + + cfg := new(config.Config) + cfg.DataDir = configs + dbs, err := nodb.Open(cfg) + if err != nil { + return fmt.Errorf("session/nodb: error opening db: %v", err) + } + + p.c, err = dbs.Select(0) + return err +} + +// Read returns raw session store by session ID. +func (p *NodbProvider) Read(sid string) (session.RawStore, error) { + if !p.Exist(sid) { + if err := p.c.Set([]byte(sid), []byte("")); err != nil { + return nil, err + } + } + + var kv map[interface{}]interface{} + kvs, err := p.c.Get([]byte(sid)) + if err != nil { + return nil, err + } + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(kvs) + if err != nil { + return nil, err + } + } + + return NewNodbStore(p.c, sid, p.expire, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *NodbProvider) Exist(sid string) bool { + count, err := p.c.Exists([]byte(sid)) + return err == nil && count > 0 +} + +// Destory deletes a session by session ID. +func (p *NodbProvider) Destory(sid string) error { + _, err := p.c.Del([]byte(sid)) + return err +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *NodbProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + kvs := make([]byte, 0) + if p.Exist(oldsid) { + if kvs, err = p.c.Get([]byte(oldsid)); err != nil { + return nil, err + } else if _, err = p.c.Del([]byte(oldsid)); err != nil { + return nil, err + } + } + + if err = p.c.Set([]byte(sid), kvs); err != nil { + return nil, err + } else if _, err = p.c.Expire([]byte(sid), p.expire); err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(kvs) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob([]byte(kvs)) + if err != nil { + return nil, err + } + } + + return NewNodbStore(p.c, sid, p.expire, kv), nil +} + +// Count counts and returns number of sessions. +func (p *NodbProvider) Count() int { + // FIXME: how come this library does not have DbSize() method? + return -1 +} + +// GC calls GC to clean expired sessions. +func (p *NodbProvider) GC() {} + +func init() { + session.Register("nodb", &NodbProvider{}) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go new file mode 100644 index 00000000000..c86ba98ded5 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/nodb/nodb_test.go @@ -0,0 +1,105 @@ +// Copyright 2015 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_LedisProvider(t *testing.T) { + Convey("Test nodb session provider", t, func() { + opt := session.Options{ + Provider: "nodb", + ProviderConfig: "./tmp.db", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + Convey("Regenrate empty session", func() { + m.Get("/empty", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + }) + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/empty", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") + m.ServeHTTP(resp, req) + }) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go new file mode 100644 index 00000000000..5cb4c82ea84 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.go @@ -0,0 +1,196 @@ +// Copyright 2013 Beego Authors +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "database/sql" + "fmt" + "log" + "sync" + "time" + + _ "github.com/lib/pq" + + "github.com/macaron-contrib/session" +) + +// PostgresStore represents a postgres session store implementation. +type PostgresStore struct { + c *sql.DB + sid string + lock sync.RWMutex + data map[interface{}]interface{} +} + +// NewPostgresStore creates and returns a postgres session store. +func NewPostgresStore(c *sql.DB, sid string, kv map[interface{}]interface{}) *PostgresStore { + return &PostgresStore{ + c: c, + sid: sid, + data: kv, + } +} + +// Set sets value to given key in session. +func (s *PostgresStore) Set(key, value interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = value + return nil +} + +// Get gets value by given key in session. +func (s *PostgresStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete delete a key from session. +func (s *PostgresStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *PostgresStore) ID() string { + return s.sid +} + +// save postgres session values to database. +// must call this method to save values to database. +func (s *PostgresStore) Release() error { + data, err := session.EncodeGob(s.data) + if err != nil { + return err + } + + _, err = s.c.Exec("UPDATE session SET data=$1, expiry=$2 WHERE key=$3", + data, time.Now().Unix(), s.sid) + return err +} + +// Flush deletes all session data. +func (s *PostgresStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// PostgresProvider represents a postgres session provider implementation. +type PostgresProvider struct { + c *sql.DB + maxlifetime int64 +} + +// Init initializes postgres session provider. +// connStr: user=a password=b host=localhost port=5432 dbname=c sslmode=disable +func (p *PostgresProvider) Init(maxlifetime int64, connStr string) (err error) { + p.maxlifetime = maxlifetime + + p.c, err = sql.Open("postgres", connStr) + if err != nil { + return err + } + return p.c.Ping() +} + +// Read returns raw session store by session ID. +func (p *PostgresProvider) Read(sid string) (session.RawStore, error) { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) + if err == sql.ErrNoRows { + _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", + sid, "", time.Now().Unix()) + } + if err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(data) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = session.DecodeGob(data) + if err != nil { + return nil, err + } + } + + return NewPostgresStore(p.c, sid, kv), nil +} + +// Exist returns true if session with given ID exists. +func (p *PostgresProvider) Exist(sid string) bool { + var data []byte + err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) + if err != nil && err != sql.ErrNoRows { + panic("session/postgres: error checking existence: " + err.Error()) + } + return err != sql.ErrNoRows +} + +// Destory deletes a session by session ID. +func (p *PostgresProvider) Destory(sid string) error { + _, err := p.c.Exec("DELETE FROM session WHERE key=$1", sid) + return err +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *PostgresProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + if !p.Exist(oldsid) { + if _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", + oldsid, "", time.Now().Unix()); err != nil { + return nil, err + } + } + + if _, err = p.c.Exec("UPDATE session SET key=$1 WHERE key=$2", sid, oldsid); err != nil { + return nil, err + } + + return p.Read(sid) +} + +// Count counts and returns number of sessions. +func (p *PostgresProvider) Count() (total int) { + if err := p.c.QueryRow("SELECT COUNT(*) AS NUM FROM session").Scan(&total); err != nil { + panic("session/postgres: error counting records: " + err.Error()) + } + return total +} + +// GC calls GC to clean expired sessions. +func (p *PostgresProvider) GC() { + if _, err := p.c.Exec("DELETE FROM session WHERE EXTRACT(EPOCH FROM NOW()) - expiry > $1", p.maxlifetime); err != nil { + log.Printf("session/postgres: error garbage collecting: %v", err) + } +} + +func init() { + session.Register("postgres", &PostgresProvider{}) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go new file mode 100644 index 00000000000..ea212c729f6 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgres_test.go @@ -0,0 +1,138 @@ +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_PostgresProvider(t *testing.T) { + Convey("Test postgres session provider", t, func() { + opt := session.Options{ + Provider: "postgres", + ProviderConfig: "user=jiahuachen dbname=macaron port=5432 sslmode=disable", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + So(raw.Release(), ShouldBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + }) + + Convey("Regenrate empty session", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf48; Path=/;") + m.ServeHTTP(resp, req) + }) + + Convey("GC session", func() { + m := macaron.New() + opt2 := opt + opt2.Gclifetime = 1 + m.Use(session.Sessioner(opt2)) + + m.Get("/", func(sess session.Store) { + sess.Set("uname", "unknwon") + So(sess.ID(), ShouldNotBeEmpty) + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Flush(), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + time.Sleep(2 * time.Second) + sess.GC() + So(sess.Count(), ShouldEqual, 0) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgresql.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgresql.go deleted file mode 100644 index bfe87c69ee0..00000000000 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgresql.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2013 Beego Authors -// Copyright 2014 Unknwon -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package session - -import ( - "database/sql" - "sync" - "time" - - _ "github.com/lib/pq" - - "github.com/macaron-contrib/session" -) - -// PostgresqlSessionStore represents a postgresql session store implementation. -type PostgresqlSessionStore struct { - c *sql.DB - sid string - lock sync.RWMutex - data map[interface{}]interface{} -} - -// Set sets value to given key in session. -func (s *PostgresqlSessionStore) Set(key, value interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data[key] = value - return nil -} - -// Get gets value by given key in session. -func (s *PostgresqlSessionStore) Get(key interface{}) interface{} { - s.lock.RLock() - defer s.lock.RUnlock() - - return s.data[key] -} - -// Delete delete a key from session. -func (s *PostgresqlSessionStore) Delete(key interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - delete(s.data, key) - return nil -} - -// ID returns current session ID. -func (s *PostgresqlSessionStore) ID() string { - return s.sid -} - -// save postgresql session values to database. -// must call this method to save values to database. -func (s *PostgresqlSessionStore) Release() error { - defer s.c.Close() - - data, err := session.EncodeGob(s.data) - if err != nil { - return err - } - - _, err = s.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3", - data, time.Now().Format(time.RFC3339), s.sid) - return err -} - -// Flush deletes all session data. -func (s *PostgresqlSessionStore) Flush() error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = make(map[interface{}]interface{}) - return nil -} - -// PostgresqlProvider represents a postgresql session provider implementation. -type PostgresqlProvider struct { - maxlifetime int64 - connStr string -} - -func (p *PostgresqlProvider) connectInit() *sql.DB { - db, e := sql.Open("postgres", p.connStr) - if e != nil { - return nil - } - return db -} - -// Init initializes memory session provider. -func (p *PostgresqlProvider) Init(maxlifetime int64, connStr string) error { - p.maxlifetime = maxlifetime - p.connStr = connStr - return nil -} - -// Read returns raw session store by session ID. -func (p *PostgresqlProvider) Read(sid string) (session.RawStore, error) { - c := p.connectInit() - row := c.QueryRow("select session_data from session where session_key=$1", sid) - var sessiondata []byte - err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - _, err = c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)", - sid, "", time.Now().Format(time.RFC3339)) - - if err != nil { - return nil, err - } - } else if err != nil { - return nil, err - } - - var kv map[interface{}]interface{} - if len(sessiondata) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob(sessiondata) - if err != nil { - return nil, err - } - } - rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv} - return rs, nil -} - -// Exist returns true if session with given ID exists. -func (p *PostgresqlProvider) Exist(sid string) bool { - c := p.connectInit() - defer c.Close() - row := c.QueryRow("select session_data from session where session_key=$1", sid) - var sessiondata []byte - err := row.Scan(&sessiondata) - - if err == sql.ErrNoRows { - return false - } else { - return true - } -} - -// Destory deletes a session by session ID. -func (p *PostgresqlProvider) Destory(sid string) (err error) { - c := p.connectInit() - if _, err = c.Exec("DELETE FROM session where session_key=$1", sid); err != nil { - return err - } - return c.Close() -} - -// Regenerate regenerates a session store from old session ID to new one. -func (p *PostgresqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - c := p.connectInit() - row := c.QueryRow("select session_data from session where session_key=$1", oldsid) - var sessiondata []byte - err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)", - oldsid, "", time.Now().Format(time.RFC3339)) - } - c.Exec("update session set session_key=$1 where session_key=$2", sid, oldsid) - var kv map[interface{}]interface{} - if len(sessiondata) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob(sessiondata) - if err != nil { - return nil, err - } - } - rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv} - return rs, nil -} - -// Count counts and returns number of sessions. -func (p *PostgresqlProvider) Count() int { - c := p.connectInit() - defer c.Close() - var total int - err := c.QueryRow("SELECT count(*) as num from session").Scan(&total) - if err != nil { - return 0 - } - return total -} - -// GC calls GC to clean expired sessions. -func (mp *PostgresqlProvider) GC() { - c := mp.connectInit() - c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime) - c.Close() -} - -func init() { - session.Register("postgresql", &PostgresqlProvider{}) -} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go index 211866317f3..6d6a2c464c8 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go @@ -16,31 +16,39 @@ package session import ( - "strconv" + "fmt" "strings" "sync" + "time" - "github.com/beego/redigo/redis" + "github.com/Unknwon/com" + "gopkg.in/ini.v1" + "gopkg.in/redis.v2" "github.com/macaron-contrib/session" ) -// redis max pool size -var MAX_POOL_SIZE = 100 +// RedisStore represents a redis session store implementation. +type RedisStore struct { + c *redis.Client + sid string + duration time.Duration + lock sync.RWMutex + data map[interface{}]interface{} +} -var redisPool chan redis.Conn - -// RedisSessionStore represents a redis session store implementation. -type RedisSessionStore struct { - p *redis.Pool - sid string - lock sync.RWMutex - data map[interface{}]interface{} - maxlifetime int64 +// NewRedisStore creates and returns a redis session store. +func NewRedisStore(c *redis.Client, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { + return &RedisStore{ + c: c, + sid: sid, + duration: dur, + data: kv, + } } // Set sets value to given key in session. -func (s *RedisSessionStore) Set(key, val interface{}) error { +func (s *RedisStore) Set(key, val interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -49,7 +57,7 @@ func (s *RedisSessionStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *RedisSessionStore) Get(key interface{}) interface{} { +func (s *RedisStore) Get(key interface{}) interface{} { s.lock.RLock() defer s.lock.RUnlock() @@ -57,7 +65,7 @@ func (s *RedisSessionStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *RedisSessionStore) Delete(key interface{}) error { +func (s *RedisStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() @@ -66,26 +74,22 @@ func (s *RedisSessionStore) Delete(key interface{}) error { } // ID returns current session ID. -func (s *RedisSessionStore) ID() string { +func (s *RedisStore) ID() string { return s.sid } // Release releases resource and save data to provider. -func (s *RedisSessionStore) Release() error { - c := s.p.Get() - defer c.Close() - +func (s *RedisStore) Release() error { data, err := session.EncodeGob(s.data) if err != nil { return err } - _, err = c.Do("SETEX", s.sid, s.maxlifetime, string(data)) - return err + return s.c.SetEx(s.sid, s.duration, string(data)).Err() } // Flush deletes all session data. -func (s *RedisSessionStore) Flush() error { +func (s *RedisStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() @@ -95,59 +99,65 @@ func (s *RedisSessionStore) Flush() error { // RedisProvider represents a redis session provider implementation. type RedisProvider struct { - maxlifetime int64 - connAddr string - poolsize int - password string - poollist *redis.Pool + c *redis.Client + duration time.Duration } -// Init initializes memory session provider. -// connStr: ,, -// e.g. 127.0.0.1:6379,100,macaron -func (p *RedisProvider) Init(maxlifetime int64, connStr string) error { - p.maxlifetime = maxlifetime - configs := strings.Split(connStr, ",") - if len(configs) > 0 { - p.connAddr = configs[0] +// Init initializes redis session provider. +// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180 +func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) { + p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime)) + if err != nil { + return err } - if len(configs) > 1 { - poolsize, err := strconv.Atoi(configs[1]) - if err != nil || poolsize <= 0 { - p.poolsize = MAX_POOL_SIZE - } else { - p.poolsize = poolsize - } - } else { - p.poolsize = MAX_POOL_SIZE - } - if len(configs) > 2 { - p.password = configs[2] - } - p.poollist = redis.NewPool(func() (redis.Conn, error) { - c, err := redis.Dial("tcp", p.connAddr) - if err != nil { - return nil, err - } - if p.password != "" { - if _, err := c.Do("AUTH", p.password); err != nil { - c.Close() - return nil, err - } - } - return c, err - }, p.poolsize) - return p.poollist.Get().Err() + cfg, err := ini.Load([]byte(strings.Replace(configs, ",", "\n", -1))) + if err != nil { + return err + } + + opt := &redis.Options{ + Network: "tcp", + } + for k, v := range cfg.Section("").KeysHash() { + switch k { + case "network": + opt.Network = v + case "addr": + opt.Addr = v + case "password": + opt.Password = v + case "db": + opt.DB = com.StrTo(v).MustInt64() + case "pool_size": + opt.PoolSize = com.StrTo(v).MustInt() + case "idle_timeout": + opt.IdleTimeout, err = time.ParseDuration(v + "s") + if err != nil { + return fmt.Errorf("error parsing idle timeout: %v", err) + } + default: + return fmt.Errorf("session/redis: unsupported option '%s'", k) + } + } + + p.c = redis.NewClient(opt) + return p.c.Ping().Err() } // Read returns raw session store by session ID. func (p *RedisProvider) Read(sid string) (session.RawStore, error) { - c := p.poollist.Get() - defer c.Close() + if !p.Exist(sid) { + if err := p.c.Set(sid, "").Err(); err != nil { + return nil, err + } + } - kvs, err := redis.String(c.Do("GET", sid)) var kv map[interface{}]interface{} + kvs, err := p.c.Get(sid).Result() + if err != nil { + return nil, err + } if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { @@ -157,48 +167,41 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) { } } - rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime} - return rs, nil + return NewRedisStore(p.c, sid, p.duration, kv), nil } // Exist returns true if session with given ID exists. func (p *RedisProvider) Exist(sid string) bool { - c := p.poollist.Get() - defer c.Close() - - if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 { - return false - } else { - return true - } + has, err := p.c.Exists(sid).Result() + return err == nil && has } // Destory deletes a session by session ID. func (p *RedisProvider) Destory(sid string) error { - c := p.poollist.Get() - defer c.Close() - - _, err := c.Do("DEL", sid) - return err + return p.c.Del(sid).Err() } // Regenerate regenerates a session store from old session ID to new one. -func (p *RedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { - c := p.poollist.Get() - defer c.Close() - - if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 { - // oldsid doesn't exists, set the new sid directly - // ignore error here, since if it return error - // the existed value will be 0 - c.Do("SET", sid, "", "EX", p.maxlifetime) - } else { - c.Do("RENAME", oldsid, sid) - c.Do("EXPIRE", sid, p.maxlifetime) +func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } else if !p.Exist(oldsid) { + // Make a fake old session. + if err = p.c.SetEx(oldsid, p.duration, "").Err(); err != nil { + return nil, err + } + } + + if err = p.c.Rename(oldsid, sid).Err(); err != nil { + return nil, err } - kvs, err := redis.String(c.Do("GET", sid)) var kv map[interface{}]interface{} + kvs, err := p.c.Get(sid).Result() + if err != nil { + return nil, err + } + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { @@ -208,14 +211,12 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) } } - rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime} - return rs, nil + return NewRedisStore(p.c, sid, p.duration, kv), nil } // Count counts and returns number of sessions. func (p *RedisProvider) Count() int { - // FIXME - return 0 + return int(p.c.DbSize().Val()) } // GC calls GC to clean expired sessions. diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.goconvey b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.goconvey new file mode 100644 index 00000000000..8485e986e45 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.goconvey @@ -0,0 +1 @@ +ignore \ No newline at end of file diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go new file mode 100644 index 00000000000..9fd8e6518f8 --- /dev/null +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis_test.go @@ -0,0 +1,107 @@ +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/Unknwon/macaron" + . "github.com/smartystreets/goconvey/convey" + + "github.com/macaron-contrib/session" +) + +func Test_RedisProvider(t *testing.T) { + Convey("Test redis session provider", t, func() { + opt := session.Options{ + Provider: "redis", + ProviderConfig: "addr=:6379", + } + + Convey("Basic operation", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + sess.Set("uname", "unknwon") + }) + m.Get("/reg", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := raw.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + }) + m.Get("/get", func(ctx *macaron.Context, sess session.Store) { + sid := sess.ID() + So(sid, ShouldNotBeEmpty) + + raw, err := sess.Read(sid) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + + uname := sess.Get("uname") + So(uname, ShouldNotBeNil) + So(uname, ShouldEqual, "unknwon") + + So(sess.Delete("uname"), ShouldBeNil) + So(sess.Get("uname"), ShouldBeNil) + + So(sess.Destory(ctx), ShouldBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + m.ServeHTTP(resp, req) + + cookie := resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/reg", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + + cookie = resp.Header().Get("Set-Cookie") + + resp = httptest.NewRecorder() + req, err = http.NewRequest("GET", "/get", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", cookie) + m.ServeHTTP(resp, req) + }) + + Convey("Regenrate empty session", func() { + m := macaron.New() + m.Use(session.Sessioner(opt)) + m.Get("/", func(ctx *macaron.Context, sess session.Store) { + raw, err := sess.RegenerateId(ctx) + So(err, ShouldBeNil) + So(raw, ShouldNotBeNil) + }) + + resp := httptest.NewRecorder() + req, err := http.NewRequest("GET", "/", nil) + So(err, ShouldBeNil) + req.Header.Set("Cookie", "MacaronSession=ad2c7e3cbecfcf486; Path=/;") + m.ServeHTTP(resp, req) + }) + }) +} diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go index 204c65d7668..9cc1d528749 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/session.go @@ -13,7 +13,7 @@ // License for the specific language governing permissions and limitations // under the License. -// Package session a middleware that provides the session manager of Macaron. +// Package session a middleware that provides the session management of Macaron. package session // NOTE: last sync 000033e on Nov 4, 2014. @@ -28,7 +28,7 @@ import ( "github.com/Unknwon/macaron" ) -const _VERSION = "0.1.1" +const _VERSION = "0.1.6" func Version() string { return _VERSION @@ -37,11 +37,11 @@ func Version() string { // RawStore is the interface that operates the session data. type RawStore interface { // Set sets value to given key in session. - Set(key, value interface{}) error + Set(interface{}, interface{}) error // Get gets value by given key in session. - Get(key interface{}) interface{} - // Delete delete a key from session. - Delete(key interface{}) error + Get(interface{}) interface{} + // Delete deletes a key from session. + Delete(interface{}) error // ID returns current session ID. ID() string // Release releases session resource and save data to provider. @@ -54,7 +54,7 @@ type RawStore interface { type Store interface { RawStore // Read returns raw session store by session ID. - Read(sid string) (RawStore, error) + Read(string) (RawStore, error) // Destory deletes a session. Destory(*macaron.Context) error // RegenerateId regenerates a session store from old session ID to new one. @@ -111,7 +111,7 @@ func prepareOptions(options []Options) Options { if len(opt.Provider) == 0 { opt.Provider = sec.Key("PROVIDER").MustString("memory") } - if len(opt.ProviderConfig) == 0 && opt.Provider == "file" { + if len(opt.ProviderConfig) == 0 { opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions") } if len(opt.CookieName) == 0 { @@ -155,7 +155,7 @@ func Sessioner(options ...Options) macaron.Handler { return func(ctx *macaron.Context) { sess, err := manager.Start(ctx) if err != nil { - panic("session: " + err.Error()) + panic("session(start): " + err.Error()) } // Get flash. @@ -187,8 +187,8 @@ func Sessioner(options ...Options) macaron.Handler { ctx.Next() - if sess.Release() != nil { - panic("session: " + err.Error()) + if err = sess.Release(); err != nil { + panic("session(release): " + err.Error()) } } } @@ -242,17 +242,14 @@ type Manager struct { func NewManager(name string, opt Options) (*Manager, error) { p, ok := providers[name] if !ok { - return nil, fmt.Errorf("session: unknown provider ‘%q’(forgotten import?)", name) + return nil, fmt.Errorf("session: unknown provider '%s'(forgotten import?)", name) } - if err := p.Init(opt.Maxlifetime, opt.ProviderConfig); err != nil { - return nil, err - } - return &Manager{p, opt}, nil + return &Manager{p, opt}, p.Init(opt.Maxlifetime, opt.ProviderConfig) } // sessionId generates a new session ID with rand string, unix nano time, remote addr by hash function. func (m *Manager) sessionId() string { - return hex.EncodeToString(generateRandomKey(m.opt.IDLength)) + return hex.EncodeToString(generateRandomKey(m.opt.IDLength / 2)) } // Start starts a session by generating new one @@ -315,16 +312,9 @@ func (m *Manager) Destory(ctx *macaron.Context) error { func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) { sid := m.sessionId() oldsid := ctx.GetCookie(m.opt.CookieName) - if len(oldsid) == 0 { - sess, err = m.provider.Read(oldsid) - if err != nil { - return nil, err - } - } else { - sess, err = m.provider.Regenerate(oldsid, sid) - if err != nil { - return nil, err - } + sess, err = m.provider.Regenerate(oldsid, sid) + if err != nil { + return nil, err } ck := &http.Cookie{ Name: m.opt.CookieName, diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go index 327c1d0c6a5..82efc277c61 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/session_test.go @@ -42,7 +42,7 @@ func Test_Sessioner(t *testing.T) { m.ServeHTTP(resp, req) }) - Convey("Register invalid provider that", t, func() { + Convey("Register invalid provider", t, func() { Convey("Provider not exists", func() { defer func() { So(recover(), ShouldNotBeNil) diff --git a/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go b/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go index a165de83070..6c9ea495fe5 100644 --- a/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go +++ b/Godeps/_workspace/src/github.com/macaron-contrib/session/utils.go @@ -24,39 +24,19 @@ import ( "github.com/Unknwon/com" ) -func init() { - gob.Register([]interface{}{}) - gob.Register(map[int]interface{}{}) - gob.Register(map[string]interface{}{}) - gob.Register(map[interface{}]interface{}{}) - gob.Register(map[string]string{}) - gob.Register(map[int]string{}) - gob.Register(map[int]int{}) - gob.Register(map[int]int64{}) -} - func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { for _, v := range obj { gob.Register(v) } buf := bytes.NewBuffer(nil) - enc := gob.NewEncoder(buf) - err := enc.Encode(obj) - if err != nil { - return []byte(""), err - } - return buf.Bytes(), nil + err := gob.NewEncoder(buf).Encode(obj) + return buf.Bytes(), err } -func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) { +func DecodeGob(encoded []byte) (out map[interface{}]interface{}, err error) { buf := bytes.NewBuffer(encoded) - dec := gob.NewDecoder(buf) - var out map[interface{}]interface{} - err := dec.Decode(&out) - if err != nil { - return nil, err - } - return out, nil + err = gob.NewDecoder(buf).Decode(&out) + return out, err } // generateRandomKey creates a random key with the given strength. diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md index 9d04745fa78..4383f0cd4ce 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/README.md @@ -41,12 +41,18 @@ FAQ > See: https://github.com/mattn/go-sqlite3/issues/106 > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html +* Want to get time.Time with current locale + + Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`. + License ------- MIT: http://mattn.mit-license.org/2012 -sqlite.c, sqlite3.h, sqlite3ext.h +sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h + +The -binding suffix was added to avoid build failures under gccgo. In this repository, those files are amalgamation code that copied from SQLite3. The license of those codes are depend on the license of SQLite3. diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go index 270446aa724..3807c606b22 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/backup.go @@ -6,7 +6,7 @@ package sqlite3 /* -#include +#include #include */ import "C" diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go index a0061889465..1ccbe5bf858 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/error_test.go @@ -231,6 +231,12 @@ func TestExtendedErrorCodes_Unique(t *testing.T) { t.Errorf("Wrong extended error code: %d != %d", sqliteErr.ExtendedCode, ErrConstraintUnique) } + extended := sqliteErr.Code.Extend(3).Error() + expected := "constraint failed" + if extended != expected { + t.Errorf("Wrong basic error code: %q != %q", + extended, expected) + } } } diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.c b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.c similarity index 100% rename from Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.c rename to Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.c diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.h b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.h similarity index 100% rename from Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.h rename to Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3-binding.h diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go index d446fb69f42..f4de3fd6f1c 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3.go @@ -6,7 +6,10 @@ package sqlite3 /* -#include +#cgo CFLAGS: -std=gnu99 +#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE +#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS +#include #include #include @@ -44,14 +47,23 @@ _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) { #include #include -static long -_sqlite3_last_insert_rowid(sqlite3* db) { - return (long) sqlite3_last_insert_rowid(db); +static int +_sqlite3_exec(sqlite3* db, const char* pcmd, long* rowid, long* changes) +{ + int rv = sqlite3_exec(db, pcmd, 0, 0, 0); + *rowid = (long) sqlite3_last_insert_rowid(db); + *changes = (long) sqlite3_changes(db); + return rv; } -static long -_sqlite3_changes(sqlite3* db) { - return (long) sqlite3_changes(db); +static int +_sqlite3_step(sqlite3_stmt* stmt, long* rowid, long* changes) +{ + int rv = sqlite3_step(stmt); + sqlite3* db = sqlite3_db_handle(stmt); + *rowid = (long) sqlite3_last_insert_rowid(db); + *changes = (long) sqlite3_changes(db); + return rv; } */ @@ -60,8 +72,11 @@ import ( "database/sql" "database/sql/driver" "errors" + "fmt" "io" + "net/url" "runtime" + "strconv" "strings" "time" "unsafe" @@ -102,7 +117,8 @@ type SQLiteDriver struct { // Conn struct. type SQLiteConn struct { - db *C.sqlite3 + db *C.sqlite3 + loc *time.Location } // Tx struct. @@ -114,6 +130,8 @@ type SQLiteTx struct { type SQLiteStmt struct { c *SQLiteConn s *C.sqlite3_stmt + nv int + nn []string t string closed bool cls bool @@ -174,7 +192,7 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err if s.(*SQLiteStmt).s != nil { na := s.NumInput() if len(args) < na { - return nil, errors.New("args is not enough to execute query") + return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args)) } res, err = s.Exec(args[:na]) if err != nil && err != driver.ErrSkip { @@ -201,6 +219,9 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro } s.(*SQLiteStmt).cls = true na := s.NumInput() + if len(args) < na { + return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args)) + } rows, err := s.Query(args[:na]) if err != nil && err != driver.ErrSkip { s.Close() @@ -220,14 +241,13 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro func (c *SQLiteConn) exec(cmd string) (driver.Result, error) { pcmd := C.CString(cmd) defer C.free(unsafe.Pointer(pcmd)) - rv := C.sqlite3_exec(c.db, pcmd, nil, nil, nil) + + var rowid, changes C.long + rv := C._sqlite3_exec(c.db, pcmd, &rowid, &changes) if rv != C.SQLITE_OK { return nil, c.lastError() } - return &SQLiteResult{ - int64(C._sqlite3_last_insert_rowid(c.db)), - int64(C._sqlite3_changes(c.db)), - }, nil + return &SQLiteResult{int64(rowid), int64(changes)}, nil } // Begin transaction. @@ -248,11 +268,51 @@ func errorString(err Error) string { // file:test.db?cache=shared&mode=memory // :memory: // file::memory: +// go-sqlite handle especially query parameters. +// _loc=XXX +// Specify location of time format. It's possible to specify "auto". +// _busy_timeout=XXX +// Specify value for sqlite3_busy_timeout. func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { if C.sqlite3_threadsafe() == 0 { return nil, errors.New("sqlite library was not compiled for thread-safe operation") } + var loc *time.Location + busy_timeout := 5000 + pos := strings.IndexRune(dsn, '?') + if pos >= 1 { + params, err := url.ParseQuery(dsn[pos+1:]) + if err != nil { + return nil, err + } + + // _loc + if val := params.Get("_loc"); val != "" { + if val == "auto" { + loc = time.Local + } else { + loc, err = time.LoadLocation(val) + if err != nil { + return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err) + } + } + } + + // _busy_timeout + if val := params.Get("_busy_timeout"); val != "" { + iv, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) + } + busy_timeout = int(iv) + } + + if !strings.HasPrefix(dsn, "file:") { + dsn = dsn[:pos] + } + } + var db *C.sqlite3 name := C.CString(dsn) defer C.free(unsafe.Pointer(name)) @@ -268,12 +328,12 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, errors.New("sqlite succeeded without returning a database") } - rv = C.sqlite3_busy_timeout(db, 5000) + rv = C.sqlite3_busy_timeout(db, C.int(busy_timeout)) if rv != C.SQLITE_OK { return nil, Error{Code: ErrNo(rv)} } - conn := &SQLiteConn{db} + conn := &SQLiteConn{db: db, loc: loc} if len(d.Extensions) > 0 { rv = C.sqlite3_enable_load_extension(db, 1) @@ -281,21 +341,15 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) } - stmt, err := conn.Prepare("SELECT load_extension(?);") - if err != nil { - return nil, err - } - for _, extension := range d.Extensions { - if _, err = stmt.Exec([]driver.Value{extension}); err != nil { - return nil, err + cext := C.CString(extension) + defer C.free(unsafe.Pointer(cext)) + rv = C.sqlite3_load_extension(db, cext, nil, nil) + if rv != C.SQLITE_OK { + return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) } } - if err = stmt.Close(); err != nil { - return nil, err - } - rv = C.sqlite3_enable_load_extension(db, 0) if rv != C.SQLITE_OK { return nil, errors.New(C.GoString(C.sqlite3_errmsg(db))) @@ -333,10 +387,18 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { return nil, c.lastError() } var t string - if tail != nil && C.strlen(tail) > 0 { + if tail != nil && *tail != '\000' { t = strings.TrimSpace(C.GoString(tail)) } - ss := &SQLiteStmt{c: c, s: s, t: t} + nv := int(C.sqlite3_bind_parameter_count(s)) + var nn []string + for i := 0; i < nv; i++ { + pn := C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1))) + if len(pn) > 1 && pn[0] == '$' && 48 <= pn[1] && pn[1] <= 57 { + nn = append(nn, C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1)))) + } + } + ss := &SQLiteStmt{c: c, s: s, nv: nv, nn: nn, t: t} runtime.SetFinalizer(ss, (*SQLiteStmt).Close) return ss, nil } @@ -360,7 +422,12 @@ func (s *SQLiteStmt) Close() error { // Return a number of parameters. func (s *SQLiteStmt) NumInput() int { - return int(C.sqlite3_bind_parameter_count(s.s)) + return s.nv +} + +type bindArg struct { + n int + v driver.Value } func (s *SQLiteStmt) bind(args []driver.Value) error { @@ -369,8 +436,24 @@ func (s *SQLiteStmt) bind(args []driver.Value) error { return s.c.lastError() } - for i, v := range args { - n := C.int(i + 1) + var vargs []bindArg + narg := len(args) + vargs = make([]bindArg, narg) + if len(s.nn) > 0 { + for i, v := range s.nn { + if pi, err := strconv.Atoi(v[1:]); err == nil { + vargs[i] = bindArg{pi, args[i]} + } + } + } else { + for i, v := range args { + vargs[i] = bindArg{i + 1, v} + } + } + + for _, varg := range vargs { + n := C.int(varg.n) + v := varg.v switch v := v.(type) { case nil: rv = C.sqlite3_bind_null(s.s, n) @@ -431,19 +514,18 @@ func (r *SQLiteResult) RowsAffected() (int64, error) { func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { if err := s.bind(args); err != nil { C.sqlite3_reset(s.s) + C.sqlite3_clear_bindings(s.s) return nil, err } - rv := C.sqlite3_step(s.s) + var rowid, changes C.long + rv := C._sqlite3_step(s.s, &rowid, &changes) if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { + err := s.c.lastError() C.sqlite3_reset(s.s) - return nil, s.c.lastError() + C.sqlite3_clear_bindings(s.s) + return nil, err } - - res := &SQLiteResult{ - int64(C._sqlite3_last_insert_rowid(s.c.db)), - int64(C._sqlite3_changes(s.c.db)), - } - return res, nil + return &SQLiteResult{int64(rowid), int64(changes)}, nil } // Close the rows. @@ -499,7 +581,22 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) switch rc.decltype[i] { case "timestamp", "datetime", "date": - dest[i] = time.Unix(val, 0).Local() + unixTimestamp := strconv.FormatInt(val, 10) + var t time.Time + if len(unixTimestamp) == 13 { + duration, err := time.ParseDuration(unixTimestamp + "ms") + if err != nil { + return fmt.Errorf("error parsing %s value %d, %s", rc.decltype[i], val, err) + } + epoch := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) + t = epoch.Add(duration) + } else { + t = time.Unix(val, 0) + } + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t case "boolean": dest[i] = val > 0 default: @@ -531,16 +628,21 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { switch rc.decltype[i] { case "timestamp", "datetime", "date": + var t time.Time for _, format := range SQLiteTimestampFormats { if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil { - dest[i] = timeVal.Local() + t = timeVal break } } if err != nil { // The column is a time value, so return the zero time on parse failure. - dest[i] = time.Time{} + t = time.Time{} } + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t default: dest[i] = []byte(s) } diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go new file mode 100644 index 00000000000..a1cd2172d79 --- /dev/null +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_fts3_test.go @@ -0,0 +1,83 @@ +// Copyright (C) 2015 Yasuhiro Matsumoto . +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file. + +package sqlite3 + +import ( + "database/sql" + "os" + "testing" +) + +func TestFTS3(t *testing.T) { + tempFilename := TempFilename() + db, err := sql.Open("sqlite3", tempFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer os.Remove(tempFilename) + defer db.Close() + + _, err = db.Exec("DROP TABLE foo") + _, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)") + if err != nil { + t.Fatal("Failed to create table:", err) + } + + _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`) + if err != nil { + t.Fatal("Failed to insert value:", err) + } + + _, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`) + if err != nil { + t.Fatal("Failed to insert value:", err) + } + + rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'") + if err != nil { + t.Fatal("Unable to query foo table:", err) + } + defer rows.Close() + + for rows.Next() { + var id int + var value string + + if err := rows.Scan(&id, &value); err != nil { + t.Error("Unable to scan results:", err) + continue + } + + if id == 1 && value != `今日の 晩御飯は 天麩羅よ` { + t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value) + } else if id == 2 && value != `今日は いい 天気だ` { + t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value) + } + } + + rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'") + if err != nil { + t.Fatal("Unable to query foo table:", err) + } + defer rows.Close() + + var value string + if !rows.Next() { + t.Fatal("Result should be only one") + } + + if err := rows.Scan(&value); err != nil { + t.Fatal("Unable to scan results:", err) + } + + if value != `今日の 晩御飯は 天麩羅よ` { + t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value) + } + + if rows.Next() { + t.Fatal("Result should be only one") + } +} diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go index 54b6c7a5e25..8d98b4a3a6c 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_other.go @@ -9,6 +9,6 @@ package sqlite3 /* #cgo CFLAGS: -I. #cgo linux LDFLAGS: -ldl -#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE +#cgo LDFLAGS: -lpthread */ import "C" diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go index 9cc5a0ec5ed..aa8601181b5 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_test.go @@ -9,8 +9,10 @@ import ( "crypto/rand" "database/sql" "encoding/hex" + "net/url" "os" "path/filepath" + "strings" "testing" "time" @@ -309,6 +311,7 @@ func TestTimestamp(t *testing.T) { {"0000-00-00 00:00:00", time.Time{}}, {timestamp1, timestamp1}, {timestamp1.Unix(), timestamp1}, + {timestamp1.UnixNano() / int64(time.Millisecond), timestamp1}, {timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1}, {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1}, {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1}, @@ -633,6 +636,102 @@ func TestWAL(t *testing.T) { } } +func TestTimezoneConversion(t *testing.T) { + zones := []string{"UTC", "US/Central", "US/Pacific", "Local"} + for _, tz := range zones { + tempFilename := TempFilename() + db, err := sql.Open("sqlite3", tempFilename+"?_loc="+url.QueryEscape(tz)) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer os.Remove(tempFilename) + defer db.Close() + + _, err = db.Exec("DROP TABLE foo") + _, err = db.Exec("CREATE TABLE foo(id INTEGER, ts TIMESTAMP, dt DATETIME)") + if err != nil { + t.Fatal("Failed to create table:", err) + } + + loc, err := time.LoadLocation(tz) + if err != nil { + t.Fatal("Failed to load location:", err) + } + + timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC) + timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC) + timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC) + tests := []struct { + value interface{} + expected time.Time + }{ + {"nonsense", time.Time{}.In(loc)}, + {"0000-00-00 00:00:00", time.Time{}.In(loc)}, + {timestamp1, timestamp1.In(loc)}, + {timestamp1.Unix(), timestamp1.In(loc)}, + {timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1.In(loc)}, + {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1.In(loc)}, + {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1.In(loc)}, + {timestamp1.Format("2006-01-02 15:04:05"), timestamp1.In(loc)}, + {timestamp1.Format("2006-01-02T15:04:05"), timestamp1.In(loc)}, + {timestamp2, timestamp2.In(loc)}, + {"2006-01-02 15:04:05.123456789", timestamp2.In(loc)}, + {"2006-01-02T15:04:05.123456789", timestamp2.In(loc)}, + {"2012-11-04", timestamp3.In(loc)}, + {"2012-11-04 00:00", timestamp3.In(loc)}, + {"2012-11-04 00:00:00", timestamp3.In(loc)}, + {"2012-11-04 00:00:00.000", timestamp3.In(loc)}, + {"2012-11-04T00:00", timestamp3.In(loc)}, + {"2012-11-04T00:00:00", timestamp3.In(loc)}, + {"2012-11-04T00:00:00.000", timestamp3.In(loc)}, + } + for i := range tests { + _, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value) + if err != nil { + t.Fatal("Failed to insert timestamp:", err) + } + } + + rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC") + if err != nil { + t.Fatal("Unable to query foo table:", err) + } + defer rows.Close() + + seen := 0 + for rows.Next() { + var id int + var ts, dt time.Time + + if err := rows.Scan(&id, &ts, &dt); err != nil { + t.Error("Unable to scan results:", err) + continue + } + if id < 0 || id >= len(tests) { + t.Error("Bad row id: ", id) + continue + } + seen++ + if !tests[id].expected.Equal(ts) { + t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, ts) + } + if !tests[id].expected.Equal(dt) { + t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) + } + if tests[id].expected.Location().String() != ts.Location().String() { + t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), ts.Location().String()) + } + if tests[id].expected.Location().String() != dt.Location().String() { + t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), dt.Location().String()) + } + } + + if seen != len(tests) { + t.Errorf("Expected to see %d rows", len(tests)) + } + } +} + func TestSuite(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { @@ -742,3 +841,107 @@ func TestStress(t *testing.T) { db.Close() } } + +func TestDateTimeLocal(t *testing.T) { + zone := "Asia/Tokyo" + tempFilename := TempFilename() + db, err := sql.Open("sqlite3", tempFilename+"?_loc="+zone) + if err != nil { + t.Fatal("Failed to open database:", err) + } + db.Exec("CREATE TABLE foo (dt datetime);") + db.Exec("INSERT INTO foo VALUES('2015-03-05 15:16:17');") + + row := db.QueryRow("select * from foo") + var d time.Time + err = row.Scan(&d) + if err != nil { + t.Fatal("Failed to scan datetime:", err) + } + if d.Hour() == 15 || !strings.Contains(d.String(), "JST") { + t.Fatal("Result should have timezone", d) + } + db.Close() + + db, err = sql.Open("sqlite3", tempFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + + row = db.QueryRow("select * from foo") + err = row.Scan(&d) + if err != nil { + t.Fatal("Failed to scan datetime:", err) + } + if d.UTC().Hour() != 15 || !strings.Contains(d.String(), "UTC") { + t.Fatalf("Result should not have timezone %v %v", zone, d.String()) + } + + _, err = db.Exec("DELETE FROM foo") + if err != nil { + t.Fatal("Failed to delete table:", err) + } + dt, err := time.Parse("2006/1/2 15/4/5 -0700 MST", "2015/3/5 15/16/17 +0900 JST") + if err != nil { + t.Fatal("Failed to parse datetime:", err) + } + db.Exec("INSERT INTO foo VALUES(?);", dt) + + db.Close() + db, err = sql.Open("sqlite3", tempFilename+"?_loc="+zone) + if err != nil { + t.Fatal("Failed to open database:", err) + } + + row = db.QueryRow("select * from foo") + err = row.Scan(&d) + if err != nil { + t.Fatal("Failed to scan datetime:", err) + } + if d.Hour() != 15 || !strings.Contains(d.String(), "JST") { + t.Fatalf("Result should have timezone %v %v", zone, d.String()) + } +} + +func TestVersion(t *testing.T) { + s, n, id := Version() + if s == "" || n == 0 || id == "" { + t.Errorf("Version failed %q, %d, %q\n", s, n, id) + } +} + +func TestNumberNamedParams(t *testing.T) { + tempFilename := TempFilename() + db, err := sql.Open("sqlite3", tempFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer os.Remove(tempFilename) + defer db.Close() + + _, err = db.Exec(` + create table foo (id integer, name text, extra text); + `) + if err != nil { + t.Error("Failed to call db.Query:", err) + } + + _, err = db.Exec(`insert into foo(id, name, extra) values($1, $2, $2)`, 1, "foo") + if err != nil { + t.Error("Failed to call db.Exec:", err) + } + + row := db.QueryRow(`select id, extra from foo where id = $1 and extra = $2`, 1, "foo") + if row == nil { + t.Error("Failed to call db.QueryRow") + } + var id int + var extra string + err = row.Scan(&id, &extra) + if err != nil { + t.Error("Failed to db.Scan:", err) + } + if id != 1 || extra != "foo" { + t.Error("Failed to db.QueryRow: not matched results") + } +} diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go index 84eb457f61b..abc8384e4b8 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3_windows.go @@ -2,6 +2,7 @@ // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. +// +build windows package sqlite3 @@ -9,6 +10,5 @@ package sqlite3 #cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe #cgo windows,386 CFLAGS: -D_localtime32=localtime #cgo LDFLAGS: -lmingwex -lmingw32 -#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE */ import "C" diff --git a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3ext.h b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3ext.h index ecf93f62f6c..7cc58b6f86b 100644 --- a/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3ext.h +++ b/Godeps/_workspace/src/github.com/mattn/go-sqlite3/sqlite3ext.h @@ -17,7 +17,7 @@ */ #ifndef _SQLITE3EXT_H_ #define _SQLITE3EXT_H_ -#include "sqlite3.h" +#include "sqlite3-binding.h" typedef struct sqlite3_api_routines sqlite3_api_routines; diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml b/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml index 01bb8d44ee9..a035125c358 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml +++ b/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml @@ -8,7 +8,7 @@ install: - export GOPATH="$HOME/gopath" - mkdir -p "$GOPATH/src/golang.org/x" - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2" - - go get -v -t -d -tags='appengine appenginevm' golang.org/x/oauth2/... + - go get -v -t -d golang.org/x/oauth2/... script: - - go test -v -tags='appengine appenginevm' golang.org/x/oauth2/... + - go test -v golang.org/x/oauth2/... diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md index d76faef21a8..46aa2b12dda 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md +++ b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md @@ -1,25 +1,31 @@ -# Contributing +# Contributing to Go -We don't use GitHub pull requests but use Gerrit for code reviews, -similar to the Go project. +Go is an open source project. -1. Sign one of the contributor license agreements below. -2. `go get golang.org/x/review/git-codereview` to install the code reviewing tool. -3. Get the package by running `go get -d golang.org/x/oauth2`. -Make changes and create a change by running `git codereview change `, provide a command message, and use `git codereview mail` to create a Gerrit CL. -Keep amending to the change and mail as your recieve feedback. +It is the work of hundreds of contributors. We appreciate your help! -For more information about the workflow, see Go's [Contribution Guidelines](https://golang.org/doc/contribute.html). -Before we can accept any pull requests -we have to jump through a couple of legal hurdles, -primarily a Contributor License Agreement (CLA): +## Filing issues -- **If you are an individual writing original source code** - and you're sure you own the intellectual property, - then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). -- **If you work for a company that wants to allow you to contribute your work**, - then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). +When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +**We do not accept GitHub pull requests** +(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. -You can sign these electronically (just scroll to the bottom). -After that, we'll be able to accept your pull requests. diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/README.md b/Godeps/_workspace/src/golang.org/x/oauth2/README.md index ecf9c4e022f..a5afeca2216 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/README.md +++ b/Godeps/_workspace/src/golang.org/x/oauth2/README.md @@ -16,3 +16,49 @@ See godoc for further documentation and examples. * [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google) +## App Engine + +In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor +of the [`context.Context`](https://golang.org/x/net/context#Context) type from +the `golang.org/x/net/context` package + +This means its no longer possible to use the "Classic App Engine" +`appengine.Context` type with the `oauth2` package. (You're using +Classic App Engine if you import the package `"appengine"`.) + +To work around this, you may use the new `"google.golang.org/appengine"` +package. This package has almost the same API as the `"appengine"` package, +but it can be fetched with `go get` and used on "Managed VMs" and well as +Classic App Engine. + +See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app) +for information on updating your app. + +If you don't want to update your entire app to use the new App Engine packages, +you may use both sets of packages in parallel, using only the new packages +with the `oauth2` package. + + import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + newappengine "google.golang.org/appengine" + newurlftech "google.golang.org/urlfetch" + + "appengine" + ) + + func handler(w http.ResponseWriter, r *http.Request) { + var c appengine.Context = appengine.NewContext(r) + c.Infof("Logging a message with the old package") + + var ctx context.Context = newappengine.NewContext(r) + client := &http.Client{ + Transport: &oauth2.Transport{ + Source: google.AppEngineTokenSource(ctx, "scope"), + Base: &newurlfetch.Transport{Context: ctx}, + }, + } + client.Get("...") + } + diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go b/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go index d9ce8045ba4..10aaf917d62 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go @@ -2,38 +2,23 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build appengine,!appenginevm +// +build appengine appenginevm // App Engine hooks. package oauth2 import ( - "log" "net/http" - "sync" - "appengine" - "appengine/urlfetch" + "golang.org/x/net/context" + "google.golang.org/appengine/urlfetch" ) -var warnOnce sync.Once - func init() { registerContextClientFunc(contextClientAppEngine) } -func contextClientAppEngine(ctx Context) (*http.Client, error) { - if actx, ok := ctx.(appengine.Context); ok { - return urlfetch.Client(actx), nil - } - // The user did it wrong. We'll log once (and hope they see it - // in dev_appserver), but stil return (nil, nil) in case some - // other contextClientFunc hook finds a way to proceed. - warnOnce.Do(gaeDoingItWrongHelp) - return nil, nil -} - -func gaeDoingItWrongHelp() { - log.Printf("WARNING: you attempted to use the oauth2 package without passing a valid appengine.Context or *http.Request as the oauth2.Context. App Engine requires that all service RPCs (including urlfetch) be associated with an *http.Request/appengine.Context.") +func contextClientAppEngine(ctx context.Context) (*http.Client, error) { + return urlfetch.Client(ctx), nil } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go index cb4726f606d..8be27885567 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go @@ -7,15 +7,10 @@ package oauth2_test import ( "fmt" "log" - "testing" "golang.org/x/oauth2" ) -// TODO(jbd): Remove after Go 1.4. -// Related to https://codereview.appspot.com/107320046 -func TestA(t *testing.T) {} - func ExampleConfig() { conf := &oauth2.Config{ ClientID: "YOUR_CLIENT_ID", @@ -48,25 +43,3 @@ func ExampleConfig() { client := conf.Client(oauth2.NoContext, tok) client.Get("...") } - -func ExampleJWTConfig() { - var initialToken *oauth2.Token // nil means no initial token - conf := &oauth2.JWTConfig{ - Email: "xxx@developer.com", - // The contents of your RSA private key or your PEM file - // that contains a private key. - // If you have a p12 file instead, you - // can use `openssl` to export the private key into a pem file. - // - // $ openssl pkcs12 -in key.p12 -out key.pem -nodes - // - // It only supports PEM containers with no passphrase. - PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), - Subject: "user@example.com", - TokenURL: "https://provider.com/o/oauth2/token", - } - // Initiate an http.Client, the following GET request will be - // authorized and authenticated on the behalf of user@example.com. - client := conf.Client(oauth2.NoContext, initialToken) - client.Get("...") -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go b/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go new file mode 100644 index 00000000000..962e86b0eb8 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package facebook provides constants for using OAuth2 to access Facebook. +package facebook // import "golang.org/x/oauth2/facebook" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Facebook's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.facebook.com/dialog/oauth", + TokenURL: "https://graph.facebook.com/oauth/access_token", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go b/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go index 82ca623dd12..1648cb58daa 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package github provides constants for using OAuth2 to access Github. -package github +package github // import "golang.org/x/oauth2/github" import ( "golang.org/x/oauth2" diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go index c6213d9cea3..65dc347314d 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go @@ -2,36 +2,82 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build appengine,!appenginevm - package google import ( + "sort" + "strings" + "sync" "time" - "appengine" - + "golang.org/x/net/context" "golang.org/x/oauth2" ) +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) + // AppEngineTokenSource returns a token source that fetches tokens // issued to the current App Engine application's service account. // If you are implementing a 3-legged OAuth 2.0 flow on App Engine // that involves user accounts, see oauth2.Config instead. // -// You are required to provide a valid appengine.Context as context. -func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource { +// The provided context must have come from appengine.NewContext. +func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + scopes := append([]string{}, scope...) + sort.Strings(scopes) return &appEngineTokenSource{ - ctx: ctx, - scopes: scope, - fetcherFunc: aeFetcherFunc, + ctx: ctx, + scopes: scopes, + key: strings.Join(scopes, " "), } } -var aeFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) { - c, ok := ctx.(appengine.Context) - if !ok { - return "", time.Time{}, errInvalidContext - } - return appengine.AccessToken(c, scope...) +// aeTokens helps the fetched tokens to be reused until their expiration. +var ( + aeTokensMu sync.Mutex + aeTokens = make(map[string]*tokenLock) // key is space-separated scopes +) + +type tokenLock struct { + mu sync.Mutex // guards t; held while fetching or updating t + t *oauth2.Token +} + +type appEngineTokenSource struct { + ctx context.Context + scopes []string + key string // to aeTokens map; space-separated scopes +} + +func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + + aeTokensMu.Lock() + tok, ok := aeTokens[ts.key] + if !ok { + tok = &tokenLock{} + aeTokens[ts.key] = tok + } + aeTokensMu.Unlock() + + tok.mu.Lock() + defer tok.mu.Unlock() + if tok.t.Valid() { + return tok.t, nil + } + access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...) + if err != nil { + return nil, err + } + tok.t = &oauth2.Token{ + AccessToken: access, + Expiry: exp, + } + return tok.t, nil } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go new file mode 100644 index 00000000000..2f9b15432fa --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go @@ -0,0 +1,13 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine appenginevm + +package google + +import "google.golang.org/appengine" + +func init() { + appengineTokenFunc = appengine.AccessToken +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go deleted file mode 100644 index 12af742d2f5..00000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2014 The oauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appenginevm !appengine - -package google - -import ( - "time" - - "golang.org/x/oauth2" - "google.golang.org/appengine" -) - -// AppEngineTokenSource returns a token source that fetches tokens -// issued to the current App Engine application's service account. -// If you are implementing a 3-legged OAuth 2.0 flow on App Engine -// that involves user accounts, see oauth2.Config instead. -// -// You are required to provide a valid appengine.Context as context. -func AppEngineTokenSource(ctx appengine.Context, scope ...string) oauth2.TokenSource { - return &appEngineTokenSource{ - ctx: ctx, - scopes: scope, - fetcherFunc: aeVMFetcherFunc, - } -} - -var aeVMFetcherFunc = func(ctx oauth2.Context, scope ...string) (string, time.Time, error) { - c, ok := ctx.(appengine.Context) - if !ok { - return "", time.Time{}, errInvalidContext - } - return appengine.AccessToken(c, scope...) -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go new file mode 100644 index 00000000000..817bfb720f6 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go @@ -0,0 +1,154 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" + "google.golang.org/cloud/compute/metadata" +) + +// DefaultClient returns an HTTP Client that uses the +// DefaultTokenSource to obtain authentication credentials. +// +// This client should be used when developing services +// that run on Google App Engine or Google Compute Engine +// and use "Application Default Credentials." +// +// For more details, see: +// https://developers.google.com/accounts/application-default-credentials +// +func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { + ts, err := DefaultTokenSource(ctx, scope...) + if err != nil { + return nil, err + } + return oauth2.NewClient(ctx, ts), nil +} + +// DefaultTokenSource is a token source that uses +// "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine, it fetches credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +// +// For more details, see: +// https://developers.google.com/accounts/application-default-credentials +// +func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { + // First, try the environment variable. + const envVar = "GOOGLE_APPLICATION_CREDENTIALS" + if filename := os.Getenv(envVar); filename != "" { + ts, err := tokenSourceFromFile(ctx, filename, scope) + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) + } + return ts, nil + } + + // Second, try a well-known file. + filename := wellKnownFile() + _, err := os.Stat(filename) + if err == nil { + ts, err2 := tokenSourceFromFile(ctx, filename, scope) + if err2 == nil { + return ts, nil + } + err = err2 + } else if os.IsNotExist(err) { + err = nil // ignore this error + } + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) + } + + // Third, if we're on Google App Engine use those credentials. + if appengineTokenFunc != nil { + return AppEngineTokenSource(ctx, scope...), nil + } + + // Fourth, if we're on Google Compute Engine use the metadata server. + if metadata.OnGCE() { + return ComputeTokenSource(""), nil + } + + // None are found; return helpful error. + const url = "https://developers.google.com/accounts/application-default-credentials" + return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) +} + +func wellKnownFile() string { + const f = "application_default_credentials.json" + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) + } + return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) +} + +func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + var d struct { + // Common fields + Type string + ClientID string `json:"client_id"` + + // User Credential fields + ClientSecret string `json:"client_secret"` + RefreshToken string `json:"refresh_token"` + + // Service Account fields + ClientEmail string `json:"client_email"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` + } + if err := json.Unmarshal(b, &d); err != nil { + return nil, err + } + switch d.Type { + case "authorized_user": + cfg := &oauth2.Config{ + ClientID: d.ClientID, + ClientSecret: d.ClientSecret, + Scopes: append([]string{}, scopes...), // copy + Endpoint: Endpoint, + } + tok := &oauth2.Token{RefreshToken: d.RefreshToken} + return cfg.TokenSource(ctx, tok), nil + case "service_account": + cfg := &jwt.Config{ + Email: d.ClientEmail, + PrivateKey: []byte(d.PrivateKey), + Scopes: append([]string{}, scopes...), // copy + TokenURL: JWTTokenURL, + } + return cfg.TokenSource(ctx), nil + case "": + return nil, errors.New("missing 'type' field in credentials") + default: + return nil, fmt.Errorf("unknown credential type: %q", d.Type) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go index 6d21d5e96b8..17262802a91 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go @@ -11,17 +11,22 @@ import ( "io/ioutil" "log" "net/http" - "testing" "golang.org/x/oauth2" "golang.org/x/oauth2/google" + "golang.org/x/oauth2/jwt" "google.golang.org/appengine" "google.golang.org/appengine/urlfetch" ) -// Remove after Go 1.4. -// Related to https://codereview.appspot.com/107320046 -func TestA(t *testing.T) {} +func ExampleDefaultClient() { + client, err := google.DefaultClient(oauth2.NoContext, + "https://www.googleapis.com/auth/devstorage.full_control") + if err != nil { + log.Fatal(err) + } + client.Get("...") +} func Example_webServer() { // Your credentials should be obtained from the Google @@ -62,21 +67,34 @@ func ExampleJWTConfigFromJSON() { if err != nil { log.Fatal(err) } - conf, err := google.JWTConfigFromJSON(oauth2.NoContext, data, "https://www.googleapis.com/auth/bigquery") + conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery") if err != nil { log.Fatal(err) } // Initiate an http.Client. The following GET request will be // authorized and authenticated on the behalf of // your service account. - client := conf.Client(oauth2.NoContext, nil) + client := conf.Client(oauth2.NoContext) + client.Get("...") +} + +func ExampleSDKConfig() { + // The credentials will be obtained from the first account that + // has been authorized with `gcloud auth login`. + conf, err := google.NewSDKConfig("") + if err != nil { + log.Fatal(err) + } + // Initiate an http.Client. The following GET request will be + // authorized and authenticated on the behalf of the SDK user. + client := conf.Client(oauth2.NoContext) client.Get("...") } func Example_serviceAccount() { // Your credentials should be obtained from the Google // Developer Console (https://console.developers.google.com). - conf := &oauth2.JWTConfig{ + conf := &jwt.Config{ Email: "xxx@developer.gserviceaccount.com", // The contents of your RSA private key or your PEM file // that contains a private key. @@ -101,7 +119,7 @@ func Example_serviceAccount() { } // Initiate an http.Client, the following GET request will be // authorized and authenticated on the behalf of user@example.com. - client := conf.Client(oauth2.NoContext, nil) + client := conf.Client(oauth2.NoContext) client.Get("...") } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go index a8fc643f3d8..69f8ff42590 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go @@ -2,26 +2,28 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package google provides support for making -// OAuth2 authorized and authenticated HTTP requests -// to Google APIs. It supports Web server, client-side, -// service accounts, Google Compute Engine service accounts, -// and Google App Engine service accounts authorization -// and authentications flows: +// Package google provides support for making OAuth2 authorized and +// authenticated HTTP requests to Google APIs. +// It supports the Web server flow, client-side credentials, service accounts, +// Google Compute Engine service accounts, and Google App Engine service +// accounts. // // For more information, please read -// https://developers.google.com/accounts/docs/OAuth2. -package google +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/application-default-credentials. +package google // import "golang.org/x/oauth2/google" import ( "encoding/json" - + "errors" "fmt" - "net" - "net/http" + "strings" "time" "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" + "google.golang.org/cloud/compute/metadata" ) // Endpoint is Google's OAuth 2.0 endpoint. @@ -33,11 +35,55 @@ var Endpoint = oauth2.Endpoint{ // JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. const JWTTokenURL = "https://accounts.google.com/o/oauth2/token" +// ConfigFromJSON uses a Google Developers Console client_credentials.json +// file to construct a config. +// client_credentials.json can be downloadable from https://console.developers.google.com, +// under "APIs & Auth" > "Credentials". Download the Web application credentials in the +// JSON format and provide the contents of the file as jsonKey. +func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { + type cred struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectURIs []string `json:"redirect_uris"` + AuthURI string `json:"auth_uri"` + TokenURI string `json:"token_uri"` + } + var j struct { + Web *cred `json:"web"` + Installed *cred `json:"installed"` + } + if err := json.Unmarshal(jsonKey, &j); err != nil { + return nil, err + } + var c *cred + switch { + case j.Web != nil: + c = j.Web + case j.Installed != nil: + c = j.Installed + default: + return nil, fmt.Errorf("oauth2/google: no credentials found") + } + if len(c.RedirectURIs) < 1 { + return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json") + } + return &oauth2.Config{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RedirectURL: c.RedirectURIs[0], + Scopes: scope, + Endpoint: oauth2.Endpoint{ + AuthURL: c.AuthURI, + TokenURL: c.TokenURI, + }, + }, nil +} + // JWTConfigFromJSON uses a Google Developers service account JSON key file to read // the credentials that authorize and authenticate the requests. // Create a service account on "Credentials" page under "APIs & Auth" for your // project at https://console.developers.google.com to download a JSON key file. -func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oauth2.JWTConfig, error) { +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { var key struct { Email string `json:"client_email"` PrivateKey string `json:"private_key"` @@ -45,7 +91,7 @@ func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oa if err := json.Unmarshal(jsonKey, &key); err != nil { return nil, err } - return &oauth2.JWTConfig{ + return &jwt.Config{ Email: key.Email, PrivateKey: []byte(key.PrivateKey), Scopes: scope, @@ -53,12 +99,6 @@ func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oa }, nil } -type metaTokenRespBody struct { - AccessToken string `json:"access_token"` - ExpiresIn time.Duration `json:"expires_in"` - TokenType string `json:"token_type"` -} - // ComputeTokenSource returns a token source that fetches access tokens // from Google Compute Engine (GCE)'s metadata server. It's only valid to use // this token source if your program is running on a GCE instance. @@ -66,50 +106,40 @@ type metaTokenRespBody struct { // Further information about retrieving access tokens from the GCE metadata // server can be found at https://cloud.google.com/compute/docs/authentication. func ComputeTokenSource(account string) oauth2.TokenSource { - return &computeSource{account: account} + return oauth2.ReuseTokenSource(nil, computeSource{account: account}) } type computeSource struct { account string } -var metaClient = &http.Client{ - Transport: &http.Transport{ - Dial: (&net.Dialer{ - Timeout: 750 * time.Millisecond, - KeepAlive: 30 * time.Second, - }).Dial, - ResponseHeaderTimeout: 750 * time.Millisecond, - }, -} - -func (cs *computeSource) Token() (*oauth2.Token, error) { +func (cs computeSource) Token() (*oauth2.Token, error) { + if !metadata.OnGCE() { + return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE") + } acct := cs.account if acct == "" { acct = "default" } - u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token" - req, err := http.NewRequest("GET", u, nil) + tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token") if err != nil { return nil, err } - req.Header.Add("X-Google-Metadata-Request", "True") - resp, err := metaClient.Do(req) - if err != nil { - return nil, err + var res struct { + AccessToken string `json:"access_token"` + ExpiresInSec int `json:"expires_in"` + TokenType string `json:"token_type"` } - defer resp.Body.Close() - if resp.StatusCode < 200 || resp.StatusCode > 299 { - return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode) - } - var tokenResp metaTokenRespBody - err = json.NewDecoder(resp.Body).Decode(&tokenResp) + err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) if err != nil { - return nil, err + return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err) + } + if res.ExpiresInSec == 0 || res.AccessToken == "" { + return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata") } return &oauth2.Token{ - AccessToken: tokenResp.AccessToken, - TokenType: tokenResp.TokenType, - Expiry: time.Now().Add(tokenResp.ExpiresIn * time.Second), + AccessToken: res.AccessToken, + TokenType: res.TokenType, + Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), }, nil } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go new file mode 100644 index 00000000000..4cc01884b2c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go @@ -0,0 +1,67 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "strings" + "testing" +) + +var webJSONKey = []byte(` +{ + "web": { + "auth_uri": "https://google.com/o/oauth2/auth", + "client_secret": "3Oknc4jS_wA2r9i", + "token_uri": "https://google.com/o/oauth2/token", + "client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", + "redirect_uris": ["https://www.example.com/oauth2callback"], + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com", + "client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "javascript_origins": ["https://www.example.com"] + } +}`) + +var installedJSONKey = []byte(`{ + "installed": { + "client_id": "222-installed.apps.googleusercontent.com", + "redirect_uris": ["https://www.example.com/oauth2callback"] + } +}`) + +func TestConfigFromJSON(t *testing.T) { + conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2") + if err != nil { + t.Error(err) + } + if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want { + t.Errorf("ClientID = %q; want %q", got, want) + } + if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want { + t.Errorf("ClientSecret = %q; want %q", got, want) + } + if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want { + t.Errorf("RedictURL = %q; want %q", got, want) + } + if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want { + t.Errorf("Scopes = %q; want %q", got, want) + } + if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want { + t.Errorf("AuthURL = %q; want %q", got, want) + } + if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want { + t.Errorf("TokenURL = %q; want %q", got, want) + } +} + +func TestConfigFromJSON_Installed(t *testing.T) { + conf, err := ConfigFromJSON(installedJSONKey) + if err != nil { + t.Error(err) + } + if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want { + t.Errorf("ClientID = %q; want %q", got, want) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 00000000000..01ba0ecb008 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,168 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" +) + +type sdkCredentials struct { + Data []struct { + Credential struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenExpiry *time.Time `json:"token_expiry"` + } `json:"credential"` + Key struct { + Account string `json:"account"` + Scope string `json:"scope"` + } `json:"key"` + } +} + +// An SDKConfig provides access to tokens from an account already +// authorized via the Google Cloud SDK. +type SDKConfig struct { + conf oauth2.Config + initialToken *oauth2.Token +} + +// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK +// account. If account is empty, the account currently active in +// Google Cloud SDK properties is used. +// Google Cloud SDK credentials must be created by running `gcloud auth` +// before using this function. +// The Google Cloud SDK is available at https://cloud.google.com/sdk/. +func NewSDKConfig(account string) (*SDKConfig, error) { + configPath, err := sdkConfigPath() + if err != nil { + return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err) + } + credentialsPath := filepath.Join(configPath, "credentials") + f, err := os.Open(credentialsPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err) + } + defer f.Close() + + var c sdkCredentials + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err) + } + if len(c.Data) == 0 { + return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath) + } + if account == "" { + propertiesPath := filepath.Join(configPath, "properties") + f, err := os.Open(propertiesPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err) + } + defer f.Close() + ini, err := internal.ParseINI(f) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err) + } + core, ok := ini["core"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini) + } + active, ok := core["account"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core) + } + account = active + } + + for _, d := range c.Data { + if account == "" || d.Key.Account == account { + if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" { + return nil, fmt.Errorf("oauth2/google: no token available for account %q", account) + } + var expiry time.Time + if d.Credential.TokenExpiry != nil { + expiry = *d.Credential.TokenExpiry + } + return &SDKConfig{ + conf: oauth2.Config{ + ClientID: d.Credential.ClientID, + ClientSecret: d.Credential.ClientSecret, + Scopes: strings.Split(d.Key.Scope, " "), + Endpoint: Endpoint, + RedirectURL: "oob", + }, + initialToken: &oauth2.Token{ + AccessToken: d.Credential.AccessToken, + RefreshToken: d.Credential.RefreshToken, + Expiry: expiry, + }, + }, nil + } + } + return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account) +} + +// Client returns an HTTP client using Google Cloud SDK credentials to +// authorize requests. The token will auto-refresh as necessary. The +// underlying http.RoundTripper will be obtained using the provided +// context. The returned client and its Transport should not be +// modified. +func (c *SDKConfig) Client(ctx context.Context) *http.Client { + return &http.Client{ + Transport: &oauth2.Transport{ + Source: c.TokenSource(ctx), + }, + } +} + +// TokenSource returns an oauth2.TokenSource that retrieve tokens from +// Google Cloud SDK credentials using the provided context. +// It will returns the current access token stored in the credentials, +// and refresh it when it expires, but it won't update the credentials +// with the new access token. +func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource { + return c.conf.TokenSource(ctx, c.initialToken) +} + +// Scopes are the OAuth 2.0 scopes the current account is authorized for. +func (c *SDKConfig) Scopes() []string { + return c.conf.Scopes +} + +// sdkConfigPath tries to guess where the gcloud config is located. +// It can be overridden during tests. +var sdkConfigPath = func() (string, error) { + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil + } + homeDir := guessUnixHomeDir() + if homeDir == "" { + return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty") + } + return filepath.Join(homeDir, ".config", "gcloud"), nil +} + +func guessUnixHomeDir() string { + usr, err := user.Current() + if err == nil { + return usr.HomeDir + } + return os.Getenv("HOME") +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go new file mode 100644 index 00000000000..79df8896443 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go @@ -0,0 +1,46 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import "testing" + +func TestSDKConfig(t *testing.T) { + sdkConfigPath = func() (string, error) { + return "testdata/gcloud", nil + } + + tests := []struct { + account string + accessToken string + err bool + }{ + {"", "bar_access_token", false}, + {"foo@example.com", "foo_access_token", false}, + {"bar@example.com", "bar_access_token", false}, + {"baz@serviceaccount.example.com", "", true}, + } + for _, tt := range tests { + c, err := NewSDKConfig(tt.account) + if got, want := err != nil, tt.err; got != want { + if !tt.err { + t.Errorf("expected no error, got error: %v", tt.err, err) + } else { + t.Errorf("expected error, got none") + } + continue + } + if err != nil { + continue + } + tok := c.initialToken + if tok == nil { + t.Errorf("expected token %q, got: nil", tt.accessToken) + continue + } + if tok.AccessToken != tt.accessToken { + t.Errorf("expected token %q, got: %q", tt.accessToken, tok.AccessToken) + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go deleted file mode 100644 index 9b8aa97819c..00000000000 --- a/Godeps/_workspace/src/golang.org/x/oauth2/google/source_appengine.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 The oauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package google - -import ( - "errors" - "sort" - "strings" - "sync" - "time" - - "golang.org/x/oauth2" -) - -var ( - aeTokensMu sync.Mutex // guards aeTokens and appEngineTokenSource.key - - // aeTokens helps the fetched tokens to be reused until their expiration. - aeTokens = make(map[string]*tokenLock) // key is '\0'-separated scopes -) - -var errInvalidContext = errors.New("oauth2: a valid appengine.Context is required") - -type tokenLock struct { - mu sync.Mutex // guards t; held while updating t - t *oauth2.Token -} - -type appEngineTokenSource struct { - ctx oauth2.Context - scopes []string - key string // guarded by package-level mutex, aeTokensMu - - // fetcherFunc makes the actual RPC to fetch a new access token with an expiry time. - // Provider of this function is responsible to assert that the given context is valid. - fetcherFunc func(ctx oauth2.Context, scope ...string) (string, time.Time, error) -} - -func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) { - aeTokensMu.Lock() - if ts.key == "" { - sort.Sort(sort.StringSlice(ts.scopes)) - ts.key = strings.Join(ts.scopes, string(0)) - } - tok, ok := aeTokens[ts.key] - if !ok { - tok = &tokenLock{} - aeTokens[ts.key] = tok - } - aeTokensMu.Unlock() - - tok.mu.Lock() - defer tok.mu.Unlock() - if tok.t != nil && !tok.t.Expired() { - return tok.t, nil - } - access, exp, err := ts.fetcherFunc(ts.ctx, ts.scopes...) - if err != nil { - return nil, err - } - tok.t = &oauth2.Token{ - AccessToken: access, - Expiry: exp, - } - return tok.t, nil -} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials new file mode 100644 index 00000000000..ff5eefbd0a8 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials @@ -0,0 +1,122 @@ +{ + "data": [ + { + "credential": { + "_class": "OAuth2Credentials", + "_module": "oauth2client.client", + "access_token": "foo_access_token", + "client_id": "foo_client_id", + "client_secret": "foo_client_secret", + "id_token": { + "at_hash": "foo_at_hash", + "aud": "foo_aud", + "azp": "foo_azp", + "cid": "foo_cid", + "email": "foo@example.com", + "email_verified": true, + "exp": 1420573614, + "iat": 1420569714, + "id": "1337", + "iss": "accounts.google.com", + "sub": "1337", + "token_hash": "foo_token_hash", + "verified_email": true + }, + "invalid": false, + "refresh_token": "foo_refresh_token", + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "token_expiry": "2015-01-09T00:51:51Z", + "token_response": { + "access_token": "foo_access_token", + "expires_in": 3600, + "id_token": "foo_id_token", + "token_type": "Bearer" + }, + "token_uri": "https://accounts.google.com/o/oauth2/token", + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "foo@example.com", + "clientId": "foo_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + }, + { + "credential": { + "_class": "OAuth2Credentials", + "_module": "oauth2client.client", + "access_token": "bar_access_token", + "client_id": "bar_client_id", + "client_secret": "bar_client_secret", + "id_token": { + "at_hash": "bar_at_hash", + "aud": "bar_aud", + "azp": "bar_azp", + "cid": "bar_cid", + "email": "bar@example.com", + "email_verified": true, + "exp": 1420573614, + "iat": 1420569714, + "id": "1337", + "iss": "accounts.google.com", + "sub": "1337", + "token_hash": "bar_token_hash", + "verified_email": true + }, + "invalid": false, + "refresh_token": "bar_refresh_token", + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "token_expiry": "2015-01-09T00:51:51Z", + "token_response": { + "access_token": "bar_access_token", + "expires_in": 3600, + "id_token": "bar_id_token", + "token_type": "Bearer" + }, + "token_uri": "https://accounts.google.com/o/oauth2/token", + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "bar@example.com", + "clientId": "bar_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + }, + { + "credential": { + "_class": "ServiceAccountCredentials", + "_kwargs": {}, + "_module": "oauth2client.client", + "_private_key_id": "00000000000000000000000000000000", + "_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n", + "_revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "_service_account_email": "baz@serviceaccount.example.com", + "_service_account_id": "baz.serviceaccount.example.com", + "_token_uri": "https://accounts.google.com/o/oauth2/token", + "_user_agent": "Cloud SDK Command Line Tool", + "access_token": null, + "assertion_type": null, + "client_id": null, + "client_secret": null, + "id_token": null, + "invalid": false, + "refresh_token": null, + "revoke_uri": "https://accounts.google.com/o/oauth2/revoke", + "service_account_name": "baz@serviceaccount.example.com", + "token_expiry": null, + "token_response": null, + "user_agent": "Cloud SDK Command Line Tool" + }, + "key": { + "account": "baz@serviceaccount.example.com", + "clientId": "baz_client_id", + "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting", + "type": "google-cloud-sdk" + } + } + ], + "file_version": 1 +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties new file mode 100644 index 00000000000..025de886cf7 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties @@ -0,0 +1,2 @@ +[core] +account = bar@example.com \ No newline at end of file diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go index 47c8f14317c..37571a12928 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go @@ -6,10 +6,14 @@ package internal import ( + "bufio" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" + "fmt" + "io" + "strings" ) // ParseKey converts the binary contents of a private key file @@ -26,12 +30,40 @@ func ParseKey(key []byte) (*rsa.PrivateKey, error) { if err != nil { parsedKey, err = x509.ParsePKCS1PrivateKey(key) if err != nil { - return nil, err + return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) } } parsed, ok := parsedKey.(*rsa.PrivateKey) if !ok { - return nil, errors.New("oauth2: private key is invalid") + return nil, errors.New("private key is invalid") } return parsed, nil } + +func ParseINI(ini io.Reader) (map[string]map[string]string, error) { + result := map[string]map[string]string{ + "": map[string]string{}, // root section + } + scanner := bufio.NewScanner(ini) + currentSection := "" + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, ";") { + // comment. + continue + } + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + currentSection = strings.TrimSpace(line[1 : len(line)-1]) + result[currentSection] = map[string]string{} + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 && parts[0] != "" { + result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error scanning ini: %v", err) + } + return result, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go new file mode 100644 index 00000000000..014a351e006 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go @@ -0,0 +1,62 @@ +// Copyright 2014 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( + "reflect" + "strings" + "testing" +) + +func TestParseINI(t *testing.T) { + tests := []struct { + ini string + want map[string]map[string]string + }{ + { + `root = toor +[foo] +bar = hop +ini = nin +`, + map[string]map[string]string{ + "": map[string]string{"root": "toor"}, + "foo": map[string]string{"bar": "hop", "ini": "nin"}, + }, + }, + { + `[empty] +[section] +empty= +`, + map[string]map[string]string{ + "": map[string]string{}, + "empty": map[string]string{}, + "section": map[string]string{"empty": ""}, + }, + }, + { + `ignore +[invalid +=stuff +;comment=true +`, + map[string]map[string]string{ + "": map[string]string{}, + }, + }, + } + for _, tt := range tests { + result, err := ParseINI(strings.NewReader(tt.ini)) + if err != nil { + t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err) + continue + } + if !reflect.DeepEqual(result, tt.want) { + t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want) + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go b/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go index 396b3fac827..362323c4e74 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go @@ -4,7 +4,7 @@ // Package jws provides encoding and decoding utilities for // signed JWS messages. -package jws +package jws // import "golang.org/x/oauth2/jws" import ( "bytes" diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go new file mode 100644 index 00000000000..6d618836ea3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go @@ -0,0 +1,31 @@ +// Copyright 2014 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package jwt_test + +import ( + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" +) + +func ExampleJWTConfig() { + conf := &jwt.Config{ + Email: "xxx@developer.com", + // The contents of your RSA private key or your PEM file + // that contains a private key. + // If you have a p12 file instead, you + // can use `openssl` to export the private key into a pem file. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + // It only supports PEM containers with no passphrase. + PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."), + Subject: "user@example.com", + TokenURL: "https://provider.com/o/oauth2/token", + } + // Initiate an http.Client, the following GET request will be + // authorized and authenticated on the behalf of user@example.com. + client := conf.Client(oauth2.NoContext) + client.Get("...") +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go similarity index 75% rename from Godeps/_workspace/src/golang.org/x/oauth2/jwt.go rename to Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go index d7ec0d33d9d..205d23ed438 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/jwt.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go @@ -2,7 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package oauth2 +// Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly +// known as "two-legged OAuth 2.0". +// +// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12 +package jwt import ( "encoding/json" @@ -14,6 +18,8 @@ import ( "strings" "time" + "golang.org/x/net/context" + "golang.org/x/oauth2" "golang.org/x/oauth2/internal" "golang.org/x/oauth2/jws" ) @@ -23,9 +29,9 @@ var ( defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"} ) -// JWTConfig is the configuration for using JWT to fetch tokens, -// commonly known as "two-legged OAuth". -type JWTConfig struct { +// Config is the configuration for using JWT to fetch tokens, +// commonly known as "two-legged OAuth 2.0". +type Config struct { // Email is the OAuth client identifier used when communicating with // the configured OAuth provider. Email string @@ -52,47 +58,32 @@ type JWTConfig struct { // TokenSource returns a JWT TokenSource using the configuration // in c and the HTTP client from the provided context. -// -// The returned TokenSource only does JWT requests when necessary but -// otherwise returns the same token repeatedly until it expires. -// -// The provided initialToken may be nil, in which case the first -// call to TokenSource will do a new JWT request. -func (c *JWTConfig) TokenSource(ctx Context, initialToken *Token) TokenSource { - return &newWhenNeededSource{ - t: initialToken, - new: jwtSource{ctx, c}, - } +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c}) } // Client returns an HTTP client wrapping the context's // HTTP transport and adding Authorization headers with tokens // obtained from c. // -// The provided initialToken may be nil, in which case the first -// call to TokenSource will do a new JWT request. -// // The returned client and its Transport should not be modified. -func (c *JWTConfig) Client(ctx Context, initialToken *Token) *http.Client { - return NewClient(ctx, c.TokenSource(ctx, initialToken)) +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) } // jwtSource is a source that always does a signed JWT request for a token. -// It should typically be wrapped with a newWhenNeededSource. +// It should typically be wrapped with a reuseTokenSource. type jwtSource struct { - ctx Context - conf *JWTConfig + ctx context.Context + conf *Config } -func (js jwtSource) Token() (*Token, error) { +func (js jwtSource) Token() (*oauth2.Token, error) { pk, err := internal.ParseKey(js.conf.PrivateKey) if err != nil { return nil, err } - hc, err := contextClient(js.ctx) - if err != nil { - return nil, err - } + hc := oauth2.NewClient(js.ctx, nil) claimSet := &jws.ClaimSet{ Iss: js.conf.Email, Scope: strings.Join(js.conf.Scopes, " "), @@ -133,12 +124,13 @@ func (js jwtSource) Token() (*Token, error) { if err := json.Unmarshal(body, &tokenRes); err != nil { return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) } - token := &Token{ + token := &oauth2.Token{ AccessToken: tokenRes.AccessToken, TokenType: tokenRes.TokenType, - raw: make(map[string]interface{}), } - json.Unmarshal(body, &token.raw) // no error checks for optional fields + raw := make(map[string]interface{}) + json.Unmarshal(body, &raw) // no error checks for optional fields + token = token.WithExtra(raw) if secs := tokenRes.ExpiresIn; secs > 0 { token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go similarity index 84% rename from Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go rename to Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go index 8c2e62e12f0..da922c3d00d 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/jwt_test.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package oauth2 +package jwt import ( "net/http" "net/http/httptest" "testing" + + "golang.org/x/oauth2" ) var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY----- @@ -50,17 +52,17 @@ func TestJWTFetch_JSONResponse(t *testing.T) { })) defer ts.Close() - conf := &JWTConfig{ + conf := &Config{ Email: "aaa@xxx.com", PrivateKey: dummyPrivateKey, TokenURL: ts.URL, } - tok, err := conf.TokenSource(NoContext, nil).Token() + tok, err := conf.TokenSource(oauth2.NoContext).Token() if err != nil { t.Fatal(err) } - if tok.Expired() { - t.Errorf("Token shouldn't be expired") + if !tok.Valid() { + t.Errorf("Token invalid") } if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { t.Errorf("Unexpected access token, %#v", tok.AccessToken) @@ -84,24 +86,30 @@ func TestJWTFetch_BadResponse(t *testing.T) { })) defer ts.Close() - conf := &JWTConfig{ + conf := &Config{ Email: "aaa@xxx.com", PrivateKey: dummyPrivateKey, TokenURL: ts.URL, } - tok, err := conf.TokenSource(NoContext, nil).Token() + tok, err := conf.TokenSource(oauth2.NoContext).Token() if err != nil { t.Fatal(err) } - if tok.AccessToken != "" { - t.Errorf("Unexpected access token, %#v.", tok.AccessToken) + if tok == nil { + t.Fatalf("token is nil") } - if tok.TokenType != "bearer" { - t.Errorf("Unexpected token type, %#v.", tok.TokenType) + if tok.Valid() { + t.Errorf("token is valid. want invalid.") + } + if tok.AccessToken != "" { + t.Errorf("Unexpected non-empty access token %q.", tok.AccessToken) + } + if want := "bearer"; tok.TokenType != want { + t.Errorf("TokenType = %q; want %q", tok.TokenType, want) } scope := tok.Extra("scope") - if scope != "user" { - t.Errorf("Unexpected value for scope: %v", scope) + if want := "user"; scope != want { + t.Errorf("token scope = %q; want %q", scope, want) } } @@ -111,12 +119,12 @@ func TestJWTFetch_BadResponseType(t *testing.T) { w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`)) })) defer ts.Close() - conf := &JWTConfig{ + conf := &Config{ Email: "aaa@xxx.com", PrivateKey: dummyPrivateKey, TokenURL: ts.URL, } - tok, err := conf.TokenSource(NoContext, nil).Token() + tok, err := conf.TokenSource(oauth2.NoContext).Token() if err == nil { t.Error("got a token; expected error") if tok.AccessToken != "" { diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go b/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go new file mode 100644 index 00000000000..de91d5b9400 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package linkedin provides constants for using OAuth2 to access LinkedIn. +package linkedin // import "golang.org/x/oauth2/linkedin" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is LinkedIn's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.linkedin.com/uas/oauth2/authorization", + TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go index 5b6b43923ef..0c6a1ed980d 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go @@ -5,7 +5,7 @@ // Package oauth2 provides support for making // OAuth2 authorized and authenticated HTTP requests. // It can additionally grant authorization with Bearer JWT. -package oauth2 +package oauth2 // import "golang.org/x/oauth2" import ( "bytes" @@ -25,18 +25,12 @@ import ( "golang.org/x/net/context" ) -// Context can be an golang.org/x/net.Context, or an App Engine Context. -// In the future these will be unified. -// If you don't care and aren't running on App Engine, you may use NoContext. -type Context interface{} - -// NoContext is the default context. If you're not running this code -// on App Engine or not using golang.org/x/net.Context to provide a custom -// HTTP client, you should use NoContext. -var NoContext Context = nil +// NoContext is the default context you should supply if not using +// your own context.Context (see https://golang.org/x/net/context). +var NoContext = context.TODO() // Config describes a typical 3-legged OAuth2 flow, with both the -// client application information and the server's URLs. +// client application information and the server's endpoint URLs. type Config struct { // ClientID is the application's ID. ClientID string @@ -45,9 +39,9 @@ type Config struct { ClientSecret string // Endpoint contains the resource server's token endpoint - // URLs. These are supplied by the server and are often - // available via site-specific packages (for example, - // google.Endpoint or github.Endpoint) + // URLs. These are constants specific to each server and are + // often available via site-specific packages, such as + // google.Endpoint or github.Endpoint. Endpoint Endpoint // RedirectURL is the URL to redirect users going through @@ -61,6 +55,8 @@ type Config struct { // A TokenSource is anything that can return a token. type TokenSource interface { // Token returns a token or an error. + // Token must be safe for concurrent use by multiple goroutines. + // The returned Token must not be modified. Token() (*Token, error) } @@ -77,28 +73,34 @@ var ( // "access_type" field that gets sent in the URL returned by // AuthCodeURL. // - // Online (the default if neither is specified) is the default. - // If your application needs to refresh access tokens when the - // user is not present at the browser, then use offline. This - // will result in your application obtaining a refresh token - // the first time your application exchanges an authorization + // Online is the default if neither is specified. If your + // application needs to refresh access tokens when the user + // is not present at the browser, then use offline. This will + // result in your application obtaining a refresh token the + // first time your application exchanges an authorization // code for a user. - AccessTypeOnline AuthCodeOption = setParam{"access_type", "online"} - AccessTypeOffline AuthCodeOption = setParam{"access_type", "offline"} + AccessTypeOnline AuthCodeOption = SetParam("access_type", "online") + AccessTypeOffline AuthCodeOption = SetParam("access_type", "offline") // ApprovalForce forces the users to view the consent dialog // and confirm the permissions request at the URL returned // from AuthCodeURL, even if they've already done so. - ApprovalForce AuthCodeOption = setParam{"approval_prompt", "force"} + ApprovalForce AuthCodeOption = SetParam("approval_prompt", "force") ) +// An AuthCodeOption is passed to Config.AuthCodeURL. +type AuthCodeOption interface { + setValue(url.Values) +} + type setParam struct{ k, v string } func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } -// An AuthCodeOption is passed to Config.AuthCodeURL. -type AuthCodeOption interface { - setValue(url.Values) +// SetParam builds an AuthCodeOption which passes key/value parameters +// to a provider's authorization endpoint. +func SetParam(key, value string) AuthCodeOption { + return setParam{key, value} } // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page @@ -133,17 +135,37 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { return buf.String() } +// PasswordCredentialsToken converts a resource owner username and password +// pair into a token. +// +// Per the RFC, this grant type should only be used "when there is a high +// degree of trust between the resource owner and the client (e.g., the client +// is part of the device operating system or a highly privileged application), +// and when other authorization grant types are not available." +// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. +// +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { + return retrieveToken(ctx, c, url.Values{ + "grant_type": {"password"}, + "username": {username}, + "password": {password}, + "scope": condVal(strings.Join(c.Scopes, " ")), + }) +} + // Exchange converts an authorization code into a token. // // It is used after a resource provider redirects the user back // to the Redirect URI (the URL obtained from AuthCodeURL). // -// The HTTP client to use is derived from the context. If nil, -// http.DefaultClient is used. See the Context type's documentation. +// The HTTP client to use is derived from the context. +// If a client is not provided via the context, http.DefaultClient is used. // // The code will be in the *http.Request.FormValue("code"). Before // calling Exchange, be sure to validate FormValue("state"). -func (c *Config) Exchange(ctx Context, code string) (*Token, error) { +func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { return retrieveToken(ctx, c, url.Values{ "grant_type": {"authorization_code"}, "code": {code}, @@ -156,7 +178,7 @@ func (c *Config) Exchange(ctx Context, code string) (*Token, error) { // given a Context value. If it returns an error, the search stops // with that error. If it returns (nil, nil), the search continues // down the list of registered funcs. -type contextClientFunc func(Context) (*http.Client, error) +type contextClientFunc func(context.Context) (*http.Client, error) var contextClientFuncs []contextClientFunc @@ -164,7 +186,7 @@ func registerContextClientFunc(fn contextClientFunc) { contextClientFuncs = append(contextClientFuncs, fn) } -func contextClient(ctx Context) (*http.Client, error) { +func contextClient(ctx context.Context) (*http.Client, error) { for _, fn := range contextClientFuncs { c, err := fn(ctx) if err != nil { @@ -174,15 +196,13 @@ func contextClient(ctx Context) (*http.Client, error) { return c, nil } } - if xc, ok := ctx.(context.Context); ok { - if hc, ok := xc.Value(HTTPClient).(*http.Client); ok { - return hc, nil - } + if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { + return hc, nil } return http.DefaultClient, nil } -func contextTransport(ctx Context) http.RoundTripper { +func contextTransport(ctx context.Context) http.RoundTripper { hc, err := contextClient(ctx) if err != nil { // This is a rare error case (somebody using nil on App Engine), @@ -198,54 +218,64 @@ func contextTransport(ctx Context) http.RoundTripper { // The token will auto-refresh as necessary. The underlying // HTTP transport will be obtained using the provided context. // The returned client and its Transport should not be modified. -func (c *Config) Client(ctx Context, t *Token) *http.Client { +func (c *Config) Client(ctx context.Context, t *Token) *http.Client { return NewClient(ctx, c.TokenSource(ctx, t)) } // TokenSource returns a TokenSource that returns t until t expires, // automatically refreshing it as necessary using the provided context. -// See the the Context documentation. // // Most users will use Config.Client instead. -func (c *Config) TokenSource(ctx Context, t *Token) TokenSource { - nwn := &newWhenNeededSource{t: t} - nwn.new = tokenRefresher{ - ctx: ctx, - conf: c, - oldToken: &nwn.t, +func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { + tkr := &tokenRefresher{ + ctx: ctx, + conf: c, + } + if t != nil { + tkr.refreshToken = t.RefreshToken + } + return &reuseTokenSource{ + t: t, + new: tkr, } - return nwn } // tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token" // HTTP requests to renew a token using a RefreshToken. type tokenRefresher struct { - ctx Context // used to get HTTP requests - conf *Config - oldToken **Token // pointer to old *Token w/ RefreshToken + ctx context.Context // used to get HTTP requests + conf *Config + refreshToken string } -func (tf tokenRefresher) Token() (*Token, error) { - t := *tf.oldToken - if t == nil { - return nil, errors.New("oauth2: attempted use of nil Token") - } - if t.RefreshToken == "" { +// WARNING: Token is not safe for concurrent access, as it +// updates the tokenRefresher's refreshToken field. +// Within this package, it is used by reuseTokenSource which +// synchronizes calls to this method with its own mutex. +func (tf *tokenRefresher) Token() (*Token, error) { + if tf.refreshToken == "" { return nil, errors.New("oauth2: token expired and refresh token is not set") } - return retrieveToken(tf.ctx, tf.conf, url.Values{ + + tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ "grant_type": {"refresh_token"}, - "refresh_token": {t.RefreshToken}, + "refresh_token": {tf.refreshToken}, }) + + if err != nil { + return nil, err + } + if tf.refreshToken != tk.RefreshToken { + tf.refreshToken = tk.RefreshToken + } + return tk, err } -// newWhenNeededSource is a TokenSource that holds a single token in memory +// reuseTokenSource is a TokenSource that holds a single token in memory // and validates its expiry before each call to retrieve it with // Token. If it's expired, it will be auto-refreshed using the // new TokenSource. -// -// The first call to TokenRefresher must be SetToken. -type newWhenNeededSource struct { +type reuseTokenSource struct { new TokenSource // called when t is expired. mu sync.Mutex // guards t @@ -255,10 +285,10 @@ type newWhenNeededSource struct { // Token returns the current token if it's still valid, else will // refresh the current token (using r.Context for HTTP client // information) and return the new one. -func (s *newWhenNeededSource) Token() (*Token, error) { +func (s *reuseTokenSource) Token() (*Token, error) { s.mu.Lock() defer s.mu.Unlock() - if s.t != nil && !s.t.Expired() { + if s.t.Valid() { return s.t, nil } t, err := s.new.Token() @@ -269,7 +299,7 @@ func (s *newWhenNeededSource) Token() (*Token, error) { return t, nil } -func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) { +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { hc, err := contextClient(ctx) if err != nil { return nil, err @@ -284,7 +314,7 @@ func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) { return nil, err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - if !bustedAuth && c.ClientSecret != "" { + if !bustedAuth { req.SetBasicAuth(c.ClientID, c.ClientSecret) } r, err := hc.Do(req) @@ -350,11 +380,11 @@ func retrieveToken(ctx Context, c *Config, v url.Values) (*Token, error) { // tokenJSON is the struct representing the HTTP response from OAuth2 // providers returning a token in JSON form. type tokenJSON struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - RefreshToken string `json:"refresh_token"` - ExpiresIn int32 `json:"expires_in"` - Expires int32 `json:"expires"` // broken Facebook spelling of expires_in + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + RefreshToken string `json:"refresh_token"` + ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number + Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in } func (e *tokenJSON) expiry() (t time.Time) { @@ -367,6 +397,22 @@ func (e *tokenJSON) expiry() (t time.Time) { return } +type expirationTime int32 + +func (e *expirationTime) UnmarshalJSON(b []byte) error { + var n json.Number + err := json.Unmarshal(b, &n) + if err != nil { + return err + } + i, err := n.Int64() + if err != nil { + return err + } + *e = expirationTime(i) + return nil +} + func condVal(v string) []string { if v == "" { return nil @@ -374,6 +420,25 @@ func condVal(v string) []string { return []string{v} } +var brokenAuthHeaderProviders = []string{ + "https://accounts.google.com/", + "https://www.googleapis.com/", + "https://github.com/", + "https://api.instagram.com/", + "https://www.douban.com/", + "https://api.dropbox.com/", + "https://api.soundcloud.com/", + "https://www.linkedin.com/", + "https://api.twitch.tv/", + "https://oauth.vk.com/", + "https://api.odnoklassniki.ru/", + "https://connect.stripe.com/", + "https://api.pushbullet.com/", + "https://oauth.sandbox.trainingpeaks.com/", + "https://oauth.trainingpeaks.com/", + "https://www.strava.com/oauth/", +} + // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL // implements the OAuth2 spec correctly // See https://code.google.com/p/goauth2/issues/detail?id=31 for background. @@ -381,16 +446,13 @@ func condVal(v string) []string { // - Reddit only accepts client secret in the Authorization header // - Dropbox accepts either it in URL param or Auth header, but not both. // - Google only accepts URL param (not spec compliant?), not Auth header +// - Stripe only accepts client secret in Auth header with Bearer method, not Basic func providerAuthHeaderWorks(tokenURL string) bool { - if strings.HasPrefix(tokenURL, "https://accounts.google.com/") || - strings.HasPrefix(tokenURL, "https://github.com/") || - strings.HasPrefix(tokenURL, "https://api.instagram.com/") || - strings.HasPrefix(tokenURL, "https://www.douban.com/") || - strings.HasPrefix(tokenURL, "https://api.dropbox.com/") || - strings.HasPrefix(tokenURL, "https://api.soundcloud.com/") || - strings.HasPrefix(tokenURL, "https://www.linkedin.com/") { - // Some sites fail to implement the OAuth2 spec fully. - return false + for _, s := range brokenAuthHeaderProviders { + if strings.HasPrefix(tokenURL, s) { + // Some sites fail to implement the OAuth2 spec fully. + return false + } } // Assume the provider implements the spec properly @@ -410,12 +472,52 @@ var HTTPClient contextKey type contextKey struct{} // NewClient creates an *http.Client from a Context and TokenSource. -// The client's lifetime does not extend beyond the lifetime of the context. -func NewClient(ctx Context, src TokenSource) *http.Client { +// The returned client is not valid beyond the lifetime of the context. +// +// As a special case, if src is nil, a non-OAuth2 client is returned +// using the provided context. This exists to support related OAuth2 +// packages. +func NewClient(ctx context.Context, src TokenSource) *http.Client { + if src == nil { + c, err := contextClient(ctx) + if err != nil { + return &http.Client{Transport: errorTransport{err}} + } + return c + } return &http.Client{ Transport: &Transport{ Base: contextTransport(ctx), - Source: src, + Source: ReuseTokenSource(nil, src), }, } } + +// ReuseTokenSource returns a TokenSource which repeatedly returns the +// same token as long as it's valid, starting with t. +// When its cached token is invalid, a new token is obtained from src. +// +// ReuseTokenSource is typically used to reuse tokens from a cache +// (such as a file on disk) between runs of a program, rather than +// obtaining new tokens unnecessarily. +// +// The initial token t may be nil, in which case the TokenSource is +// wrapped in a caching version if it isn't one already. This also +// means it's always safe to wrap ReuseTokenSource around any other +// TokenSource without adverse effects. +func ReuseTokenSource(t *Token, src TokenSource) TokenSource { + // Don't wrap a reuseTokenSource in itself. That would work, + // but cause an unnecessary number of mutex operations. + // Just build the equivalent one. + if rt, ok := src.(*reuseTokenSource); ok { + if t == nil { + // Just use it directly. + return rt + } + src = rt.new + } + return &reuseTokenSource{ + t: t, + new: src, + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go index c567c3a3d2d..0bc612934cc 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go @@ -5,11 +5,16 @@ package oauth2 import ( + "encoding/json" "errors" + "fmt" "io/ioutil" "net/http" "net/http/httptest" + "reflect" + "strconv" "testing" + "time" "golang.org/x/net/context" ) @@ -56,6 +61,15 @@ func TestAuthCodeURL(t *testing.T) { } } +func TestAuthCodeURL_CustomParam(t *testing.T) { + conf := newConf("server") + param := SetParam("foo", "bar") + url := conf.AuthCodeURL("baz", param) + if url != "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" { + t.Errorf("Auth code URL doesn't match the expected, found: %v", url) + } +} + func TestAuthCodeURL_Optional(t *testing.T) { conf := &Config{ ClientID: "CLIENT_ID", @@ -99,8 +113,8 @@ func TestExchangeRequest(t *testing.T) { if err != nil { t.Error(err) } - if tok.Expired() { - t.Errorf("Token shouldn't be expired.") + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) } if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { t.Errorf("Unexpected access token, %#v.", tok.AccessToken) @@ -143,8 +157,8 @@ func TestExchangeRequest_JSONResponse(t *testing.T) { if err != nil { t.Error(err) } - if tok.Expired() { - t.Errorf("Token shouldn't be expired.") + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) } if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" { t.Errorf("Unexpected access token, %#v.", tok.AccessToken) @@ -158,6 +172,60 @@ func TestExchangeRequest_JSONResponse(t *testing.T) { } } +const day = 24 * time.Hour + +func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) { + seconds := int32(day.Seconds()) + jsonNumberType := reflect.TypeOf(json.Number("0")) + for _, c := range []struct { + expires string + expect error + }{ + {fmt.Sprintf(`"expires_in": %d`, seconds), nil}, + {fmt.Sprintf(`"expires_in": "%d"`, seconds), nil}, // PayPal case + {fmt.Sprintf(`"expires": %d`, seconds), nil}, // Facebook case + {`"expires": false`, &json.UnmarshalTypeError{Value: "bool", Type: jsonNumberType}}, // wrong type + {`"expires": {}`, &json.UnmarshalTypeError{Value: "object", Type: jsonNumberType}}, // wrong type + {`"expires": "zzz"`, &strconv.NumError{Func: "ParseInt", Num: "zzz", Err: strconv.ErrSyntax}}, // wrong value + } { + testExchangeRequest_JSONResponse_expiry(t, c.expires, c.expect) + } +} + +func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, expect error) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp))) + })) + defer ts.Close() + conf := newConf(ts.URL) + t1 := time.Now().Add(day) + tok, err := conf.Exchange(NoContext, "exchange-code") + t2 := time.Now().Add(day) + // Do a fmt.Sprint comparison so either side can be + // nil. fmt.Sprint just stringifies them to "", and no + // non-nil expected error ever stringifies as "", so this + // isn't terribly disgusting. We do this because Go 1.4 and + // Go 1.5 return a different deep value for + // json.UnmarshalTypeError. In Go 1.5, the + // json.UnmarshalTypeError contains a new field with a new + // non-zero value. Rather than ignore it here with reflect or + // add new files and +build tags, just look at the strings. + if fmt.Sprint(err) != fmt.Sprint(expect) { + t.Errorf("Error = %v; want %v", err, expect) + } + if err != nil { + return + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + expiry := tok.Expiry + if expiry.Before(t1) || expiry.After(t2) { + t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2) + } +} + func TestExchangeRequest_BadResponse(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -210,6 +278,53 @@ func TestExchangeRequest_NonBasicAuth(t *testing.T) { conf.Exchange(ctx, "code") } +func TestPasswordCredentialsTokenRequest(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + expected := "/token" + if r.URL.String() != expected { + t.Errorf("URL = %q; want %q", r.URL, expected) + } + headerAuth := r.Header.Get("Authorization") + expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" + if headerAuth != expected { + t.Errorf("Authorization header = %q; want %q", headerAuth, expected) + } + headerContentType := r.Header.Get("Content-Type") + expected = "application/x-www-form-urlencoded" + if headerContentType != expected { + t.Errorf("Content-Type header = %q; want %q", headerContentType, expected) + } + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Errorf("Failed reading request body: %s.", err) + } + expected = "client_id=CLIENT_ID&grant_type=password&password=password1&scope=scope1+scope2&username=user1" + if string(body) != expected { + t.Errorf("res.Body = %q; want %q", string(body), expected) + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer")) + })) + defer ts.Close() + conf := newConf(ts.URL) + tok, err := conf.PasswordCredentialsToken(NoContext, "user1", "password1") + if err != nil { + t.Error(err) + } + if !tok.Valid() { + t.Fatalf("Token invalid. Got: %#v", tok) + } + expected := "90d64460d14870c08c81352a05dedd3465940a7c" + if tok.AccessToken != expected { + t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected) + } + expected = "bearer" + if tok.TokenType != expected { + t.Errorf("TokenType = %q; want %q", tok.TokenType, expected) + } +} + func TestTokenRefreshRequest(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.String() == "/somethingelse" { @@ -258,3 +373,67 @@ func TestFetchWithNoRefreshToken(t *testing.T) { t.Errorf("Fetch should return an error if no refresh token is set") } } + +func TestRefreshToken_RefreshTokenReplacement(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"access_token":"ACCESS TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW REFRESH TOKEN"}`)) + return + })) + defer ts.Close() + conf := newConf(ts.URL) + tkr := tokenRefresher{ + conf: conf, + ctx: NoContext, + refreshToken: "OLD REFRESH TOKEN", + } + tk, err := tkr.Token() + if err != nil { + t.Errorf("Unexpected refreshToken error returned: %v", err) + return + } + if tk.RefreshToken != tkr.refreshToken { + t.Errorf("tokenRefresher.refresh_token = %s; want %s", tkr.refreshToken, tk.RefreshToken) + } +} + +func TestConfigClientWithToken(t *testing.T) { + tok := &Token{ + AccessToken: "abc123", + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want { + t.Errorf("Authorization header = %q; want %q", got, want) + } + return + })) + defer ts.Close() + conf := newConf(ts.URL) + + c := conf.Client(NoContext, tok) + req, err := http.NewRequest("GET", ts.URL, nil) + if err != nil { + t.Error(err) + } + _, err = c.Do(req) + if err != nil { + t.Error(err) + } +} + +func Test_providerAuthHeaderWorks(t *testing.T) { + for _, p := range brokenAuthHeaderProviders { + if providerAuthHeaderWorks(p) { + t.Errorf("URL: %s not found in list", p) + } + p := fmt.Sprintf("%ssomesuffix", p) + if providerAuthHeaderWorks(p) { + t.Errorf("URL: %s not found in list", p) + } + } + p := "https://api.not-in-the-list-example.com/" + if !providerAuthHeaderWorks(p) { + t.Errorf("URL: %s found in list", p) + } + +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go b/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go new file mode 100644 index 00000000000..2f7a9621e48 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package odnoklassniki provides constants for using OAuth2 to access Odnoklassniki. +package odnoklassniki // import "golang.org/x/oauth2/odnoklassniki" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Odnoklassniki's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.odnoklassniki.ru/oauth/authorize", + TokenURL: "https://api.odnoklassniki.ru/oauth/token.do", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go b/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go new file mode 100644 index 00000000000..baeaa237276 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go @@ -0,0 +1,22 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package paypal provides constants for using OAuth2 to access PayPal. +package paypal // import "golang.org/x/oauth2/paypal" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice", +} + +// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment. +var SandboxEndpoint = oauth2.Endpoint{ + AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/token.go b/Godeps/_workspace/src/golang.org/x/oauth2/token.go index 0c528883e31..9852ceb4a6a 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/token.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/token.go @@ -10,13 +10,18 @@ import ( "time" ) +// expiryDelta determines how earlier a token should be considered +// expired than its actual expiration time. It is used to avoid late +// expirations due to client-server time mismatches. +const expiryDelta = 10 * time.Second + // Token represents the crendentials used to authorize // the requests to access protected resources on the OAuth 2.0 // provider's backend. // // Most users of this package should not access fields of Token // directly. They're exported mostly for use by related packages -// implementing derivate OAuth2 flows. +// implementing derivative OAuth2 flows. type Token struct { // AccessToken is the token that authorizes and authenticates // the requests. @@ -60,28 +65,40 @@ func (t *Token) SetAuthHeader(r *http.Request) { r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) } -// Extra returns an extra field returned from the server during token -// retrieval. -func (t *Token) Extra(key string) string { +// WithExtra returns a new Token that's a clone of t, but using the +// provided raw extra map. This is only intended for use by packages +// implementing derivative OAuth2 flows. +func (t *Token) WithExtra(extra interface{}) *Token { + t2 := new(Token) + *t2 = *t + t2.raw = extra + return t2 +} + +// Extra returns an extra field. +// Extra fields are key-value pairs returned by the server as a +// part of the token retrieval response. +func (t *Token) Extra(key string) interface{} { if vals, ok := t.raw.(url.Values); ok { + // TODO(jbd): Cast numeric values to int64 or float64. return vals.Get(key) } if raw, ok := t.raw.(map[string]interface{}); ok { - if val, ok := raw[key].(string); ok { - return val - } + return raw[key] } - return "" + return nil } -// Expired returns true if there is no access token or the -// access token is expired. -func (t *Token) Expired() bool { - if t.AccessToken == "" { - return true - } +// expired reports whether the token is expired. +// t must be non-nil. +func (t *Token) expired() bool { if t.Expiry.IsZero() { return false } - return t.Expiry.Before(time.Now()) + return t.Expiry.Add(-expiryDelta).Before(time.Now()) +} + +// Valid reports whether t is non-nil, has an AccessToken, and is not expired. +func (t *Token) Valid() bool { + return t != nil && t.AccessToken != "" && !t.expired() } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go new file mode 100644 index 00000000000..739eeb2a205 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go @@ -0,0 +1,50 @@ +// Copyright 2014 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "testing" + "time" +) + +func TestTokenExtra(t *testing.T) { + type testCase struct { + key string + val interface{} + want interface{} + } + const key = "extra-key" + cases := []testCase{ + {key: key, val: "abc", want: "abc"}, + {key: key, val: 123, want: 123}, + {key: key, val: "", want: ""}, + {key: "other-key", val: "def", want: nil}, + } + for _, tc := range cases { + extra := make(map[string]interface{}) + extra[tc.key] = tc.val + tok := &Token{raw: extra} + if got, want := tok.Extra(key), tc.want; got != want { + t.Errorf("Extra(%q) = %q; want %q", key, got, want) + } + } +} + +func TestTokenExpiry(t *testing.T) { + now := time.Now() + cases := []struct { + name string + tok *Token + want bool + }{ + {name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false}, + {name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true}, + } + for _, tc := range cases { + if got, want := tc.tok.expired(), tc.want; got != want { + t.Errorf("expired (%q) = %v; want %v", tc.name, got, want) + } + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go b/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go index b3414e3a3e4..efb8232ac4c 100644 --- a/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go +++ b/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go @@ -32,10 +32,10 @@ func TestTransportTokenSource(t *testing.T) { client.Get(server.URL) } -func TestExpiredWithNoAccessToken(t *testing.T) { +func TestTokenValidNoAccessToken(t *testing.T) { token := &Token{} - if !token.Expired() { - t.Errorf("Token should be expired if no access token is provided") + if token.Valid() { + t.Errorf("Token should not be valid with no access token") } } @@ -43,8 +43,8 @@ func TestExpiredWithExpiry(t *testing.T) { token := &Token{ Expiry: time.Now().Add(-5 * time.Hour), } - if !token.Expired() { - t.Errorf("Token should be expired if no access token is provided") + if token.Valid() { + t.Errorf("Token should not be valid if it expired in the past") } } diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go b/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go new file mode 100644 index 00000000000..5acdeb18ff1 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vk provides constants for using OAuth2 to access VK.com. +package vk // import "golang.org/x/oauth2/vk" + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is VK's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://oauth.vk.com/authorize", + TokenURL: "https://oauth.vk.com/access_token", +} diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/.travis.yml b/Godeps/_workspace/src/gopkg.in/bufio.v1/.travis.yml new file mode 100644 index 00000000000..ccca6bb4a61 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.0 + - 1.1 + - 1.2 + - tip + +install: + - go get launchpad.net/gocheck + - go get gopkg.in/bufio.v1 diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/LICENSE b/Godeps/_workspace/src/gopkg.in/bufio.v1/LICENSE new file mode 100644 index 00000000000..07a316cbf47 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013 The bufio Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/Makefile b/Godeps/_workspace/src/gopkg.in/bufio.v1/Makefile new file mode 100644 index 00000000000..038ed47e941 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/Makefile @@ -0,0 +1,2 @@ +all: + go test gopkg.in/bufio.v1 diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/README.md b/Godeps/_workspace/src/gopkg.in/bufio.v1/README.md new file mode 100644 index 00000000000..bfb85ee544f --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/README.md @@ -0,0 +1,4 @@ +bufio +===== + +This is a fork of the http://golang.org/pkg/bufio/ package. It adds `ReadN` method that allows reading next `n` bytes from the internal buffer without allocating intermediate buffer. This method works just like the [Buffer.Next](http://golang.org/pkg/bytes/#Buffer.Next) method, but has slightly different signature. diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer.go b/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer.go new file mode 100644 index 00000000000..8b915605b64 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer.go @@ -0,0 +1,413 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio + +// Simple byte buffer for marshaling data. + +import ( + "bytes" + "errors" + "io" + "unicode/utf8" +) + +// A Buffer is a variable-sized buffer of bytes with Read and Write methods. +// The zero value for Buffer is an empty buffer ready to use. +type Buffer struct { + buf []byte // contents are the bytes buf[off : len(buf)] + off int // read at &buf[off], write at &buf[len(buf)] + runeBytes [utf8.UTFMax]byte // avoid allocation of slice on each WriteByte or Rune + bootstrap [64]byte // memory to hold first slice; helps small buffers (Printf) avoid allocation. + lastRead readOp // last read operation, so that Unread* can work correctly. +} + +// The readOp constants describe the last action performed on +// the buffer, so that UnreadRune and UnreadByte can +// check for invalid usage. +type readOp int + +const ( + opInvalid readOp = iota // Non-read operation. + opReadRune // Read rune. + opRead // Any other read operation. +) + +// ErrTooLarge is passed to panic if memory cannot be allocated to store data in a buffer. +var ErrTooLarge = errors.New("bytes.Buffer: too large") + +// Bytes returns a slice of the contents of the unread portion of the buffer; +// len(b.Bytes()) == b.Len(). If the caller changes the contents of the +// returned slice, the contents of the buffer will change provided there +// are no intervening method calls on the Buffer. +func (b *Buffer) Bytes() []byte { return b.buf[b.off:] } + +// String returns the contents of the unread portion of the buffer +// as a string. If the Buffer is a nil pointer, it returns "". +func (b *Buffer) String() string { + if b == nil { + // Special case, useful in debugging. + return "" + } + return string(b.buf[b.off:]) +} + +// Len returns the number of bytes of the unread portion of the buffer; +// b.Len() == len(b.Bytes()). +func (b *Buffer) Len() int { return len(b.buf) - b.off } + +// Truncate discards all but the first n unread bytes from the buffer. +// It panics if n is negative or greater than the length of the buffer. +func (b *Buffer) Truncate(n int) { + b.lastRead = opInvalid + switch { + case n < 0 || n > b.Len(): + panic("bytes.Buffer: truncation out of range") + case n == 0: + // Reuse buffer space. + b.off = 0 + } + b.buf = b.buf[0 : b.off+n] +} + +// Reset resets the buffer so it has no content. +// b.Reset() is the same as b.Truncate(0). +func (b *Buffer) Reset() { b.Truncate(0) } + +// grow grows the buffer to guarantee space for n more bytes. +// It returns the index where bytes should be written. +// If the buffer can't grow it will panic with ErrTooLarge. +func (b *Buffer) grow(n int) int { + m := b.Len() + // If buffer is empty, reset to recover space. + if m == 0 && b.off != 0 { + b.Truncate(0) + } + if len(b.buf)+n > cap(b.buf) { + var buf []byte + if b.buf == nil && n <= len(b.bootstrap) { + buf = b.bootstrap[0:] + } else if m+n <= cap(b.buf)/2 { + // We can slide things down instead of allocating a new + // slice. We only need m+n <= cap(b.buf) to slide, but + // we instead let capacity get twice as large so we + // don't spend all our time copying. + copy(b.buf[:], b.buf[b.off:]) + buf = b.buf[:m] + } else { + // not enough space anywhere + buf = makeSlice(2*cap(b.buf) + n) + copy(buf, b.buf[b.off:]) + } + b.buf = buf + b.off = 0 + } + b.buf = b.buf[0 : b.off+m+n] + return b.off + m +} + +// Grow grows the buffer's capacity, if necessary, to guarantee space for +// another n bytes. After Grow(n), at least n bytes can be written to the +// buffer without another allocation. +// If n is negative, Grow will panic. +// If the buffer can't grow it will panic with ErrTooLarge. +func (b *Buffer) Grow(n int) { + if n < 0 { + panic("bytes.Buffer.Grow: negative count") + } + m := b.grow(n) + b.buf = b.buf[0:m] +} + +// Write appends the contents of p to the buffer, growing the buffer as +// needed. The return value n is the length of p; err is always nil. If the +// buffer becomes too large, Write will panic with ErrTooLarge. +func (b *Buffer) Write(p []byte) (n int, err error) { + b.lastRead = opInvalid + m := b.grow(len(p)) + return copy(b.buf[m:], p), nil +} + +// WriteString appends the contents of s to the buffer, growing the buffer as +// needed. The return value n is the length of s; err is always nil. If the +// buffer becomes too large, WriteString will panic with ErrTooLarge. +func (b *Buffer) WriteString(s string) (n int, err error) { + b.lastRead = opInvalid + m := b.grow(len(s)) + return copy(b.buf[m:], s), nil +} + +// MinRead is the minimum slice size passed to a Read call by +// Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond +// what is required to hold the contents of r, ReadFrom will not grow the +// underlying buffer. +const MinRead = 512 + +// ReadFrom reads data from r until EOF and appends it to the buffer, growing +// the buffer as needed. The return value n is the number of bytes read. Any +// error except io.EOF encountered during the read is also returned. If the +// buffer becomes too large, ReadFrom will panic with ErrTooLarge. +func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { + b.lastRead = opInvalid + // If buffer is empty, reset to recover space. + if b.off >= len(b.buf) { + b.Truncate(0) + } + for { + if free := cap(b.buf) - len(b.buf); free < MinRead { + // not enough space at end + newBuf := b.buf + if b.off+free < MinRead { + // not enough space using beginning of buffer; + // double buffer capacity + newBuf = makeSlice(2*cap(b.buf) + MinRead) + } + copy(newBuf, b.buf[b.off:]) + b.buf = newBuf[:len(b.buf)-b.off] + b.off = 0 + } + m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]) + b.buf = b.buf[0 : len(b.buf)+m] + n += int64(m) + if e == io.EOF { + break + } + if e != nil { + return n, e + } + } + return n, nil // err is EOF, so return nil explicitly +} + +// makeSlice allocates a slice of size n. If the allocation fails, it panics +// with ErrTooLarge. +func makeSlice(n int) []byte { + // If the make fails, give a known error. + defer func() { + if recover() != nil { + panic(ErrTooLarge) + } + }() + return make([]byte, n) +} + +// WriteTo writes data to w until the buffer is drained or an error occurs. +// The return value n is the number of bytes written; it always fits into an +// int, but it is int64 to match the io.WriterTo interface. Any error +// encountered during the write is also returned. +func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) { + b.lastRead = opInvalid + if b.off < len(b.buf) { + nBytes := b.Len() + m, e := w.Write(b.buf[b.off:]) + if m > nBytes { + panic("bytes.Buffer.WriteTo: invalid Write count") + } + b.off += m + n = int64(m) + if e != nil { + return n, e + } + // all bytes should have been written, by definition of + // Write method in io.Writer + if m != nBytes { + return n, io.ErrShortWrite + } + } + // Buffer is now empty; reset. + b.Truncate(0) + return +} + +// WriteByte appends the byte c to the buffer, growing the buffer as needed. +// The returned error is always nil, but is included to match bufio.Writer's +// WriteByte. If the buffer becomes too large, WriteByte will panic with +// ErrTooLarge. +func (b *Buffer) WriteByte(c byte) error { + b.lastRead = opInvalid + m := b.grow(1) + b.buf[m] = c + return nil +} + +// WriteRune appends the UTF-8 encoding of Unicode code point r to the +// buffer, returning its length and an error, which is always nil but is +// included to match bufio.Writer's WriteRune. The buffer is grown as needed; +// if it becomes too large, WriteRune will panic with ErrTooLarge. +func (b *Buffer) WriteRune(r rune) (n int, err error) { + if r < utf8.RuneSelf { + b.WriteByte(byte(r)) + return 1, nil + } + n = utf8.EncodeRune(b.runeBytes[0:], r) + b.Write(b.runeBytes[0:n]) + return n, nil +} + +// Read reads the next len(p) bytes from the buffer or until the buffer +// is drained. The return value n is the number of bytes read. If the +// buffer has no data to return, err is io.EOF (unless len(p) is zero); +// otherwise it is nil. +func (b *Buffer) Read(p []byte) (n int, err error) { + b.lastRead = opInvalid + if b.off >= len(b.buf) { + // Buffer is empty, reset to recover space. + b.Truncate(0) + if len(p) == 0 { + return + } + return 0, io.EOF + } + n = copy(p, b.buf[b.off:]) + b.off += n + if n > 0 { + b.lastRead = opRead + } + return +} + +// Next returns a slice containing the next n bytes from the buffer, +// advancing the buffer as if the bytes had been returned by Read. +// If there are fewer than n bytes in the buffer, Next returns the entire buffer. +// The slice is only valid until the next call to a read or write method. +func (b *Buffer) Next(n int) []byte { + b.lastRead = opInvalid + m := b.Len() + if n > m { + n = m + } + data := b.buf[b.off : b.off+n] + b.off += n + if n > 0 { + b.lastRead = opRead + } + return data +} + +// ReadByte reads and returns the next byte from the buffer. +// If no byte is available, it returns error io.EOF. +func (b *Buffer) ReadByte() (c byte, err error) { + b.lastRead = opInvalid + if b.off >= len(b.buf) { + // Buffer is empty, reset to recover space. + b.Truncate(0) + return 0, io.EOF + } + c = b.buf[b.off] + b.off++ + b.lastRead = opRead + return c, nil +} + +// ReadRune reads and returns the next UTF-8-encoded +// Unicode code point from the buffer. +// If no bytes are available, the error returned is io.EOF. +// If the bytes are an erroneous UTF-8 encoding, it +// consumes one byte and returns U+FFFD, 1. +func (b *Buffer) ReadRune() (r rune, size int, err error) { + b.lastRead = opInvalid + if b.off >= len(b.buf) { + // Buffer is empty, reset to recover space. + b.Truncate(0) + return 0, 0, io.EOF + } + b.lastRead = opReadRune + c := b.buf[b.off] + if c < utf8.RuneSelf { + b.off++ + return rune(c), 1, nil + } + r, n := utf8.DecodeRune(b.buf[b.off:]) + b.off += n + return r, n, nil +} + +// UnreadRune unreads the last rune returned by ReadRune. +// If the most recent read or write operation on the buffer was +// not a ReadRune, UnreadRune returns an error. (In this regard +// it is stricter than UnreadByte, which will unread the last byte +// from any read operation.) +func (b *Buffer) UnreadRune() error { + if b.lastRead != opReadRune { + return errors.New("bytes.Buffer: UnreadRune: previous operation was not ReadRune") + } + b.lastRead = opInvalid + if b.off > 0 { + _, n := utf8.DecodeLastRune(b.buf[0:b.off]) + b.off -= n + } + return nil +} + +// UnreadByte unreads the last byte returned by the most recent +// read operation. If write has happened since the last read, UnreadByte +// returns an error. +func (b *Buffer) UnreadByte() error { + if b.lastRead != opReadRune && b.lastRead != opRead { + return errors.New("bytes.Buffer: UnreadByte: previous operation was not a read") + } + b.lastRead = opInvalid + if b.off > 0 { + b.off-- + } + return nil +} + +// ReadBytes reads until the first occurrence of delim in the input, +// returning a slice containing the data up to and including the delimiter. +// If ReadBytes encounters an error before finding a delimiter, +// it returns the data read before the error and the error itself (often io.EOF). +// ReadBytes returns err != nil if and only if the returned data does not end in +// delim. +func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) { + slice, err := b.readSlice(delim) + // return a copy of slice. The buffer's backing array may + // be overwritten by later calls. + line = append(line, slice...) + return +} + +// readSlice is like ReadBytes but returns a reference to internal buffer data. +func (b *Buffer) readSlice(delim byte) (line []byte, err error) { + i := bytes.IndexByte(b.buf[b.off:], delim) + end := b.off + i + 1 + if i < 0 { + end = len(b.buf) + err = io.EOF + } + line = b.buf[b.off:end] + b.off = end + b.lastRead = opRead + return line, err +} + +// ReadString reads until the first occurrence of delim in the input, +// returning a string containing the data up to and including the delimiter. +// If ReadString encounters an error before finding a delimiter, +// it returns the data read before the error and the error itself (often io.EOF). +// ReadString returns err != nil if and only if the returned data does not end +// in delim. +func (b *Buffer) ReadString(delim byte) (line string, err error) { + slice, err := b.readSlice(delim) + return string(slice), err +} + +// NewBuffer creates and initializes a new Buffer using buf as its initial +// contents. It is intended to prepare a Buffer to read existing data. It +// can also be used to size the internal buffer for writing. To do that, +// buf should have the desired capacity but a length of zero. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. +func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } + +// NewBufferString creates and initializes a new Buffer using string s as its +// initial contents. It is intended to prepare a buffer to read an existing +// string. +// +// In most cases, new(Buffer) (or just declaring a Buffer variable) is +// sufficient to initialize a Buffer. +func NewBufferString(s string) *Buffer { + return &Buffer{buf: []byte(s)} +} diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer_test.go b/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer_test.go new file mode 100644 index 00000000000..ca1ac210513 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/buffer_test.go @@ -0,0 +1,527 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio + +import ( + "bytes" + "io" + "math/rand" + "runtime" + "testing" + "unicode/utf8" +) + +const N = 10000 // make this bigger for a larger (and slower) test +var data string // test data for write tests +var testBytes []byte // test data; same as data but as a slice. + +func init() { + testBytes = make([]byte, N) + for i := 0; i < N; i++ { + testBytes[i] = 'a' + byte(i%26) + } + data = string(testBytes) +} + +// Verify that contents of buf match the string s. +func check(t *testing.T, testname string, buf *Buffer, s string) { + bytes := buf.Bytes() + str := buf.String() + if buf.Len() != len(bytes) { + t.Errorf("%s: buf.Len() == %d, len(buf.Bytes()) == %d", testname, buf.Len(), len(bytes)) + } + + if buf.Len() != len(str) { + t.Errorf("%s: buf.Len() == %d, len(buf.String()) == %d", testname, buf.Len(), len(str)) + } + + if buf.Len() != len(s) { + t.Errorf("%s: buf.Len() == %d, len(s) == %d", testname, buf.Len(), len(s)) + } + + if string(bytes) != s { + t.Errorf("%s: string(buf.Bytes()) == %q, s == %q", testname, string(bytes), s) + } +} + +// Fill buf through n writes of string fus. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +func fillString(t *testing.T, testname string, buf *Buffer, s string, n int, fus string) string { + check(t, testname+" (fill 1)", buf, s) + for ; n > 0; n-- { + m, err := buf.WriteString(fus) + if m != len(fus) { + t.Errorf(testname+" (fill 2): m == %d, expected %d", m, len(fus)) + } + if err != nil { + t.Errorf(testname+" (fill 3): err should always be nil, found err == %s", err) + } + s += fus + check(t, testname+" (fill 4)", buf, s) + } + return s +} + +// Fill buf through n writes of byte slice fub. +// The initial contents of buf corresponds to the string s; +// the result is the final contents of buf returned as a string. +func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub []byte) string { + check(t, testname+" (fill 1)", buf, s) + for ; n > 0; n-- { + m, err := buf.Write(fub) + if m != len(fub) { + t.Errorf(testname+" (fill 2): m == %d, expected %d", m, len(fub)) + } + if err != nil { + t.Errorf(testname+" (fill 3): err should always be nil, found err == %s", err) + } + s += string(fub) + check(t, testname+" (fill 4)", buf, s) + } + return s +} + +func TestNewBuffer(t *testing.T) { + buf := NewBuffer(testBytes) + check(t, "NewBuffer", buf, data) +} + +func TestNewBufferString(t *testing.T) { + buf := NewBufferString(data) + check(t, "NewBufferString", buf, data) +} + +// Empty buf through repeated reads into fub. +// The initial contents of buf corresponds to the string s. +func empty(t *testing.T, testname string, buf *Buffer, s string, fub []byte) { + check(t, testname+" (empty 1)", buf, s) + + for { + n, err := buf.Read(fub) + if n == 0 { + break + } + if err != nil { + t.Errorf(testname+" (empty 2): err should always be nil, found err == %s", err) + } + s = s[n:] + check(t, testname+" (empty 3)", buf, s) + } + + check(t, testname+" (empty 4)", buf, "") +} + +func TestBasicOperations(t *testing.T) { + var buf Buffer + + for i := 0; i < 5; i++ { + check(t, "TestBasicOperations (1)", &buf, "") + + buf.Reset() + check(t, "TestBasicOperations (2)", &buf, "") + + buf.Truncate(0) + check(t, "TestBasicOperations (3)", &buf, "") + + n, err := buf.Write([]byte(data[0:1])) + if n != 1 { + t.Errorf("wrote 1 byte, but n == %d", n) + } + if err != nil { + t.Errorf("err should always be nil, but err == %s", err) + } + check(t, "TestBasicOperations (4)", &buf, "a") + + buf.WriteByte(data[1]) + check(t, "TestBasicOperations (5)", &buf, "ab") + + n, err = buf.Write([]byte(data[2:26])) + if n != 24 { + t.Errorf("wrote 25 bytes, but n == %d", n) + } + check(t, "TestBasicOperations (6)", &buf, string(data[0:26])) + + buf.Truncate(26) + check(t, "TestBasicOperations (7)", &buf, string(data[0:26])) + + buf.Truncate(20) + check(t, "TestBasicOperations (8)", &buf, string(data[0:20])) + + empty(t, "TestBasicOperations (9)", &buf, string(data[0:20]), make([]byte, 5)) + empty(t, "TestBasicOperations (10)", &buf, "", make([]byte, 100)) + + buf.WriteByte(data[1]) + c, err := buf.ReadByte() + if err != nil { + t.Error("ReadByte unexpected eof") + } + if c != data[1] { + t.Errorf("ReadByte wrong value c=%v", c) + } + c, err = buf.ReadByte() + if err == nil { + t.Error("ReadByte unexpected not eof") + } + } +} + +func TestLargeStringWrites(t *testing.T) { + var buf Buffer + limit := 30 + if testing.Short() { + limit = 9 + } + for i := 3; i < limit; i += 3 { + s := fillString(t, "TestLargeWrites (1)", &buf, "", 5, data) + empty(t, "TestLargeStringWrites (2)", &buf, s, make([]byte, len(data)/i)) + } + check(t, "TestLargeStringWrites (3)", &buf, "") +} + +func TestLargeByteWrites(t *testing.T) { + var buf Buffer + limit := 30 + if testing.Short() { + limit = 9 + } + for i := 3; i < limit; i += 3 { + s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, testBytes) + empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i)) + } + check(t, "TestLargeByteWrites (3)", &buf, "") +} + +func TestLargeStringReads(t *testing.T) { + var buf Buffer + for i := 3; i < 30; i += 3 { + s := fillString(t, "TestLargeReads (1)", &buf, "", 5, data[0:len(data)/i]) + empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))) + } + check(t, "TestLargeStringReads (3)", &buf, "") +} + +func TestLargeByteReads(t *testing.T) { + var buf Buffer + for i := 3; i < 30; i += 3 { + s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) + empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))) + } + check(t, "TestLargeByteReads (3)", &buf, "") +} + +func TestMixedReadsAndWrites(t *testing.T) { + var buf Buffer + s := "" + for i := 0; i < 50; i++ { + wlen := rand.Intn(len(data)) + if i%2 == 0 { + s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen]) + } else { + s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testBytes[0:wlen]) + } + + rlen := rand.Intn(len(data)) + fub := make([]byte, rlen) + n, _ := buf.Read(fub) + s = s[n:] + } + empty(t, "TestMixedReadsAndWrites (2)", &buf, s, make([]byte, buf.Len())) +} + +func TestNil(t *testing.T) { + var b *Buffer + if b.String() != "" { + t.Errorf("expected ; got %q", b.String()) + } +} + +func TestReadFrom(t *testing.T) { + var buf Buffer + for i := 3; i < 30; i += 3 { + s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) + var b Buffer + b.ReadFrom(&buf) + empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) + } +} + +func TestWriteTo(t *testing.T) { + var buf Buffer + for i := 3; i < 30; i += 3 { + s := fillBytes(t, "TestWriteTo (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) + var b Buffer + buf.WriteTo(&b) + empty(t, "TestWriteTo (2)", &b, s, make([]byte, len(data))) + } +} + +func TestRuneIO(t *testing.T) { + const NRune = 1000 + // Built a test slice while we write the data + b := make([]byte, utf8.UTFMax*NRune) + var buf Buffer + n := 0 + for r := rune(0); r < NRune; r++ { + size := utf8.EncodeRune(b[n:], r) + nbytes, err := buf.WriteRune(r) + if err != nil { + t.Fatalf("WriteRune(%U) error: %s", r, err) + } + if nbytes != size { + t.Fatalf("WriteRune(%U) expected %d, got %d", r, size, nbytes) + } + n += size + } + b = b[0:n] + + // Check the resulting bytes + if !bytes.Equal(buf.Bytes(), b) { + t.Fatalf("incorrect result from WriteRune: %q not %q", buf.Bytes(), b) + } + + p := make([]byte, utf8.UTFMax) + // Read it back with ReadRune + for r := rune(0); r < NRune; r++ { + size := utf8.EncodeRune(p, r) + nr, nbytes, err := buf.ReadRune() + if nr != r || nbytes != size || err != nil { + t.Fatalf("ReadRune(%U) got %U,%d not %U,%d (err=%s)", r, nr, nbytes, r, size, err) + } + } + + // Check that UnreadRune works + buf.Reset() + buf.Write(b) + for r := rune(0); r < NRune; r++ { + r1, size, _ := buf.ReadRune() + if err := buf.UnreadRune(); err != nil { + t.Fatalf("UnreadRune(%U) got error %q", r, err) + } + r2, nbytes, err := buf.ReadRune() + if r1 != r2 || r1 != r || nbytes != size || err != nil { + t.Fatalf("ReadRune(%U) after UnreadRune got %U,%d not %U,%d (err=%s)", r, r2, nbytes, r, size, err) + } + } +} + +func TestNext(t *testing.T) { + b := []byte{0, 1, 2, 3, 4} + tmp := make([]byte, 5) + for i := 0; i <= 5; i++ { + for j := i; j <= 5; j++ { + for k := 0; k <= 6; k++ { + // 0 <= i <= j <= 5; 0 <= k <= 6 + // Check that if we start with a buffer + // of length j at offset i and ask for + // Next(k), we get the right bytes. + buf := NewBuffer(b[0:j]) + n, _ := buf.Read(tmp[0:i]) + if n != i { + t.Fatalf("Read %d returned %d", i, n) + } + bb := buf.Next(k) + want := k + if want > j-i { + want = j - i + } + if len(bb) != want { + t.Fatalf("in %d,%d: len(Next(%d)) == %d", i, j, k, len(bb)) + } + for l, v := range bb { + if v != byte(l+i) { + t.Fatalf("in %d,%d: Next(%d)[%d] = %d, want %d", i, j, k, l, v, l+i) + } + } + } + } + } +} + +var readBytesTests = []struct { + buffer string + delim byte + expected []string + err error +}{ + {"", 0, []string{""}, io.EOF}, + {"a\x00", 0, []string{"a\x00"}, nil}, + {"abbbaaaba", 'b', []string{"ab", "b", "b", "aaab"}, nil}, + {"hello\x01world", 1, []string{"hello\x01"}, nil}, + {"foo\nbar", 0, []string{"foo\nbar"}, io.EOF}, + {"alpha\nbeta\ngamma\n", '\n', []string{"alpha\n", "beta\n", "gamma\n"}, nil}, + {"alpha\nbeta\ngamma", '\n', []string{"alpha\n", "beta\n", "gamma"}, io.EOF}, +} + +func TestReadBytes(t *testing.T) { + for _, test := range readBytesTests { + buf := NewBufferString(test.buffer) + var err error + for _, expected := range test.expected { + var bytes []byte + bytes, err = buf.ReadBytes(test.delim) + if string(bytes) != expected { + t.Errorf("expected %q, got %q", expected, bytes) + } + if err != nil { + break + } + } + if err != test.err { + t.Errorf("expected error %v, got %v", test.err, err) + } + } +} + +func TestReadString(t *testing.T) { + for _, test := range readBytesTests { + buf := NewBufferString(test.buffer) + var err error + for _, expected := range test.expected { + var s string + s, err = buf.ReadString(test.delim) + if s != expected { + t.Errorf("expected %q, got %q", expected, s) + } + if err != nil { + break + } + } + if err != test.err { + t.Errorf("expected error %v, got %v", test.err, err) + } + } +} + +func BenchmarkReadString(b *testing.B) { + const n = 32 << 10 + + data := make([]byte, n) + data[n-1] = 'x' + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + buf := NewBuffer(data) + _, err := buf.ReadString('x') + if err != nil { + b.Fatal(err) + } + } +} + +func TestGrow(t *testing.T) { + x := []byte{'x'} + y := []byte{'y'} + tmp := make([]byte, 72) + for _, startLen := range []int{0, 100, 1000, 10000, 100000} { + xBytes := bytes.Repeat(x, startLen) + for _, growLen := range []int{0, 100, 1000, 10000, 100000} { + buf := NewBuffer(xBytes) + // If we read, this affects buf.off, which is good to test. + readBytes, _ := buf.Read(tmp) + buf.Grow(growLen) + yBytes := bytes.Repeat(y, growLen) + // Check no allocation occurs in write, as long as we're single-threaded. + var m1, m2 runtime.MemStats + runtime.ReadMemStats(&m1) + buf.Write(yBytes) + runtime.ReadMemStats(&m2) + if runtime.GOMAXPROCS(-1) == 1 && m1.Mallocs != m2.Mallocs { + t.Errorf("allocation occurred during write") + } + // Check that buffer has correct data. + if !bytes.Equal(buf.Bytes()[0:startLen-readBytes], xBytes[readBytes:]) { + t.Errorf("bad initial data at %d %d", startLen, growLen) + } + if !bytes.Equal(buf.Bytes()[startLen-readBytes:startLen-readBytes+growLen], yBytes) { + t.Errorf("bad written data at %d %d", startLen, growLen) + } + } + } +} + +// Was a bug: used to give EOF reading empty slice at EOF. +func TestReadEmptyAtEOF(t *testing.T) { + b := new(Buffer) + slice := make([]byte, 0) + n, err := b.Read(slice) + if err != nil { + t.Errorf("read error: %v", err) + } + if n != 0 { + t.Errorf("wrong count; got %d want 0", n) + } +} + +func TestBufferUnreadByte(t *testing.T) { + b := new(Buffer) + b.WriteString("abcdefghijklmnopqrstuvwxyz") + + _, err := b.ReadBytes('m') + if err != nil { + t.Fatalf("ReadBytes: %v", err) + } + + err = b.UnreadByte() + if err != nil { + t.Fatalf("UnreadByte: %v", err) + } + c, err := b.ReadByte() + if err != nil { + t.Fatalf("ReadByte: %v", err) + } + if c != 'm' { + t.Errorf("ReadByte = %q; want %q", c, 'm') + } +} + +// Tests that we occasionally compact. Issue 5154. +func TestBufferGrowth(t *testing.T) { + var b Buffer + buf := make([]byte, 1024) + b.Write(buf[0:1]) + var cap0 int + for i := 0; i < 5<<10; i++ { + b.Write(buf) + b.Read(buf) + if i == 0 { + cap0 = b.Cap() + } + } + cap1 := b.Cap() + // (*Buffer).grow allows for 2x capacity slop before sliding, + // so set our error threshold at 3x. + if cap1 > cap0*3 { + t.Errorf("buffer cap = %d; too big (grew from %d)", cap1, cap0) + } +} + +// From Issue 5154. +func BenchmarkBufferNotEmptyWriteRead(b *testing.B) { + buf := make([]byte, 1024) + for i := 0; i < b.N; i++ { + var b Buffer + b.Write(buf[0:1]) + for i := 0; i < 5<<10; i++ { + b.Write(buf) + b.Read(buf) + } + } +} + +// Check that we don't compact too often. From Issue 5154. +func BenchmarkBufferFullSmallReads(b *testing.B) { + buf := make([]byte, 1024) + for i := 0; i < b.N; i++ { + var b Buffer + b.Write(buf) + for b.Len()+20 < b.Cap() { + b.Write(buf[:10]) + } + for i := 0; i < 5<<10; i++ { + b.Read(buf[:1]) + b.Write(buf[:1]) + } + } +} diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio.go b/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio.go new file mode 100644 index 00000000000..8f5cdc084d4 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio.go @@ -0,0 +1,728 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer +// object, creating another object (Reader or Writer) that also implements +// the interface but provides buffering and some help for textual I/O. +package bufio + +import ( + "bytes" + "errors" + "io" + "unicode/utf8" +) + +const ( + defaultBufSize = 4096 +) + +var ( + ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte") + ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune") + ErrBufferFull = errors.New("bufio: buffer full") + ErrNegativeCount = errors.New("bufio: negative count") +) + +// Buffered input. + +// Reader implements buffering for an io.Reader object. +type Reader struct { + buf []byte + rd io.Reader + r, w int + err error + lastByte int + lastRuneSize int +} + +const minReadBufferSize = 16 +const maxConsecutiveEmptyReads = 100 + +// NewReaderSize returns a new Reader whose buffer has at least the specified +// size. If the argument io.Reader is already a Reader with large enough +// size, it returns the underlying Reader. +func NewReaderSize(rd io.Reader, size int) *Reader { + // Is it already a Reader? + b, ok := rd.(*Reader) + if ok && len(b.buf) >= size { + return b + } + if size < minReadBufferSize { + size = minReadBufferSize + } + r := new(Reader) + r.reset(make([]byte, size), rd) + return r +} + +// NewReader returns a new Reader whose buffer has the default size. +func NewReader(rd io.Reader) *Reader { + return NewReaderSize(rd, defaultBufSize) +} + +// Reset discards any buffered data, resets all state, and switches +// the buffered reader to read from r. +func (b *Reader) Reset(r io.Reader) { + b.reset(b.buf, r) +} + +func (b *Reader) reset(buf []byte, r io.Reader) { + *b = Reader{ + buf: buf, + rd: r, + lastByte: -1, + lastRuneSize: -1, + } +} + +var errNegativeRead = errors.New("bufio: reader returned negative count from Read") + +// fill reads a new chunk into the buffer. +func (b *Reader) fill() { + // Slide existing data to beginning. + if b.r > 0 { + copy(b.buf, b.buf[b.r:b.w]) + b.w -= b.r + b.r = 0 + } + + if b.w >= len(b.buf) { + panic("bufio: tried to fill full buffer") + } + + // Read new data: try a limited number of times. + for i := maxConsecutiveEmptyReads; i > 0; i-- { + n, err := b.rd.Read(b.buf[b.w:]) + if n < 0 { + panic(errNegativeRead) + } + b.w += n + if err != nil { + b.err = err + return + } + if n > 0 { + return + } + } + b.err = io.ErrNoProgress +} + +func (b *Reader) readErr() error { + err := b.err + b.err = nil + return err +} + +// Peek returns the next n bytes without advancing the reader. The bytes stop +// being valid at the next read call. If Peek returns fewer than n bytes, it +// also returns an error explaining why the read is short. The error is +// ErrBufferFull if n is larger than b's buffer size. +func (b *Reader) Peek(n int) ([]byte, error) { + if n < 0 { + return nil, ErrNegativeCount + } + if n > len(b.buf) { + return nil, ErrBufferFull + } + // 0 <= n <= len(b.buf) + for b.w-b.r < n && b.err == nil { + b.fill() // b.w-b.r < len(b.buf) => buffer is not full + } + m := b.w - b.r + if m > n { + m = n + } + var err error + if m < n { + err = b.readErr() + if err == nil { + err = ErrBufferFull + } + } + return b.buf[b.r : b.r+m], err +} + +// Read reads data into p. +// It returns the number of bytes read into p. +// It calls Read at most once on the underlying Reader, +// hence n may be less than len(p). +// At EOF, the count will be zero and err will be io.EOF. +func (b *Reader) Read(p []byte) (n int, err error) { + n = len(p) + if n == 0 { + return 0, b.readErr() + } + if b.r == b.w { + if b.err != nil { + return 0, b.readErr() + } + if len(p) >= len(b.buf) { + // Large read, empty buffer. + // Read directly into p to avoid copy. + n, b.err = b.rd.Read(p) + if n < 0 { + panic(errNegativeRead) + } + if n > 0 { + b.lastByte = int(p[n-1]) + b.lastRuneSize = -1 + } + return n, b.readErr() + } + b.fill() // buffer is empty + if b.w == b.r { + return 0, b.readErr() + } + } + + if n > b.w-b.r { + n = b.w - b.r + } + copy(p[0:n], b.buf[b.r:]) + b.r += n + b.lastByte = int(b.buf[b.r-1]) + b.lastRuneSize = -1 + return n, nil +} + +// ReadByte reads and returns a single byte. +// If no byte is available, returns an error. +func (b *Reader) ReadByte() (c byte, err error) { + b.lastRuneSize = -1 + for b.r == b.w { + if b.err != nil { + return 0, b.readErr() + } + b.fill() // buffer is empty + } + c = b.buf[b.r] + b.r++ + b.lastByte = int(c) + return c, nil +} + +// UnreadByte unreads the last byte. Only the most recently read byte can be unread. +func (b *Reader) UnreadByte() error { + if b.lastByte < 0 || b.r == 0 && b.w > 0 { + return ErrInvalidUnreadByte + } + // b.r > 0 || b.w == 0 + if b.r > 0 { + b.r-- + } else { + // b.r == 0 && b.w == 0 + b.w = 1 + } + b.buf[b.r] = byte(b.lastByte) + b.lastByte = -1 + b.lastRuneSize = -1 + return nil +} + +// ReadRune reads a single UTF-8 encoded Unicode character and returns the +// rune and its size in bytes. If the encoded rune is invalid, it consumes one byte +// and returns unicode.ReplacementChar (U+FFFD) with a size of 1. +func (b *Reader) ReadRune() (r rune, size int, err error) { + for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) { + b.fill() // b.w-b.r < len(buf) => buffer is not full + } + b.lastRuneSize = -1 + if b.r == b.w { + return 0, 0, b.readErr() + } + r, size = rune(b.buf[b.r]), 1 + if r >= 0x80 { + r, size = utf8.DecodeRune(b.buf[b.r:b.w]) + } + b.r += size + b.lastByte = int(b.buf[b.r-1]) + b.lastRuneSize = size + return r, size, nil +} + +// UnreadRune unreads the last rune. If the most recent read operation on +// the buffer was not a ReadRune, UnreadRune returns an error. (In this +// regard it is stricter than UnreadByte, which will unread the last byte +// from any read operation.) +func (b *Reader) UnreadRune() error { + if b.lastRuneSize < 0 || b.r < b.lastRuneSize { + return ErrInvalidUnreadRune + } + b.r -= b.lastRuneSize + b.lastByte = -1 + b.lastRuneSize = -1 + return nil +} + +// Buffered returns the number of bytes that can be read from the current buffer. +func (b *Reader) Buffered() int { return b.w - b.r } + +// ReadSlice reads until the first occurrence of delim in the input, +// returning a slice pointing at the bytes in the buffer. +// The bytes stop being valid at the next read. +// If ReadSlice encounters an error before finding a delimiter, +// it returns all the data in the buffer and the error itself (often io.EOF). +// ReadSlice fails with error ErrBufferFull if the buffer fills without a delim. +// Because the data returned from ReadSlice will be overwritten +// by the next I/O operation, most clients should use +// ReadBytes or ReadString instead. +// ReadSlice returns err != nil if and only if line does not end in delim. +func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { + for { + // Search buffer. + if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 { + line = b.buf[b.r : b.r+i+1] + b.r += i + 1 + break + } + + // Pending error? + if b.err != nil { + line = b.buf[b.r:b.w] + b.r = b.w + err = b.readErr() + break + } + + // Buffer full? + if n := b.Buffered(); n >= len(b.buf) { + b.r = b.w + line = b.buf + err = ErrBufferFull + break + } + + b.fill() // buffer is not full + } + + // Handle last byte, if any. + if i := len(line) - 1; i >= 0 { + b.lastByte = int(line[i]) + } + + return +} + +// ReadN tries to read exactly n bytes. +// The bytes stop being valid at the next read call. +// If ReadN encounters an error before reading n bytes, +// it returns all the data in the buffer and the error itself (often io.EOF). +// ReadN fails with error ErrBufferFull if the buffer fills +// without reading N bytes. +// Because the data returned from ReadN will be overwritten +// by the next I/O operation, most clients should use +// ReadBytes or ReadString instead. +func (b *Reader) ReadN(n int) ([]byte, error) { + for b.Buffered() < n { + if b.err != nil { + buf := b.buf[b.r:b.w] + b.r = b.w + return buf, b.readErr() + } + + // Buffer is full? + if b.Buffered() >= len(b.buf) { + b.r = b.w + return b.buf, ErrBufferFull + } + + b.fill() + } + buf := b.buf[b.r : b.r+n] + b.r += n + return buf, nil +} + +// ReadLine is a low-level line-reading primitive. Most callers should use +// ReadBytes('\n') or ReadString('\n') instead or use a Scanner. +// +// ReadLine tries to return a single line, not including the end-of-line bytes. +// If the line was too long for the buffer then isPrefix is set and the +// beginning of the line is returned. The rest of the line will be returned +// from future calls. isPrefix will be false when returning the last fragment +// of the line. The returned buffer is only valid until the next call to +// ReadLine. ReadLine either returns a non-nil line or it returns an error, +// never both. +// +// The text returned from ReadLine does not include the line end ("\r\n" or "\n"). +// No indication or error is given if the input ends without a final line end. +// Calling UnreadByte after ReadLine will always unread the last byte read +// (possibly a character belonging to the line end) even if that byte is not +// part of the line returned by ReadLine. +func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { + line, err = b.ReadSlice('\n') + if err == ErrBufferFull { + // Handle the case where "\r\n" straddles the buffer. + if len(line) > 0 && line[len(line)-1] == '\r' { + // Put the '\r' back on buf and drop it from line. + // Let the next call to ReadLine check for "\r\n". + if b.r == 0 { + // should be unreachable + panic("bufio: tried to rewind past start of buffer") + } + b.r-- + line = line[:len(line)-1] + } + return line, true, nil + } + + if len(line) == 0 { + if err != nil { + line = nil + } + return + } + err = nil + + if line[len(line)-1] == '\n' { + drop := 1 + if len(line) > 1 && line[len(line)-2] == '\r' { + drop = 2 + } + line = line[:len(line)-drop] + } + return +} + +// ReadBytes reads until the first occurrence of delim in the input, +// returning a slice containing the data up to and including the delimiter. +// If ReadBytes encounters an error before finding a delimiter, +// it returns the data read before the error and the error itself (often io.EOF). +// ReadBytes returns err != nil if and only if the returned data does not end in +// delim. +// For simple uses, a Scanner may be more convenient. +func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { + // Use ReadSlice to look for array, + // accumulating full buffers. + var frag []byte + var full [][]byte + err = nil + + for { + var e error + frag, e = b.ReadSlice(delim) + if e == nil { // got final fragment + break + } + if e != ErrBufferFull { // unexpected error + err = e + break + } + + // Make a copy of the buffer. + buf := make([]byte, len(frag)) + copy(buf, frag) + full = append(full, buf) + } + + // Allocate new buffer to hold the full pieces and the fragment. + n := 0 + for i := range full { + n += len(full[i]) + } + n += len(frag) + + // Copy full pieces and fragment in. + buf := make([]byte, n) + n = 0 + for i := range full { + n += copy(buf[n:], full[i]) + } + copy(buf[n:], frag) + return buf, err +} + +// ReadString reads until the first occurrence of delim in the input, +// returning a string containing the data up to and including the delimiter. +// If ReadString encounters an error before finding a delimiter, +// it returns the data read before the error and the error itself (often io.EOF). +// ReadString returns err != nil if and only if the returned data does not end in +// delim. +// For simple uses, a Scanner may be more convenient. +func (b *Reader) ReadString(delim byte) (line string, err error) { + bytes, err := b.ReadBytes(delim) + line = string(bytes) + return line, err +} + +// WriteTo implements io.WriterTo. +func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { + n, err = b.writeBuf(w) + if err != nil { + return + } + + if r, ok := b.rd.(io.WriterTo); ok { + m, err := r.WriteTo(w) + n += m + return n, err + } + + if w, ok := w.(io.ReaderFrom); ok { + m, err := w.ReadFrom(b.rd) + n += m + return n, err + } + + if b.w-b.r < len(b.buf) { + b.fill() // buffer not full + } + + for b.r < b.w { + // b.r < b.w => buffer is not empty + m, err := b.writeBuf(w) + n += m + if err != nil { + return n, err + } + b.fill() // buffer is empty + } + + if b.err == io.EOF { + b.err = nil + } + + return n, b.readErr() +} + +// writeBuf writes the Reader's buffer to the writer. +func (b *Reader) writeBuf(w io.Writer) (int64, error) { + n, err := w.Write(b.buf[b.r:b.w]) + if n < b.r-b.w { + panic(errors.New("bufio: writer did not write all data")) + } + b.r += n + return int64(n), err +} + +// buffered output + +// Writer implements buffering for an io.Writer object. +// If an error occurs writing to a Writer, no more data will be +// accepted and all subsequent writes will return the error. +// After all data has been written, the client should call the +// Flush method to guarantee all data has been forwarded to +// the underlying io.Writer. +type Writer struct { + err error + buf []byte + n int + wr io.Writer +} + +// NewWriterSize returns a new Writer whose buffer has at least the specified +// size. If the argument io.Writer is already a Writer with large enough +// size, it returns the underlying Writer. +func NewWriterSize(w io.Writer, size int) *Writer { + // Is it already a Writer? + b, ok := w.(*Writer) + if ok && len(b.buf) >= size { + return b + } + if size <= 0 { + size = defaultBufSize + } + return &Writer{ + buf: make([]byte, size), + wr: w, + } +} + +// NewWriter returns a new Writer whose buffer has the default size. +func NewWriter(w io.Writer) *Writer { + return NewWriterSize(w, defaultBufSize) +} + +// Reset discards any unflushed buffered data, clears any error, and +// resets b to write its output to w. +func (b *Writer) Reset(w io.Writer) { + b.err = nil + b.n = 0 + b.wr = w +} + +// Flush writes any buffered data to the underlying io.Writer. +func (b *Writer) Flush() error { + err := b.flush() + return err +} + +func (b *Writer) flush() error { + if b.err != nil { + return b.err + } + if b.n == 0 { + return nil + } + n, err := b.wr.Write(b.buf[0:b.n]) + if n < b.n && err == nil { + err = io.ErrShortWrite + } + if err != nil { + if n > 0 && n < b.n { + copy(b.buf[0:b.n-n], b.buf[n:b.n]) + } + b.n -= n + b.err = err + return err + } + b.n = 0 + return nil +} + +// Available returns how many bytes are unused in the buffer. +func (b *Writer) Available() int { return len(b.buf) - b.n } + +// Buffered returns the number of bytes that have been written into the current buffer. +func (b *Writer) Buffered() int { return b.n } + +// Write writes the contents of p into the buffer. +// It returns the number of bytes written. +// If nn < len(p), it also returns an error explaining +// why the write is short. +func (b *Writer) Write(p []byte) (nn int, err error) { + for len(p) > b.Available() && b.err == nil { + var n int + if b.Buffered() == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, b.err = b.wr.Write(p) + } else { + n = copy(b.buf[b.n:], p) + b.n += n + b.flush() + } + nn += n + p = p[n:] + } + if b.err != nil { + return nn, b.err + } + n := copy(b.buf[b.n:], p) + b.n += n + nn += n + return nn, nil +} + +// WriteByte writes a single byte. +func (b *Writer) WriteByte(c byte) error { + if b.err != nil { + return b.err + } + if b.Available() <= 0 && b.flush() != nil { + return b.err + } + b.buf[b.n] = c + b.n++ + return nil +} + +// WriteRune writes a single Unicode code point, returning +// the number of bytes written and any error. +func (b *Writer) WriteRune(r rune) (size int, err error) { + if r < utf8.RuneSelf { + err = b.WriteByte(byte(r)) + if err != nil { + return 0, err + } + return 1, nil + } + if b.err != nil { + return 0, b.err + } + n := b.Available() + if n < utf8.UTFMax { + if b.flush(); b.err != nil { + return 0, b.err + } + n = b.Available() + if n < utf8.UTFMax { + // Can only happen if buffer is silly small. + return b.WriteString(string(r)) + } + } + size = utf8.EncodeRune(b.buf[b.n:], r) + b.n += size + return size, nil +} + +// WriteString writes a string. +// It returns the number of bytes written. +// If the count is less than len(s), it also returns an error explaining +// why the write is short. +func (b *Writer) WriteString(s string) (int, error) { + nn := 0 + for len(s) > b.Available() && b.err == nil { + n := copy(b.buf[b.n:], s) + b.n += n + nn += n + s = s[n:] + b.flush() + } + if b.err != nil { + return nn, b.err + } + n := copy(b.buf[b.n:], s) + b.n += n + nn += n + return nn, nil +} + +// ReadFrom implements io.ReaderFrom. +func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { + if b.Buffered() == 0 { + if w, ok := b.wr.(io.ReaderFrom); ok { + return w.ReadFrom(r) + } + } + var m int + for { + if b.Available() == 0 { + if err1 := b.flush(); err1 != nil { + return n, err1 + } + } + nr := 0 + for nr < maxConsecutiveEmptyReads { + m, err = r.Read(b.buf[b.n:]) + if m != 0 || err != nil { + break + } + nr++ + } + if nr == maxConsecutiveEmptyReads { + return n, io.ErrNoProgress + } + b.n += m + n += int64(m) + if err != nil { + break + } + } + if err == io.EOF { + // If we filled the buffer exactly, flush pre-emptively. + if b.Available() == 0 { + err = b.flush() + } else { + err = nil + } + } + return n, err +} + +// buffered input and output + +// ReadWriter stores pointers to a Reader and a Writer. +// It implements io.ReadWriter. +type ReadWriter struct { + *Reader + *Writer +} + +// NewReadWriter allocates a new ReadWriter that dispatches to r and w. +func NewReadWriter(r *Reader, w *Writer) *ReadWriter { + return &ReadWriter{r, w} +} diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio_test.go b/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio_test.go new file mode 100644 index 00000000000..f19d9bd282a --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/bufio_test.go @@ -0,0 +1,1418 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio_test + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "strings" + "testing" + "testing/iotest" + "time" + "unicode/utf8" + + . "gopkg.in/bufio.v1" +) + +// Reads from a reader and rot13s the result. +type rot13Reader struct { + r io.Reader +} + +func newRot13Reader(r io.Reader) *rot13Reader { + r13 := new(rot13Reader) + r13.r = r + return r13 +} + +func (r13 *rot13Reader) Read(p []byte) (int, error) { + n, err := r13.r.Read(p) + if err != nil { + return n, err + } + for i := 0; i < n; i++ { + c := p[i] | 0x20 // lowercase byte + if 'a' <= c && c <= 'm' { + p[i] += 13 + } else if 'n' <= c && c <= 'z' { + p[i] -= 13 + } + } + return n, nil +} + +// Call ReadByte to accumulate the text of a file +func readBytes(buf *Reader) string { + var b [1000]byte + nb := 0 + for { + c, err := buf.ReadByte() + if err == io.EOF { + break + } + if err == nil { + b[nb] = c + nb++ + } else if err != iotest.ErrTimeout { + panic("Data: " + err.Error()) + } + } + return string(b[0:nb]) +} + +func TestReaderSimple(t *testing.T) { + data := "hello world" + b := NewReader(strings.NewReader(data)) + if s := readBytes(b); s != "hello world" { + t.Errorf("simple hello world test failed: got %q", s) + } + + b = NewReader(newRot13Reader(strings.NewReader(data))) + if s := readBytes(b); s != "uryyb jbeyq" { + t.Errorf("rot13 hello world test failed: got %q", s) + } +} + +type readMaker struct { + name string + fn func(io.Reader) io.Reader +} + +var readMakers = []readMaker{ + {"full", func(r io.Reader) io.Reader { return r }}, + {"byte", iotest.OneByteReader}, + {"half", iotest.HalfReader}, + {"data+err", iotest.DataErrReader}, + {"timeout", iotest.TimeoutReader}, +} + +// Call ReadString (which ends up calling everything else) +// to accumulate the text of a file. +func readLines(b *Reader) string { + s := "" + for { + s1, err := b.ReadString('\n') + if err == io.EOF { + break + } + if err != nil && err != iotest.ErrTimeout { + panic("GetLines: " + err.Error()) + } + s += s1 + } + return s +} + +// Call Read to accumulate the text of a file +func reads(buf *Reader, m int) string { + var b [1000]byte + nb := 0 + for { + n, err := buf.Read(b[nb : nb+m]) + nb += n + if err == io.EOF { + break + } + } + return string(b[0:nb]) +} + +type bufReader struct { + name string + fn func(*Reader) string +} + +var bufreaders = []bufReader{ + {"1", func(b *Reader) string { return reads(b, 1) }}, + {"2", func(b *Reader) string { return reads(b, 2) }}, + {"3", func(b *Reader) string { return reads(b, 3) }}, + {"4", func(b *Reader) string { return reads(b, 4) }}, + {"5", func(b *Reader) string { return reads(b, 5) }}, + {"7", func(b *Reader) string { return reads(b, 7) }}, + {"bytes", readBytes}, + {"lines", readLines}, +} + +const minReadBufferSize = 16 + +var bufsizes = []int{ + 0, minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096, +} + +func TestReader(t *testing.T) { + var texts [31]string + str := "" + all := "" + for i := 0; i < len(texts)-1; i++ { + texts[i] = str + "\n" + all += texts[i] + str += string(i%26 + 'a') + } + texts[len(texts)-1] = all + + for h := 0; h < len(texts); h++ { + text := texts[h] + for i := 0; i < len(readMakers); i++ { + for j := 0; j < len(bufreaders); j++ { + for k := 0; k < len(bufsizes); k++ { + readmaker := readMakers[i] + bufreader := bufreaders[j] + bufsize := bufsizes[k] + read := readmaker.fn(strings.NewReader(text)) + buf := NewReaderSize(read, bufsize) + s := bufreader.fn(buf) + if s != text { + t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q", + readmaker.name, bufreader.name, bufsize, text, s) + } + } + } + } + } +} + +type zeroReader struct{} + +func (zeroReader) Read(p []byte) (int, error) { + return 0, nil +} + +func TestZeroReader(t *testing.T) { + var z zeroReader + r := NewReader(z) + + c := make(chan error) + go func() { + _, err := r.ReadByte() + c <- err + }() + + select { + case err := <-c: + if err == nil { + t.Error("error expected") + } else if err != io.ErrNoProgress { + t.Error("unexpected error:", err) + } + case <-time.After(time.Second): + t.Error("test timed out (endless loop in ReadByte?)") + } +} + +// A StringReader delivers its data one string segment at a time via Read. +type StringReader struct { + data []string + step int +} + +func (r *StringReader) Read(p []byte) (n int, err error) { + if r.step < len(r.data) { + s := r.data[r.step] + n = copy(p, s) + r.step++ + } else { + err = io.EOF + } + return +} + +func readRuneSegments(t *testing.T, segments []string) { + got := "" + want := strings.Join(segments, "") + r := NewReader(&StringReader{data: segments}) + for { + r, _, err := r.ReadRune() + if err != nil { + if err != io.EOF { + return + } + break + } + got += string(r) + } + if got != want { + t.Errorf("segments=%v got=%s want=%s", segments, got, want) + } +} + +var segmentList = [][]string{ + {}, + {""}, + {"日", "本語"}, + {"\u65e5", "\u672c", "\u8a9e"}, + {"\U000065e5", "\U0000672c", "\U00008a9e"}, + {"\xe6", "\x97\xa5\xe6", "\x9c\xac\xe8\xaa\x9e"}, + {"Hello", ", ", "World", "!"}, + {"Hello", ", ", "", "World", "!"}, +} + +func TestReadRune(t *testing.T) { + for _, s := range segmentList { + readRuneSegments(t, s) + } +} + +func TestUnreadRune(t *testing.T) { + segments := []string{"Hello, world:", "日本語"} + r := NewReader(&StringReader{data: segments}) + got := "" + want := strings.Join(segments, "") + // Normal execution. + for { + r1, _, err := r.ReadRune() + if err != nil { + if err != io.EOF { + t.Error("unexpected error on ReadRune:", err) + } + break + } + got += string(r1) + // Put it back and read it again. + if err = r.UnreadRune(); err != nil { + t.Fatal("unexpected error on UnreadRune:", err) + } + r2, _, err := r.ReadRune() + if err != nil { + t.Fatal("unexpected error reading after unreading:", err) + } + if r1 != r2 { + t.Fatalf("incorrect rune after unread: got %c, want %c", r1, r2) + } + } + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestReaderUnreadByte(t *testing.T) { + segments := []string{"Hello, ", "world"} + r := NewReader(&StringReader{data: segments}) + got := "" + want := strings.Join(segments, "") + // Normal execution. + for { + b1, err := r.ReadByte() + if err != nil { + if err != io.EOF { + t.Error("unexpected error on ReadByte:", err) + } + break + } + got += string(b1) + // Put it back and read it again. + if err = r.UnreadByte(); err != nil { + t.Fatal("unexpected error on UnreadByte:", err) + } + b2, err := r.ReadByte() + if err != nil { + t.Fatal("unexpected error reading after unreading:", err) + } + if b1 != b2 { + t.Fatalf("incorrect byte after unread: got %q, want %q", b1, b2) + } + } + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestUnreadByteMultiple(t *testing.T) { + segments := []string{"Hello, ", "world"} + data := strings.Join(segments, "") + for n := 0; n <= len(data); n++ { + r := NewReader(&StringReader{data: segments}) + // Read n bytes. + for i := 0; i < n; i++ { + b, err := r.ReadByte() + if err != nil { + t.Fatalf("n = %d: unexpected error on ReadByte: %v", n, err) + } + if b != data[i] { + t.Fatalf("n = %d: incorrect byte returned from ReadByte: got %q, want %q", n, b, data[i]) + } + } + // Unread one byte if there is one. + if n > 0 { + if err := r.UnreadByte(); err != nil { + t.Errorf("n = %d: unexpected error on UnreadByte: %v", n, err) + } + } + // Test that we cannot unread any further. + if err := r.UnreadByte(); err == nil { + t.Errorf("n = %d: expected error on UnreadByte", n) + } + } +} + +func TestUnreadByteOthers(t *testing.T) { + // A list of readers to use in conjunction with UnreadByte. + var readers = []func(*Reader, byte) ([]byte, error){ + (*Reader).ReadBytes, + (*Reader).ReadSlice, + func(r *Reader, delim byte) ([]byte, error) { + data, err := r.ReadString(delim) + return []byte(data), err + }, + // ReadLine doesn't fit the data/pattern easily + // so we leave it out. It should be covered via + // the ReadSlice test since ReadLine simply calls + // ReadSlice, and it's that function that handles + // the last byte. + } + + // Try all readers with UnreadByte. + for rno, read := range readers { + // Some input data that is longer than the minimum reader buffer size. + const n = 10 + var buf bytes.Buffer + for i := 0; i < n; i++ { + buf.WriteString("abcdefg") + } + + r := NewReaderSize(&buf, minReadBufferSize) + readTo := func(delim byte, want string) { + data, err := read(r, delim) + if err != nil { + t.Fatalf("#%d: unexpected error reading to %c: %v", rno, delim, err) + } + if got := string(data); got != want { + t.Fatalf("#%d: got %q, want %q", rno, got, want) + } + } + + // Read the data with occasional UnreadByte calls. + for i := 0; i < n; i++ { + readTo('d', "abcd") + for j := 0; j < 3; j++ { + if err := r.UnreadByte(); err != nil { + t.Fatalf("#%d: unexpected error on UnreadByte: %v", rno, err) + } + readTo('d', "d") + } + readTo('g', "efg") + } + + // All data should have been read. + _, err := r.ReadByte() + if err != io.EOF { + t.Errorf("#%d: got error %v; want EOF", rno, err) + } + } +} + +// Test that UnreadRune fails if the preceding operation was not a ReadRune. +func TestUnreadRuneError(t *testing.T) { + buf := make([]byte, 3) // All runes in this test are 3 bytes long + r := NewReader(&StringReader{data: []string{"日本語日本語日本語"}}) + if r.UnreadRune() == nil { + t.Error("expected error on UnreadRune from fresh buffer") + } + _, _, err := r.ReadRune() + if err != nil { + t.Error("unexpected error on ReadRune (1):", err) + } + if err = r.UnreadRune(); err != nil { + t.Error("unexpected error on UnreadRune (1):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after UnreadRune (1)") + } + // Test error after Read. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (2):", err) + } + _, err = r.Read(buf) + if err != nil { + t.Error("unexpected error on Read (2):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after Read (2)") + } + // Test error after ReadByte. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (2):", err) + } + for _ = range buf { + _, err = r.ReadByte() + if err != nil { + t.Error("unexpected error on ReadByte (2):", err) + } + } + if r.UnreadRune() == nil { + t.Error("expected error after ReadByte") + } + // Test error after UnreadByte. + _, _, err = r.ReadRune() // reset state + if err != nil { + t.Error("unexpected error on ReadRune (3):", err) + } + _, err = r.ReadByte() + if err != nil { + t.Error("unexpected error on ReadByte (3):", err) + } + err = r.UnreadByte() + if err != nil { + t.Error("unexpected error on UnreadByte (3):", err) + } + if r.UnreadRune() == nil { + t.Error("expected error after UnreadByte (3)") + } +} + +func TestUnreadRuneAtEOF(t *testing.T) { + // UnreadRune/ReadRune should error at EOF (was a bug; used to panic) + r := NewReader(strings.NewReader("x")) + r.ReadRune() + r.ReadRune() + r.UnreadRune() + _, _, err := r.ReadRune() + if err == nil { + t.Error("expected error at EOF") + } else if err != io.EOF { + t.Error("expected EOF; got", err) + } +} + +func TestReadWriteRune(t *testing.T) { + const NRune = 1000 + byteBuf := new(bytes.Buffer) + w := NewWriter(byteBuf) + // Write the runes out using WriteRune + buf := make([]byte, utf8.UTFMax) + for r := rune(0); r < NRune; r++ { + size := utf8.EncodeRune(buf, r) + nbytes, err := w.WriteRune(r) + if err != nil { + t.Fatalf("WriteRune(0x%x) error: %s", r, err) + } + if nbytes != size { + t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes) + } + } + w.Flush() + + r := NewReader(byteBuf) + // Read them back with ReadRune + for r1 := rune(0); r1 < NRune; r1++ { + size := utf8.EncodeRune(buf, r1) + nr, nbytes, err := r.ReadRune() + if nr != r1 || nbytes != size || err != nil { + t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r1, nr, nbytes, r1, size, err) + } + } +} + +func TestWriter(t *testing.T) { + var data [8192]byte + + for i := 0; i < len(data); i++ { + data[i] = byte(' ' + i%('~'-' ')) + } + w := new(bytes.Buffer) + for i := 0; i < len(bufsizes); i++ { + for j := 0; j < len(bufsizes); j++ { + nwrite := bufsizes[i] + bs := bufsizes[j] + + // Write nwrite bytes using buffer size bs. + // Check that the right amount makes it out + // and that the data is correct. + + w.Reset() + buf := NewWriterSize(w, bs) + context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs) + n, e1 := buf.Write(data[0:nwrite]) + if e1 != nil || n != nwrite { + t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1) + continue + } + if e := buf.Flush(); e != nil { + t.Errorf("%s: buf.Flush = %v", context, e) + } + + written := w.Bytes() + if len(written) != nwrite { + t.Errorf("%s: %d bytes written", context, len(written)) + } + for l := 0; l < len(written); l++ { + if written[i] != data[i] { + t.Errorf("wrong bytes written") + t.Errorf("want=%q", data[0:len(written)]) + t.Errorf("have=%q", written) + } + } + } + } +} + +// Check that write errors are returned properly. + +type errorWriterTest struct { + n, m int + err error + expect error +} + +func (w errorWriterTest) Write(p []byte) (int, error) { + return len(p) * w.n / w.m, w.err +} + +var errorWriterTests = []errorWriterTest{ + {0, 1, nil, io.ErrShortWrite}, + {1, 2, nil, io.ErrShortWrite}, + {1, 1, nil, nil}, + {0, 1, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 2, io.ErrClosedPipe, io.ErrClosedPipe}, + {1, 1, io.ErrClosedPipe, io.ErrClosedPipe}, +} + +func TestWriteErrors(t *testing.T) { + for _, w := range errorWriterTests { + buf := NewWriter(w) + _, e := buf.Write([]byte("hello world")) + if e != nil { + t.Errorf("Write hello to %v: %v", w, e) + continue + } + // Two flushes, to verify the error is sticky. + for i := 0; i < 2; i++ { + e = buf.Flush() + if e != w.expect { + t.Errorf("Flush %d/2 %v: got %v, wanted %v", i+1, w, e, w.expect) + } + } + } +} + +func TestNewReaderSizeIdempotent(t *testing.T) { + const BufSize = 1000 + b := NewReaderSize(strings.NewReader("hello world"), BufSize) + // Does it recognize itself? + b1 := NewReaderSize(b, BufSize) + if b1 != b { + t.Error("NewReaderSize did not detect underlying Reader") + } + // Does it wrap if existing buffer is too small? + b2 := NewReaderSize(b, 2*BufSize) + if b2 == b { + t.Error("NewReaderSize did not enlarge buffer") + } +} + +func TestNewWriterSizeIdempotent(t *testing.T) { + const BufSize = 1000 + b := NewWriterSize(new(bytes.Buffer), BufSize) + // Does it recognize itself? + b1 := NewWriterSize(b, BufSize) + if b1 != b { + t.Error("NewWriterSize did not detect underlying Writer") + } + // Does it wrap if existing buffer is too small? + b2 := NewWriterSize(b, 2*BufSize) + if b2 == b { + t.Error("NewWriterSize did not enlarge buffer") + } +} + +func TestWriteString(t *testing.T) { + const BufSize = 8 + buf := new(bytes.Buffer) + b := NewWriterSize(buf, BufSize) + b.WriteString("0") // easy + b.WriteString("123456") // still easy + b.WriteString("7890") // easy after flush + b.WriteString("abcdefghijklmnopqrstuvwxy") // hard + b.WriteString("z") + if err := b.Flush(); err != nil { + t.Error("WriteString", err) + } + s := "01234567890abcdefghijklmnopqrstuvwxyz" + if string(buf.Bytes()) != s { + t.Errorf("WriteString wants %q gets %q", s, string(buf.Bytes())) + } +} + +func TestBufferFull(t *testing.T) { + const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party" + buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize) + line, err := buf.ReadSlice('!') + if string(line) != "And now, hello, " || err != ErrBufferFull { + t.Errorf("first ReadSlice(,) = %q, %v", line, err) + } + line, err = buf.ReadSlice('!') + if string(line) != "world!" || err != nil { + t.Errorf("second ReadSlice(,) = %q, %v", line, err) + } +} + +func TestPeek(t *testing.T) { + p := make([]byte, 10) + // string is 16 (minReadBufferSize) long. + buf := NewReaderSize(strings.NewReader("abcdefghijklmnop"), minReadBufferSize) + if s, err := buf.Peek(1); string(s) != "a" || err != nil { + t.Fatalf("want %q got %q, err=%v", "a", string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err) + } + if _, err := buf.Peek(-1); err != ErrNegativeCount { + t.Fatalf("want ErrNegativeCount got %v", err) + } + if _, err := buf.Peek(32); err != ErrBufferFull { + t.Fatalf("want ErrBufFull got %v", err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil { + t.Fatalf("want %q got %q, err=%v", "abc", string(p[0:3]), err) + } + if s, err := buf.Peek(1); string(s) != "d" || err != nil { + t.Fatalf("want %q got %q, err=%v", "d", string(s), err) + } + if s, err := buf.Peek(2); string(s) != "de" || err != nil { + t.Fatalf("want %q got %q, err=%v", "de", string(s), err) + } + if _, err := buf.Read(p[0:3]); string(p[0:3]) != "def" || err != nil { + t.Fatalf("want %q got %q, err=%v", "def", string(p[0:3]), err) + } + if s, err := buf.Peek(4); string(s) != "ghij" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err) + } + if _, err := buf.Read(p[0:]); string(p[0:]) != "ghijklmnop" || err != nil { + t.Fatalf("want %q got %q, err=%v", "ghijklmnop", string(p[0:minReadBufferSize]), err) + } + if s, err := buf.Peek(0); string(s) != "" || err != nil { + t.Fatalf("want %q got %q, err=%v", "", string(s), err) + } + if _, err := buf.Peek(1); err != io.EOF { + t.Fatalf("want EOF got %v", err) + } + + // Test for issue 3022, not exposing a reader's error on a successful Peek. + buf = NewReaderSize(dataAndEOFReader("abcd"), 32) + if s, err := buf.Peek(2); string(s) != "ab" || err != nil { + t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err) + } + if s, err := buf.Peek(4); string(s) != "abcd" || err != nil { + t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err) + } + if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil { + t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err) + } + if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF { + t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err) + } +} + +type dataAndEOFReader string + +func (r dataAndEOFReader) Read(p []byte) (int, error) { + return copy(p, r), io.EOF +} + +func TestPeekThenUnreadRune(t *testing.T) { + // This sequence used to cause a crash. + r := NewReader(strings.NewReader("x")) + r.ReadRune() + r.Peek(1) + r.UnreadRune() + r.ReadRune() // Used to panic here +} + +var testOutput = []byte("0123456789abcdefghijklmnopqrstuvwxy") +var testInput = []byte("012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy") +var testInputrn = []byte("012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n") + +// TestReader wraps a []byte and returns reads of a specific length. +type testReader struct { + data []byte + stride int +} + +func (t *testReader) Read(buf []byte) (n int, err error) { + n = t.stride + if n > len(t.data) { + n = len(t.data) + } + if n > len(buf) { + n = len(buf) + } + copy(buf, t.data) + t.data = t.data[n:] + if len(t.data) == 0 { + err = io.EOF + } + return +} + +func testReadLine(t *testing.T, input []byte) { + //for stride := 1; stride < len(input); stride++ { + for stride := 1; stride < 2; stride++ { + done := 0 + reader := testReader{input, stride} + l := NewReaderSize(&reader, len(input)+1) + for { + line, isPrefix, err := l.ReadLine() + if len(line) > 0 && err != nil { + t.Errorf("ReadLine returned both data and error: %s", err) + } + if isPrefix { + t.Errorf("ReadLine returned prefix") + } + if err != nil { + if err != io.EOF { + t.Fatalf("Got unknown error: %s", err) + } + break + } + if want := testOutput[done : done+len(line)]; !bytes.Equal(want, line) { + t.Errorf("Bad line at stride %d: want: %x got: %x", stride, want, line) + } + done += len(line) + } + if done != len(testOutput) { + t.Errorf("ReadLine didn't return everything: got: %d, want: %d (stride: %d)", done, len(testOutput), stride) + } + } +} + +func TestReadLine(t *testing.T) { + testReadLine(t, testInput) + testReadLine(t, testInputrn) +} + +func TestLineTooLong(t *testing.T) { + data := make([]byte, 0) + for i := 0; i < minReadBufferSize*5/2; i++ { + data = append(data, '0'+byte(i%10)) + } + buf := bytes.NewReader(data) + l := NewReaderSize(buf, minReadBufferSize) + line, isPrefix, err := l.ReadLine() + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for first line: got %q want %q %v", line, data[:minReadBufferSize], err) + } + data = data[len(line):] + line, isPrefix, err = l.ReadLine() + if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil { + t.Errorf("bad result for second line: got %q want %q %v", line, data[:minReadBufferSize], err) + } + data = data[len(line):] + line, isPrefix, err = l.ReadLine() + if isPrefix || !bytes.Equal(line, data[:minReadBufferSize/2]) || err != nil { + t.Errorf("bad result for third line: got %q want %q %v", line, data[:minReadBufferSize/2], err) + } + line, isPrefix, err = l.ReadLine() + if isPrefix || err == nil { + t.Errorf("expected no more lines: %x %s", line, err) + } +} + +func TestReadAfterLines(t *testing.T) { + line1 := "this is line1" + restData := "this is line2\nthis is line 3\n" + inbuf := bytes.NewReader([]byte(line1 + "\n" + restData)) + outbuf := new(bytes.Buffer) + maxLineLength := len(line1) + len(restData)/2 + l := NewReaderSize(inbuf, maxLineLength) + line, isPrefix, err := l.ReadLine() + if isPrefix || err != nil || string(line) != line1 { + t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line)) + } + n, err := io.Copy(outbuf, l) + if int(n) != len(restData) || err != nil { + t.Errorf("bad result for Read: n=%d err=%v", n, err) + } + if outbuf.String() != restData { + t.Errorf("bad result for Read: got %q; expected %q", outbuf.String(), restData) + } +} + +func TestReadEmptyBuffer(t *testing.T) { + l := NewReaderSize(new(bytes.Buffer), minReadBufferSize) + line, isPrefix, err := l.ReadLine() + if err != io.EOF { + t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) + } +} + +func TestLinesAfterRead(t *testing.T) { + l := NewReaderSize(bytes.NewReader([]byte("foo")), minReadBufferSize) + _, err := ioutil.ReadAll(l) + if err != nil { + t.Error(err) + return + } + + line, isPrefix, err := l.ReadLine() + if err != io.EOF { + t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) + } +} + +func TestReadLineNonNilLineOrError(t *testing.T) { + r := NewReader(strings.NewReader("line 1\n")) + for i := 0; i < 2; i++ { + l, _, err := r.ReadLine() + if l != nil && err != nil { + t.Fatalf("on line %d/2; ReadLine=%#v, %v; want non-nil line or Error, but not both", + i+1, l, err) + } + } +} + +type readLineResult struct { + line []byte + isPrefix bool + err error +} + +var readLineNewlinesTests = []struct { + input string + expect []readLineResult +}{ + {"012345678901234\r\n012345678901234\r\n", []readLineResult{ + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {[]byte("012345678901234"), true, nil}, + {nil, false, nil}, + {nil, false, io.EOF}, + }}, + {"0123456789012345\r012345678901234\r", []readLineResult{ + {[]byte("0123456789012345"), true, nil}, + {[]byte("\r012345678901234"), true, nil}, + {[]byte("\r"), false, nil}, + {nil, false, io.EOF}, + }}, +} + +func TestReadLineNewlines(t *testing.T) { + for _, e := range readLineNewlinesTests { + testReadLineNewlines(t, e.input, e.expect) + } +} + +func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) { + b := NewReaderSize(strings.NewReader(input), minReadBufferSize) + for i, e := range expect { + line, isPrefix, err := b.ReadLine() + if !bytes.Equal(line, e.line) { + t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line) + return + } + if isPrefix != e.isPrefix { + t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix) + return + } + if err != e.err { + t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err) + return + } + } +} + +func createTestInput(n int) []byte { + input := make([]byte, n) + for i := range input { + // 101 and 251 are arbitrary prime numbers. + // The idea is to create an input sequence + // which doesn't repeat too frequently. + input[i] = byte(i % 251) + if i%101 == 0 { + input[i] ^= byte(i / 101) + } + } + return input +} + +func TestReaderWriteTo(t *testing.T) { + input := createTestInput(8192) + r := NewReader(onlyReader{bytes.NewReader(input)}) + w := new(bytes.Buffer) + if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) { + t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input)) + } + + for i, val := range w.Bytes() { + if val != input[i] { + t.Errorf("after write: out[%d] = %#x, want %#x", i, val, input[i]) + } + } +} + +type errorWriterToTest struct { + rn, wn int + rerr, werr error + expected error +} + +func (r errorWriterToTest) Read(p []byte) (int, error) { + return len(p) * r.rn, r.rerr +} + +func (w errorWriterToTest) Write(p []byte) (int, error) { + return len(p) * w.wn, w.werr +} + +var errorWriterToTests = []errorWriterToTest{ + {1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe}, + {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe}, + {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe}, + {0, 1, io.EOF, nil, nil}, +} + +func TestReaderWriteToErrors(t *testing.T) { + for i, rw := range errorWriterToTests { + r := NewReader(rw) + if _, err := r.WriteTo(rw); err != rw.expected { + t.Errorf("r.WriteTo(errorWriterToTests[%d]) = _, %v, want _,%v", i, err, rw.expected) + } + } +} + +func TestWriterReadFrom(t *testing.T) { + ws := []func(io.Writer) io.Writer{ + func(w io.Writer) io.Writer { return onlyWriter{w} }, + func(w io.Writer) io.Writer { return w }, + } + + rs := []func(io.Reader) io.Reader{ + iotest.DataErrReader, + func(r io.Reader) io.Reader { return r }, + } + + for ri, rfunc := range rs { + for wi, wfunc := range ws { + input := createTestInput(8192) + b := new(bytes.Buffer) + w := NewWriter(wfunc(b)) + r := rfunc(bytes.NewReader(input)) + if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) { + t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input)) + continue + } + if err := w.Flush(); err != nil { + t.Errorf("Flush returned %v", err) + continue + } + if got, want := b.String(), string(input); got != want { + t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want) + } + } + } +} + +type errorReaderFromTest struct { + rn, wn int + rerr, werr error + expected error +} + +func (r errorReaderFromTest) Read(p []byte) (int, error) { + return len(p) * r.rn, r.rerr +} + +func (w errorReaderFromTest) Write(p []byte) (int, error) { + return len(p) * w.wn, w.werr +} + +var errorReaderFromTests = []errorReaderFromTest{ + {0, 1, io.EOF, nil, nil}, + {1, 1, io.EOF, nil, nil}, + {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe}, + {0, 0, io.ErrClosedPipe, io.ErrShortWrite, io.ErrClosedPipe}, + {1, 0, nil, io.ErrShortWrite, io.ErrShortWrite}, +} + +func TestWriterReadFromErrors(t *testing.T) { + for i, rw := range errorReaderFromTests { + w := NewWriter(rw) + if _, err := w.ReadFrom(rw); err != rw.expected { + t.Errorf("w.ReadFrom(errorReaderFromTests[%d]) = _, %v, want _,%v", i, err, rw.expected) + } + } +} + +// TestWriterReadFromCounts tests that using io.Copy to copy into a +// bufio.Writer does not prematurely flush the buffer. For example, when +// buffering writes to a network socket, excessive network writes should be +// avoided. +func TestWriterReadFromCounts(t *testing.T) { + var w0 writeCountingDiscard + b0 := NewWriterSize(&w0, 1234) + b0.WriteString(strings.Repeat("x", 1000)) + if w0 != 0 { + t.Fatalf("write 1000 'x's: got %d writes, want 0", w0) + } + b0.WriteString(strings.Repeat("x", 200)) + if w0 != 0 { + t.Fatalf("write 1200 'x's: got %d writes, want 0", w0) + } + io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))}) + if w0 != 0 { + t.Fatalf("write 1230 'x's: got %d writes, want 0", w0) + } + io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))}) + if w0 != 1 { + t.Fatalf("write 1239 'x's: got %d writes, want 1", w0) + } + + var w1 writeCountingDiscard + b1 := NewWriterSize(&w1, 1234) + b1.WriteString(strings.Repeat("x", 1200)) + b1.Flush() + if w1 != 1 { + t.Fatalf("flush 1200 'x's: got %d writes, want 1", w1) + } + b1.WriteString(strings.Repeat("x", 89)) + if w1 != 1 { + t.Fatalf("write 1200 + 89 'x's: got %d writes, want 1", w1) + } + io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))}) + if w1 != 1 { + t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1) + } + io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))}) + if w1 != 2 { + t.Fatalf("write 1200 + 1389 'x's: got %d writes, want 2", w1) + } + b1.Flush() + if w1 != 3 { + t.Fatalf("flush 1200 + 1389 'x's: got %d writes, want 3", w1) + } +} + +// A writeCountingDiscard is like ioutil.Discard and counts the number of times +// Write is called on it. +type writeCountingDiscard int + +func (w *writeCountingDiscard) Write(p []byte) (int, error) { + *w++ + return len(p), nil +} + +type negativeReader int + +func (r *negativeReader) Read([]byte) (int, error) { return -1, nil } + +func TestNegativeRead(t *testing.T) { + // should panic with a description pointing at the reader, not at itself. + // (should NOT panic with slice index error, for example.) + b := NewReader(new(negativeReader)) + defer func() { + switch err := recover().(type) { + case nil: + t.Fatal("read did not panic") + case error: + if !strings.Contains(err.Error(), "reader returned negative count from Read") { + t.Fatalf("wrong panic: %v", err) + } + default: + t.Fatalf("unexpected panic value: %T(%v)", err, err) + } + }() + b.Read(make([]byte, 100)) +} + +var errFake = errors.New("fake error") + +type errorThenGoodReader struct { + didErr bool + nread int +} + +func (r *errorThenGoodReader) Read(p []byte) (int, error) { + r.nread++ + if !r.didErr { + r.didErr = true + return 0, errFake + } + return len(p), nil +} + +func TestReaderClearError(t *testing.T) { + r := &errorThenGoodReader{} + b := NewReader(r) + buf := make([]byte, 1) + if _, err := b.Read(nil); err != nil { + t.Fatalf("1st nil Read = %v; want nil", err) + } + if _, err := b.Read(buf); err != errFake { + t.Fatalf("1st Read = %v; want errFake", err) + } + if _, err := b.Read(nil); err != nil { + t.Fatalf("2nd nil Read = %v; want nil", err) + } + if _, err := b.Read(buf); err != nil { + t.Fatalf("3rd Read with buffer = %v; want nil", err) + } + if r.nread != 2 { + t.Errorf("num reads = %d; want 2", r.nread) + } +} + +// Test for golang.org/issue/5947 +func TestWriterReadFromWhileFull(t *testing.T) { + buf := new(bytes.Buffer) + w := NewWriterSize(buf, 10) + + // Fill buffer exactly. + n, err := w.Write([]byte("0123456789")) + if n != 10 || err != nil { + t.Fatalf("Write returned (%v, %v), want (10, nil)", n, err) + } + + // Use ReadFrom to read in some data. + n2, err := w.ReadFrom(strings.NewReader("abcdef")) + if n2 != 6 || err != nil { + t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n2, err) + } +} + +type emptyThenNonEmptyReader struct { + r io.Reader + n int +} + +func (r *emptyThenNonEmptyReader) Read(p []byte) (int, error) { + if r.n <= 0 { + return r.r.Read(p) + } + r.n-- + return 0, nil +} + +// Test for golang.org/issue/7611 +func TestWriterReadFromUntilEOF(t *testing.T) { + buf := new(bytes.Buffer) + w := NewWriterSize(buf, 5) + + // Partially fill buffer + n, err := w.Write([]byte("0123")) + if n != 4 || err != nil { + t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err) + } + + // Use ReadFrom to read in some data. + r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 3} + n2, err := w.ReadFrom(r) + if n2 != 4 || err != nil { + t.Fatalf("ReadFrom returned (%v, %v), want (4, nil)", n2, err) + } + w.Flush() + if got, want := string(buf.Bytes()), "0123abcd"; got != want { + t.Fatalf("buf.Bytes() returned %q, want %q", got, want) + } +} + +func TestWriterReadFromErrNoProgress(t *testing.T) { + buf := new(bytes.Buffer) + w := NewWriterSize(buf, 5) + + // Partially fill buffer + n, err := w.Write([]byte("0123")) + if n != 4 || err != nil { + t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err) + } + + // Use ReadFrom to read in some data. + r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 100} + n2, err := w.ReadFrom(r) + if n2 != 0 || err != io.ErrNoProgress { + t.Fatalf("buf.Bytes() returned (%v, %v), want (0, io.ErrNoProgress)", n2, err) + } +} + +func TestReaderReset(t *testing.T) { + r := NewReader(strings.NewReader("foo foo")) + buf := make([]byte, 3) + r.Read(buf) + if string(buf) != "foo" { + t.Errorf("buf = %q; want foo", buf) + } + r.Reset(strings.NewReader("bar bar")) + all, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if string(all) != "bar bar" { + t.Errorf("ReadAll = %q; want bar bar", all) + } +} + +func TestWriterReset(t *testing.T) { + var buf1, buf2 bytes.Buffer + w := NewWriter(&buf1) + w.WriteString("foo") + w.Reset(&buf2) // and not flushed + w.WriteString("bar") + w.Flush() + if buf1.String() != "" { + t.Errorf("buf1 = %q; want empty", buf1.String()) + } + if buf2.String() != "bar" { + t.Errorf("buf2 = %q; want bar", buf2.String()) + } +} + +// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. +type onlyReader struct { + io.Reader +} + +// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have. +type onlyWriter struct { + io.Writer +} + +func BenchmarkReaderCopyOptimal(b *testing.B) { + // Optimal case is where the underlying reader implements io.WriterTo + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + src := NewReader(srcBuf) + dstBuf := new(bytes.Buffer) + dst := onlyWriter{dstBuf} + for i := 0; i < b.N; i++ { + srcBuf.Reset() + src.Reset(srcBuf) + dstBuf.Reset() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyUnoptimal(b *testing.B) { + // Unoptimal case is where the underlying reader doesn't implement io.WriterTo + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + src := NewReader(onlyReader{srcBuf}) + dstBuf := new(bytes.Buffer) + dst := onlyWriter{dstBuf} + for i := 0; i < b.N; i++ { + srcBuf.Reset() + src.Reset(onlyReader{srcBuf}) + dstBuf.Reset() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyNoWriteTo(b *testing.B) { + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + srcReader := NewReader(srcBuf) + src := onlyReader{srcReader} + dstBuf := new(bytes.Buffer) + dst := onlyWriter{dstBuf} + for i := 0; i < b.N; i++ { + srcBuf.Reset() + srcReader.Reset(srcBuf) + dstBuf.Reset() + io.Copy(dst, src) + } +} + +func BenchmarkReaderWriteToOptimal(b *testing.B) { + const bufSize = 16 << 10 + buf := make([]byte, bufSize) + r := bytes.NewReader(buf) + srcReader := NewReaderSize(onlyReader{r}, 1<<10) + if _, ok := ioutil.Discard.(io.ReaderFrom); !ok { + b.Fatal("ioutil.Discard doesn't support ReaderFrom") + } + for i := 0; i < b.N; i++ { + r.Seek(0, 0) + srcReader.Reset(onlyReader{r}) + n, err := srcReader.WriteTo(ioutil.Discard) + if err != nil { + b.Fatal(err) + } + if n != bufSize { + b.Fatalf("n = %d; want %d", n, bufSize) + } + } +} + +func BenchmarkWriterCopyOptimal(b *testing.B) { + // Optimal case is where the underlying writer implements io.ReaderFrom + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + src := onlyReader{srcBuf} + dstBuf := new(bytes.Buffer) + dst := NewWriter(dstBuf) + for i := 0; i < b.N; i++ { + srcBuf.Reset() + dstBuf.Reset() + dst.Reset(dstBuf) + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyUnoptimal(b *testing.B) { + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + src := onlyReader{srcBuf} + dstBuf := new(bytes.Buffer) + dst := NewWriter(onlyWriter{dstBuf}) + for i := 0; i < b.N; i++ { + srcBuf.Reset() + dstBuf.Reset() + dst.Reset(onlyWriter{dstBuf}) + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyNoReadFrom(b *testing.B) { + srcBuf := bytes.NewBuffer(make([]byte, 8192)) + src := onlyReader{srcBuf} + dstBuf := new(bytes.Buffer) + dstWriter := NewWriter(dstBuf) + dst := onlyWriter{dstWriter} + for i := 0; i < b.N; i++ { + srcBuf.Reset() + dstBuf.Reset() + dstWriter.Reset(dstBuf) + io.Copy(dst, src) + } +} + +func BenchmarkReaderEmpty(b *testing.B) { + b.ReportAllocs() + str := strings.Repeat("x", 16<<10) + for i := 0; i < b.N; i++ { + br := NewReader(strings.NewReader(str)) + n, err := io.Copy(ioutil.Discard, br) + if err != nil { + b.Fatal(err) + } + if n != int64(len(str)) { + b.Fatal("wrong length") + } + } +} + +func BenchmarkWriterEmpty(b *testing.B) { + b.ReportAllocs() + str := strings.Repeat("x", 1<<10) + bs := []byte(str) + for i := 0; i < b.N; i++ { + bw := NewWriter(ioutil.Discard) + bw.Flush() + bw.WriteByte('a') + bw.Flush() + bw.WriteRune('B') + bw.Flush() + bw.Write(bs) + bw.Flush() + bw.WriteString(str) + bw.Flush() + } +} + +func BenchmarkWriterFlush(b *testing.B) { + b.ReportAllocs() + bw := NewWriter(ioutil.Discard) + str := strings.Repeat("x", 50) + for i := 0; i < b.N; i++ { + bw.WriteString(str) + bw.Flush() + } +} diff --git a/Godeps/_workspace/src/gopkg.in/bufio.v1/export_test.go b/Godeps/_workspace/src/gopkg.in/bufio.v1/export_test.go new file mode 100644 index 00000000000..16629d0224c --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/bufio.v1/export_test.go @@ -0,0 +1,9 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package bufio + +func (b *Buffer) Cap() int { + return cap(b.buf) +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/.travis.yml b/Godeps/_workspace/src/gopkg.in/redis.v2/.travis.yml new file mode 100644 index 00000000000..c3cf4b8a6e3 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/.travis.yml @@ -0,0 +1,19 @@ +language: go + +services: +- redis-server + +go: + - 1.1 + - 1.2 + - 1.3 + - tip + +install: + - go get gopkg.in/bufio.v1 + - go get gopkg.in/check.v1 + - mkdir -p $HOME/gopath/src/gopkg.in + - ln -s `pwd` $HOME/gopath/src/gopkg.in/redis.v2 + +before_script: + - redis-server testdata/sentinel.conf --sentinel & diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/LICENSE b/Godeps/_workspace/src/gopkg.in/redis.v2/LICENSE new file mode 100644 index 00000000000..6855a95feb9 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012 The Redis Go Client Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/Makefile b/Godeps/_workspace/src/gopkg.in/redis.v2/Makefile new file mode 100644 index 00000000000..b250d9bfa96 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/Makefile @@ -0,0 +1,3 @@ +all: + go test gopkg.in/redis.v2 -cpu=1,2,4 + go test gopkg.in/redis.v2 -short -race diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/README.md b/Godeps/_workspace/src/gopkg.in/redis.v2/README.md new file mode 100644 index 00000000000..ddf875f9a19 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/README.md @@ -0,0 +1,46 @@ +Redis client for Golang [![Build Status](https://travis-ci.org/go-redis/redis.png?branch=master)](https://travis-ci.org/go-redis/redis) +======================= + +Supports: + +- Redis 2.8 commands except QUIT, MONITOR, SLOWLOG and SYNC. +- Pub/sub. +- Transactions. +- Pipelining. +- Connection pool. +- TLS connections. +- Thread safety. +- Timeouts. +- Redis Sentinel. + +API docs: http://godoc.org/gopkg.in/redis.v2. +Examples: http://godoc.org/gopkg.in/redis.v2#pkg-examples. + +Installation +------------ + +Install: + + go get gopkg.in/redis.v2 + +Look and feel +------------- + +Some corner cases: + + SORT list LIMIT 0 2 ASC + vals, err := client.Sort("list", redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() + + ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 + vals, err := client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{ + Min: "-inf", + Max: "+inf", + Offset: 0, + Count: 2, + }).Result() + + ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM + vals, err := client.ZInterStore("out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2").Result() + + EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" + vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, []string{"hello"}).Result() diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/command.go b/Godeps/_workspace/src/gopkg.in/redis.v2/command.go new file mode 100644 index 00000000000..d7c76cf92a9 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/command.go @@ -0,0 +1,597 @@ +package redis + +import ( + "fmt" + "strconv" + "strings" + "time" + + "gopkg.in/bufio.v1" +) + +var ( + _ Cmder = (*Cmd)(nil) + _ Cmder = (*SliceCmd)(nil) + _ Cmder = (*StatusCmd)(nil) + _ Cmder = (*IntCmd)(nil) + _ Cmder = (*DurationCmd)(nil) + _ Cmder = (*BoolCmd)(nil) + _ Cmder = (*StringCmd)(nil) + _ Cmder = (*FloatCmd)(nil) + _ Cmder = (*StringSliceCmd)(nil) + _ Cmder = (*BoolSliceCmd)(nil) + _ Cmder = (*StringStringMapCmd)(nil) + _ Cmder = (*ZSliceCmd)(nil) + _ Cmder = (*ScanCmd)(nil) +) + +type Cmder interface { + args() []string + parseReply(*bufio.Reader) error + setErr(error) + + writeTimeout() *time.Duration + readTimeout() *time.Duration + + Err() error + String() string +} + +func setCmdsErr(cmds []Cmder, e error) { + for _, cmd := range cmds { + cmd.setErr(e) + } +} + +func cmdString(cmd Cmder, val interface{}) string { + s := strings.Join(cmd.args(), " ") + if err := cmd.Err(); err != nil { + return s + ": " + err.Error() + } + if val != nil { + return s + ": " + fmt.Sprint(val) + } + return s + +} + +//------------------------------------------------------------------------------ + +type baseCmd struct { + _args []string + + err error + + _writeTimeout, _readTimeout *time.Duration +} + +func newBaseCmd(args ...string) *baseCmd { + return &baseCmd{ + _args: args, + } +} + +func (cmd *baseCmd) Err() error { + if cmd.err != nil { + return cmd.err + } + return nil +} + +func (cmd *baseCmd) args() []string { + return cmd._args +} + +func (cmd *baseCmd) readTimeout() *time.Duration { + return cmd._readTimeout +} + +func (cmd *baseCmd) setReadTimeout(d time.Duration) { + cmd._readTimeout = &d +} + +func (cmd *baseCmd) writeTimeout() *time.Duration { + return cmd._writeTimeout +} + +func (cmd *baseCmd) setWriteTimeout(d time.Duration) { + cmd._writeTimeout = &d +} + +func (cmd *baseCmd) setErr(e error) { + cmd.err = e +} + +//------------------------------------------------------------------------------ + +type Cmd struct { + *baseCmd + + val interface{} +} + +func NewCmd(args ...string) *Cmd { + return &Cmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *Cmd) Val() interface{} { + return cmd.val +} + +func (cmd *Cmd) Result() (interface{}, error) { + return cmd.val, cmd.err +} + +func (cmd *Cmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *Cmd) parseReply(rd *bufio.Reader) error { + cmd.val, cmd.err = parseReply(rd, parseSlice) + return cmd.err +} + +//------------------------------------------------------------------------------ + +type SliceCmd struct { + *baseCmd + + val []interface{} +} + +func NewSliceCmd(args ...string) *SliceCmd { + return &SliceCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *SliceCmd) Val() []interface{} { + return cmd.val +} + +func (cmd *SliceCmd) Result() ([]interface{}, error) { + return cmd.val, cmd.err +} + +func (cmd *SliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *SliceCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, parseSlice) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.([]interface{}) + return nil +} + +//------------------------------------------------------------------------------ + +type StatusCmd struct { + *baseCmd + + val string +} + +func NewStatusCmd(args ...string) *StatusCmd { + return &StatusCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *StatusCmd) Val() string { + return cmd.val +} + +func (cmd *StatusCmd) Result() (string, error) { + return cmd.val, cmd.err +} + +func (cmd *StatusCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StatusCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.(string) + return nil +} + +//------------------------------------------------------------------------------ + +type IntCmd struct { + *baseCmd + + val int64 +} + +func NewIntCmd(args ...string) *IntCmd { + return &IntCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *IntCmd) Val() int64 { + return cmd.val +} + +func (cmd *IntCmd) Result() (int64, error) { + return cmd.val, cmd.err +} + +func (cmd *IntCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *IntCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.(int64) + return nil +} + +//------------------------------------------------------------------------------ + +type DurationCmd struct { + *baseCmd + + val time.Duration + precision time.Duration +} + +func NewDurationCmd(precision time.Duration, args ...string) *DurationCmd { + return &DurationCmd{ + baseCmd: newBaseCmd(args...), + precision: precision, + } +} + +func (cmd *DurationCmd) Val() time.Duration { + return cmd.val +} + +func (cmd *DurationCmd) Result() (time.Duration, error) { + return cmd.val, cmd.err +} + +func (cmd *DurationCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *DurationCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val = time.Duration(v.(int64)) * cmd.precision + return nil +} + +//------------------------------------------------------------------------------ + +type BoolCmd struct { + *baseCmd + + val bool +} + +func NewBoolCmd(args ...string) *BoolCmd { + return &BoolCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *BoolCmd) Val() bool { + return cmd.val +} + +func (cmd *BoolCmd) Result() (bool, error) { + return cmd.val, cmd.err +} + +func (cmd *BoolCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *BoolCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.(int64) == 1 + return nil +} + +//------------------------------------------------------------------------------ + +type StringCmd struct { + *baseCmd + + val string +} + +func NewStringCmd(args ...string) *StringCmd { + return &StringCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *StringCmd) Val() string { + return cmd.val +} + +func (cmd *StringCmd) Result() (string, error) { + return cmd.val, cmd.err +} + +func (cmd *StringCmd) Int64() (int64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseInt(cmd.val, 10, 64) +} + +func (cmd *StringCmd) Uint64() (uint64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseUint(cmd.val, 10, 64) +} + +func (cmd *StringCmd) Float64() (float64, error) { + if cmd.err != nil { + return 0, cmd.err + } + return strconv.ParseFloat(cmd.val, 64) +} + +func (cmd *StringCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.(string) + return nil +} + +//------------------------------------------------------------------------------ + +type FloatCmd struct { + *baseCmd + + val float64 +} + +func NewFloatCmd(args ...string) *FloatCmd { + return &FloatCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *FloatCmd) Val() float64 { + return cmd.val +} + +func (cmd *FloatCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *FloatCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, nil) + if err != nil { + cmd.err = err + return err + } + cmd.val, cmd.err = strconv.ParseFloat(v.(string), 64) + return cmd.err +} + +//------------------------------------------------------------------------------ + +type StringSliceCmd struct { + *baseCmd + + val []string +} + +func NewStringSliceCmd(args ...string) *StringSliceCmd { + return &StringSliceCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *StringSliceCmd) Val() []string { + return cmd.val +} + +func (cmd *StringSliceCmd) Result() ([]string, error) { + return cmd.Val(), cmd.Err() +} + +func (cmd *StringSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringSliceCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, parseStringSlice) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.([]string) + return nil +} + +//------------------------------------------------------------------------------ + +type BoolSliceCmd struct { + *baseCmd + + val []bool +} + +func NewBoolSliceCmd(args ...string) *BoolSliceCmd { + return &BoolSliceCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *BoolSliceCmd) Val() []bool { + return cmd.val +} + +func (cmd *BoolSliceCmd) Result() ([]bool, error) { + return cmd.val, cmd.err +} + +func (cmd *BoolSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *BoolSliceCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, parseBoolSlice) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.([]bool) + return nil +} + +//------------------------------------------------------------------------------ + +type StringStringMapCmd struct { + *baseCmd + + val map[string]string +} + +func NewStringStringMapCmd(args ...string) *StringStringMapCmd { + return &StringStringMapCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *StringStringMapCmd) Val() map[string]string { + return cmd.val +} + +func (cmd *StringStringMapCmd) Result() (map[string]string, error) { + return cmd.val, cmd.err +} + +func (cmd *StringStringMapCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *StringStringMapCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, parseStringStringMap) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.(map[string]string) + return nil +} + +//------------------------------------------------------------------------------ + +type ZSliceCmd struct { + *baseCmd + + val []Z +} + +func NewZSliceCmd(args ...string) *ZSliceCmd { + return &ZSliceCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *ZSliceCmd) Val() []Z { + return cmd.val +} + +func (cmd *ZSliceCmd) Result() ([]Z, error) { + return cmd.val, cmd.err +} + +func (cmd *ZSliceCmd) String() string { + return cmdString(cmd, cmd.val) +} + +func (cmd *ZSliceCmd) parseReply(rd *bufio.Reader) error { + v, err := parseReply(rd, parseZSlice) + if err != nil { + cmd.err = err + return err + } + cmd.val = v.([]Z) + return nil +} + +//------------------------------------------------------------------------------ + +type ScanCmd struct { + *baseCmd + + cursor int64 + keys []string +} + +func NewScanCmd(args ...string) *ScanCmd { + return &ScanCmd{ + baseCmd: newBaseCmd(args...), + } +} + +func (cmd *ScanCmd) Val() (int64, []string) { + return cmd.cursor, cmd.keys +} + +func (cmd *ScanCmd) Result() (int64, []string, error) { + return cmd.cursor, cmd.keys, cmd.err +} + +func (cmd *ScanCmd) String() string { + return cmdString(cmd, cmd.keys) +} + +func (cmd *ScanCmd) parseReply(rd *bufio.Reader) error { + vi, err := parseReply(rd, parseSlice) + if err != nil { + cmd.err = err + return cmd.err + } + v := vi.([]interface{}) + + cmd.cursor, cmd.err = strconv.ParseInt(v[0].(string), 10, 64) + if cmd.err != nil { + return cmd.err + } + + keys := v[1].([]interface{}) + for _, keyi := range keys { + cmd.keys = append(cmd.keys, keyi.(string)) + } + + return nil +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/commands.go b/Godeps/_workspace/src/gopkg.in/redis.v2/commands.go new file mode 100644 index 00000000000..6068bab17e1 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/commands.go @@ -0,0 +1,1246 @@ +package redis + +import ( + "io" + "strconv" + "time" +) + +func formatFloat(f float64) string { + return strconv.FormatFloat(f, 'f', -1, 64) +} + +func readTimeout(sec int64) time.Duration { + if sec == 0 { + return 0 + } + return time.Duration(sec+1) * time.Second +} + +//------------------------------------------------------------------------------ + +func (c *Client) Auth(password string) *StatusCmd { + cmd := NewStatusCmd("AUTH", password) + c.Process(cmd) + return cmd +} + +func (c *Client) Echo(message string) *StringCmd { + cmd := NewStringCmd("ECHO", message) + c.Process(cmd) + return cmd +} + +func (c *Client) Ping() *StatusCmd { + cmd := NewStatusCmd("PING") + c.Process(cmd) + return cmd +} + +func (c *Client) Quit() *StatusCmd { + panic("not implemented") +} + +func (c *Client) Select(index int64) *StatusCmd { + cmd := NewStatusCmd("SELECT", strconv.FormatInt(index, 10)) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) Del(keys ...string) *IntCmd { + args := append([]string{"DEL"}, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) Dump(key string) *StringCmd { + cmd := NewStringCmd("DUMP", key) + c.Process(cmd) + return cmd +} + +func (c *Client) Exists(key string) *BoolCmd { + cmd := NewBoolCmd("EXISTS", key) + c.Process(cmd) + return cmd +} + +func (c *Client) Expire(key string, dur time.Duration) *BoolCmd { + cmd := NewBoolCmd("EXPIRE", key, strconv.FormatInt(int64(dur/time.Second), 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) ExpireAt(key string, tm time.Time) *BoolCmd { + cmd := NewBoolCmd("EXPIREAT", key, strconv.FormatInt(tm.Unix(), 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) Keys(pattern string) *StringSliceCmd { + cmd := NewStringSliceCmd("KEYS", pattern) + c.Process(cmd) + return cmd +} + +func (c *Client) Migrate(host, port, key string, db, timeout int64) *StatusCmd { + cmd := NewStatusCmd( + "MIGRATE", + host, + port, + key, + strconv.FormatInt(db, 10), + strconv.FormatInt(timeout, 10), + ) + cmd.setReadTimeout(readTimeout(timeout)) + c.Process(cmd) + return cmd +} + +func (c *Client) Move(key string, db int64) *BoolCmd { + cmd := NewBoolCmd("MOVE", key, strconv.FormatInt(db, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) ObjectRefCount(keys ...string) *IntCmd { + args := append([]string{"OBJECT", "REFCOUNT"}, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ObjectEncoding(keys ...string) *StringCmd { + args := append([]string{"OBJECT", "ENCODING"}, keys...) + cmd := NewStringCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ObjectIdleTime(keys ...string) *DurationCmd { + args := append([]string{"OBJECT", "IDLETIME"}, keys...) + cmd := NewDurationCmd(time.Second, args...) + c.Process(cmd) + return cmd +} + +func (c *Client) Persist(key string) *BoolCmd { + cmd := NewBoolCmd("PERSIST", key) + c.Process(cmd) + return cmd +} + +func (c *Client) PExpire(key string, dur time.Duration) *BoolCmd { + cmd := NewBoolCmd("PEXPIRE", key, strconv.FormatInt(int64(dur/time.Millisecond), 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) PExpireAt(key string, tm time.Time) *BoolCmd { + cmd := NewBoolCmd( + "PEXPIREAT", + key, + strconv.FormatInt(tm.UnixNano()/int64(time.Millisecond), 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) PTTL(key string) *DurationCmd { + cmd := NewDurationCmd(time.Millisecond, "PTTL", key) + c.Process(cmd) + return cmd +} + +func (c *Client) RandomKey() *StringCmd { + cmd := NewStringCmd("RANDOMKEY") + c.Process(cmd) + return cmd +} + +func (c *Client) Rename(key, newkey string) *StatusCmd { + cmd := NewStatusCmd("RENAME", key, newkey) + c.Process(cmd) + return cmd +} + +func (c *Client) RenameNX(key, newkey string) *BoolCmd { + cmd := NewBoolCmd("RENAMENX", key, newkey) + c.Process(cmd) + return cmd +} + +func (c *Client) Restore(key string, ttl int64, value string) *StatusCmd { + cmd := NewStatusCmd( + "RESTORE", + key, + strconv.FormatInt(ttl, 10), + value, + ) + c.Process(cmd) + return cmd +} + +type Sort struct { + By string + Offset, Count float64 + Get []string + Order string + IsAlpha bool + Store string +} + +func (c *Client) Sort(key string, sort Sort) *StringSliceCmd { + args := []string{"SORT", key} + if sort.By != "" { + args = append(args, "BY", sort.By) + } + if sort.Offset != 0 || sort.Count != 0 { + args = append(args, "LIMIT", formatFloat(sort.Offset), formatFloat(sort.Count)) + } + for _, get := range sort.Get { + args = append(args, "GET", get) + } + if sort.Order != "" { + args = append(args, sort.Order) + } + if sort.IsAlpha { + args = append(args, "ALPHA") + } + if sort.Store != "" { + args = append(args, "STORE", sort.Store) + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) TTL(key string) *DurationCmd { + cmd := NewDurationCmd(time.Second, "TTL", key) + c.Process(cmd) + return cmd +} + +func (c *Client) Type(key string) *StatusCmd { + cmd := NewStatusCmd("TYPE", key) + c.Process(cmd) + return cmd +} + +func (c *Client) Scan(cursor int64, match string, count int64) *ScanCmd { + args := []string{"SCAN", strconv.FormatInt(cursor, 10)} + if match != "" { + args = append(args, "MATCH", match) + } + if count > 0 { + args = append(args, "COUNT", strconv.FormatInt(count, 10)) + } + cmd := NewScanCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SScan(key string, cursor int64, match string, count int64) *ScanCmd { + args := []string{"SSCAN", key, strconv.FormatInt(cursor, 10)} + if match != "" { + args = append(args, "MATCH", match) + } + if count > 0 { + args = append(args, "COUNT", strconv.FormatInt(count, 10)) + } + cmd := NewScanCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) HScan(key string, cursor int64, match string, count int64) *ScanCmd { + args := []string{"HSCAN", key, strconv.FormatInt(cursor, 10)} + if match != "" { + args = append(args, "MATCH", match) + } + if count > 0 { + args = append(args, "COUNT", strconv.FormatInt(count, 10)) + } + cmd := NewScanCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZScan(key string, cursor int64, match string, count int64) *ScanCmd { + args := []string{"ZSCAN", key, strconv.FormatInt(cursor, 10)} + if match != "" { + args = append(args, "MATCH", match) + } + if count > 0 { + args = append(args, "COUNT", strconv.FormatInt(count, 10)) + } + cmd := NewScanCmd(args...) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) Append(key, value string) *IntCmd { + cmd := NewIntCmd("APPEND", key, value) + c.Process(cmd) + return cmd +} + +type BitCount struct { + Start, End int64 +} + +func (c *Client) BitCount(key string, bitCount *BitCount) *IntCmd { + args := []string{"BITCOUNT", key} + if bitCount != nil { + args = append( + args, + strconv.FormatInt(bitCount.Start, 10), + strconv.FormatInt(bitCount.End, 10), + ) + } + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) bitOp(op, destKey string, keys ...string) *IntCmd { + args := []string{"BITOP", op, destKey} + args = append(args, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) BitOpAnd(destKey string, keys ...string) *IntCmd { + return c.bitOp("AND", destKey, keys...) +} + +func (c *Client) BitOpOr(destKey string, keys ...string) *IntCmd { + return c.bitOp("OR", destKey, keys...) +} + +func (c *Client) BitOpXor(destKey string, keys ...string) *IntCmd { + return c.bitOp("XOR", destKey, keys...) +} + +func (c *Client) BitOpNot(destKey string, key string) *IntCmd { + return c.bitOp("NOT", destKey, key) +} + +func (c *Client) Decr(key string) *IntCmd { + cmd := NewIntCmd("DECR", key) + c.Process(cmd) + return cmd +} + +func (c *Client) DecrBy(key string, decrement int64) *IntCmd { + cmd := NewIntCmd("DECRBY", key, strconv.FormatInt(decrement, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) Get(key string) *StringCmd { + cmd := NewStringCmd("GET", key) + c.Process(cmd) + return cmd +} + +func (c *Client) GetBit(key string, offset int64) *IntCmd { + cmd := NewIntCmd("GETBIT", key, strconv.FormatInt(offset, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) GetRange(key string, start, end int64) *StringCmd { + cmd := NewStringCmd( + "GETRANGE", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(end, 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) GetSet(key, value string) *StringCmd { + cmd := NewStringCmd("GETSET", key, value) + c.Process(cmd) + return cmd +} + +func (c *Client) Incr(key string) *IntCmd { + cmd := NewIntCmd("INCR", key) + c.Process(cmd) + return cmd +} + +func (c *Client) IncrBy(key string, value int64) *IntCmd { + cmd := NewIntCmd("INCRBY", key, strconv.FormatInt(value, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) IncrByFloat(key string, value float64) *FloatCmd { + cmd := NewFloatCmd("INCRBYFLOAT", key, formatFloat(value)) + c.Process(cmd) + return cmd +} + +func (c *Client) MGet(keys ...string) *SliceCmd { + args := append([]string{"MGET"}, keys...) + cmd := NewSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) MSet(pairs ...string) *StatusCmd { + args := append([]string{"MSET"}, pairs...) + cmd := NewStatusCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) MSetNX(pairs ...string) *BoolCmd { + args := append([]string{"MSETNX"}, pairs...) + cmd := NewBoolCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) PSetEx(key string, dur time.Duration, value string) *StatusCmd { + cmd := NewStatusCmd( + "PSETEX", + key, + strconv.FormatInt(int64(dur/time.Millisecond), 10), + value, + ) + c.Process(cmd) + return cmd +} + +func (c *Client) Set(key, value string) *StatusCmd { + cmd := NewStatusCmd("SET", key, value) + c.Process(cmd) + return cmd +} + +func (c *Client) SetBit(key string, offset int64, value int) *IntCmd { + cmd := NewIntCmd( + "SETBIT", + key, + strconv.FormatInt(offset, 10), + strconv.FormatInt(int64(value), 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) SetEx(key string, dur time.Duration, value string) *StatusCmd { + cmd := NewStatusCmd("SETEX", key, strconv.FormatInt(int64(dur/time.Second), 10), value) + c.Process(cmd) + return cmd +} + +func (c *Client) SetNX(key, value string) *BoolCmd { + cmd := NewBoolCmd("SETNX", key, value) + c.Process(cmd) + return cmd +} + +func (c *Client) SetRange(key string, offset int64, value string) *IntCmd { + cmd := NewIntCmd("SETRANGE", key, strconv.FormatInt(offset, 10), value) + c.Process(cmd) + return cmd +} + +func (c *Client) StrLen(key string) *IntCmd { + cmd := NewIntCmd("STRLEN", key) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) HDel(key string, fields ...string) *IntCmd { + args := append([]string{"HDEL", key}, fields...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) HExists(key, field string) *BoolCmd { + cmd := NewBoolCmd("HEXISTS", key, field) + c.Process(cmd) + return cmd +} + +func (c *Client) HGet(key, field string) *StringCmd { + cmd := NewStringCmd("HGET", key, field) + c.Process(cmd) + return cmd +} + +func (c *Client) HGetAll(key string) *StringSliceCmd { + cmd := NewStringSliceCmd("HGETALL", key) + c.Process(cmd) + return cmd +} + +func (c *Client) HGetAllMap(key string) *StringStringMapCmd { + cmd := NewStringStringMapCmd("HGETALL", key) + c.Process(cmd) + return cmd +} + +func (c *Client) HIncrBy(key, field string, incr int64) *IntCmd { + cmd := NewIntCmd("HINCRBY", key, field, strconv.FormatInt(incr, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) HIncrByFloat(key, field string, incr float64) *FloatCmd { + cmd := NewFloatCmd("HINCRBYFLOAT", key, field, formatFloat(incr)) + c.Process(cmd) + return cmd +} + +func (c *Client) HKeys(key string) *StringSliceCmd { + cmd := NewStringSliceCmd("HKEYS", key) + c.Process(cmd) + return cmd +} + +func (c *Client) HLen(key string) *IntCmd { + cmd := NewIntCmd("HLEN", key) + c.Process(cmd) + return cmd +} + +func (c *Client) HMGet(key string, fields ...string) *SliceCmd { + args := append([]string{"HMGET", key}, fields...) + cmd := NewSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) HMSet(key, field, value string, pairs ...string) *StatusCmd { + args := append([]string{"HMSET", key, field, value}, pairs...) + cmd := NewStatusCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) HSet(key, field, value string) *BoolCmd { + cmd := NewBoolCmd("HSET", key, field, value) + c.Process(cmd) + return cmd +} + +func (c *Client) HSetNX(key, field, value string) *BoolCmd { + cmd := NewBoolCmd("HSETNX", key, field, value) + c.Process(cmd) + return cmd +} + +func (c *Client) HVals(key string) *StringSliceCmd { + cmd := NewStringSliceCmd("HVALS", key) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) BLPop(timeout int64, keys ...string) *StringSliceCmd { + args := append([]string{"BLPOP"}, keys...) + args = append(args, strconv.FormatInt(timeout, 10)) + cmd := NewStringSliceCmd(args...) + cmd.setReadTimeout(readTimeout(timeout)) + c.Process(cmd) + return cmd +} + +func (c *Client) BRPop(timeout int64, keys ...string) *StringSliceCmd { + args := append([]string{"BRPOP"}, keys...) + args = append(args, strconv.FormatInt(timeout, 10)) + cmd := NewStringSliceCmd(args...) + cmd.setReadTimeout(readTimeout(timeout)) + c.Process(cmd) + return cmd +} + +func (c *Client) BRPopLPush(source, destination string, timeout int64) *StringCmd { + cmd := NewStringCmd( + "BRPOPLPUSH", + source, + destination, + strconv.FormatInt(timeout, 10), + ) + cmd.setReadTimeout(readTimeout(timeout)) + c.Process(cmd) + return cmd +} + +func (c *Client) LIndex(key string, index int64) *StringCmd { + cmd := NewStringCmd("LINDEX", key, strconv.FormatInt(index, 10)) + c.Process(cmd) + return cmd +} + +func (c *Client) LInsert(key, op, pivot, value string) *IntCmd { + cmd := NewIntCmd("LINSERT", key, op, pivot, value) + c.Process(cmd) + return cmd +} + +func (c *Client) LLen(key string) *IntCmd { + cmd := NewIntCmd("LLEN", key) + c.Process(cmd) + return cmd +} + +func (c *Client) LPop(key string) *StringCmd { + cmd := NewStringCmd("LPOP", key) + c.Process(cmd) + return cmd +} + +func (c *Client) LPush(key string, values ...string) *IntCmd { + args := append([]string{"LPUSH", key}, values...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) LPushX(key, value string) *IntCmd { + cmd := NewIntCmd("LPUSHX", key, value) + c.Process(cmd) + return cmd +} + +func (c *Client) LRange(key string, start, stop int64) *StringSliceCmd { + cmd := NewStringSliceCmd( + "LRANGE", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) LRem(key string, count int64, value string) *IntCmd { + cmd := NewIntCmd("LREM", key, strconv.FormatInt(count, 10), value) + c.Process(cmd) + return cmd +} + +func (c *Client) LSet(key string, index int64, value string) *StatusCmd { + cmd := NewStatusCmd("LSET", key, strconv.FormatInt(index, 10), value) + c.Process(cmd) + return cmd +} + +func (c *Client) LTrim(key string, start, stop int64) *StatusCmd { + cmd := NewStatusCmd( + "LTRIM", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) RPop(key string) *StringCmd { + cmd := NewStringCmd("RPOP", key) + c.Process(cmd) + return cmd +} + +func (c *Client) RPopLPush(source, destination string) *StringCmd { + cmd := NewStringCmd("RPOPLPUSH", source, destination) + c.Process(cmd) + return cmd +} + +func (c *Client) RPush(key string, values ...string) *IntCmd { + args := append([]string{"RPUSH", key}, values...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) RPushX(key string, value string) *IntCmd { + cmd := NewIntCmd("RPUSHX", key, value) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) SAdd(key string, members ...string) *IntCmd { + args := append([]string{"SADD", key}, members...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SCard(key string) *IntCmd { + cmd := NewIntCmd("SCARD", key) + c.Process(cmd) + return cmd +} + +func (c *Client) SDiff(keys ...string) *StringSliceCmd { + args := append([]string{"SDIFF"}, keys...) + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SDiffStore(destination string, keys ...string) *IntCmd { + args := append([]string{"SDIFFSTORE", destination}, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SInter(keys ...string) *StringSliceCmd { + args := append([]string{"SINTER"}, keys...) + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SInterStore(destination string, keys ...string) *IntCmd { + args := append([]string{"SINTERSTORE", destination}, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SIsMember(key, member string) *BoolCmd { + cmd := NewBoolCmd("SISMEMBER", key, member) + c.Process(cmd) + return cmd +} + +func (c *Client) SMembers(key string) *StringSliceCmd { + cmd := NewStringSliceCmd("SMEMBERS", key) + c.Process(cmd) + return cmd +} + +func (c *Client) SMove(source, destination, member string) *BoolCmd { + cmd := NewBoolCmd("SMOVE", source, destination, member) + c.Process(cmd) + return cmd +} + +func (c *Client) SPop(key string) *StringCmd { + cmd := NewStringCmd("SPOP", key) + c.Process(cmd) + return cmd +} + +func (c *Client) SRandMember(key string) *StringCmd { + cmd := NewStringCmd("SRANDMEMBER", key) + c.Process(cmd) + return cmd +} + +func (c *Client) SRem(key string, members ...string) *IntCmd { + args := append([]string{"SREM", key}, members...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SUnion(keys ...string) *StringSliceCmd { + args := append([]string{"SUNION"}, keys...) + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) SUnionStore(destination string, keys ...string) *IntCmd { + args := append([]string{"SUNIONSTORE", destination}, keys...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +type Z struct { + Score float64 + Member string +} + +type ZStore struct { + Weights []int64 + Aggregate string +} + +func (c *Client) ZAdd(key string, members ...Z) *IntCmd { + args := []string{"ZADD", key} + for _, m := range members { + args = append(args, formatFloat(m.Score), m.Member) + } + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZCard(key string) *IntCmd { + cmd := NewIntCmd("ZCARD", key) + c.Process(cmd) + return cmd +} + +func (c *Client) ZCount(key, min, max string) *IntCmd { + cmd := NewIntCmd("ZCOUNT", key, min, max) + c.Process(cmd) + return cmd +} + +func (c *Client) ZIncrBy(key string, increment float64, member string) *FloatCmd { + cmd := NewFloatCmd("ZINCRBY", key, formatFloat(increment), member) + c.Process(cmd) + return cmd +} + +func (c *Client) ZInterStore( + destination string, + store ZStore, + keys ...string, +) *IntCmd { + args := []string{"ZINTERSTORE", destination, strconv.FormatInt(int64(len(keys)), 10)} + args = append(args, keys...) + if len(store.Weights) > 0 { + args = append(args, "WEIGHTS") + for _, weight := range store.Weights { + args = append(args, strconv.FormatInt(weight, 10)) + } + } + if store.Aggregate != "" { + args = append(args, "AGGREGATE", store.Aggregate) + } + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) zRange(key string, start, stop int64, withScores bool) *StringSliceCmd { + args := []string{ + "ZRANGE", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10), + } + if withScores { + args = append(args, "WITHSCORES") + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRange(key string, start, stop int64) *StringSliceCmd { + return c.zRange(key, start, stop, false) +} + +func (c *Client) ZRangeWithScores(key string, start, stop int64) *ZSliceCmd { + args := []string{ + "ZRANGE", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10), + "WITHSCORES", + } + cmd := NewZSliceCmd(args...) + c.Process(cmd) + return cmd +} + +type ZRangeByScore struct { + Min, Max string + + Offset, Count int64 +} + +func (c *Client) zRangeByScore(key string, opt ZRangeByScore, withScores bool) *StringSliceCmd { + args := []string{"ZRANGEBYSCORE", key, opt.Min, opt.Max} + if withScores { + args = append(args, "WITHSCORES") + } + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "LIMIT", + strconv.FormatInt(opt.Offset, 10), + strconv.FormatInt(opt.Count, 10), + ) + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRangeByScore(key string, opt ZRangeByScore) *StringSliceCmd { + return c.zRangeByScore(key, opt, false) +} + +func (c *Client) ZRangeByScoreWithScores(key string, opt ZRangeByScore) *ZSliceCmd { + args := []string{"ZRANGEBYSCORE", key, opt.Min, opt.Max, "WITHSCORES"} + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "LIMIT", + strconv.FormatInt(opt.Offset, 10), + strconv.FormatInt(opt.Count, 10), + ) + } + cmd := NewZSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRank(key, member string) *IntCmd { + cmd := NewIntCmd("ZRANK", key, member) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRem(key string, members ...string) *IntCmd { + args := append([]string{"ZREM", key}, members...) + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRemRangeByRank(key string, start, stop int64) *IntCmd { + cmd := NewIntCmd( + "ZREMRANGEBYRANK", + key, + strconv.FormatInt(start, 10), + strconv.FormatInt(stop, 10), + ) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRemRangeByScore(key, min, max string) *IntCmd { + cmd := NewIntCmd("ZREMRANGEBYSCORE", key, min, max) + c.Process(cmd) + return cmd +} + +func (c *Client) zRevRange(key, start, stop string, withScores bool) *StringSliceCmd { + args := []string{"ZREVRANGE", key, start, stop} + if withScores { + args = append(args, "WITHSCORES") + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRevRange(key, start, stop string) *StringSliceCmd { + return c.zRevRange(key, start, stop, false) +} + +func (c *Client) ZRevRangeWithScores(key, start, stop string) *ZSliceCmd { + args := []string{"ZREVRANGE", key, start, stop, "WITHSCORES"} + cmd := NewZSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) zRevRangeByScore(key string, opt ZRangeByScore, withScores bool) *StringSliceCmd { + args := []string{"ZREVRANGEBYSCORE", key, opt.Max, opt.Min} + if withScores { + args = append(args, "WITHSCORES") + } + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "LIMIT", + strconv.FormatInt(opt.Offset, 10), + strconv.FormatInt(opt.Count, 10), + ) + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRevRangeByScore(key string, opt ZRangeByScore) *StringSliceCmd { + return c.zRevRangeByScore(key, opt, false) +} + +func (c *Client) ZRevRangeByScoreWithScores(key string, opt ZRangeByScore) *ZSliceCmd { + args := []string{"ZREVRANGEBYSCORE", key, opt.Max, opt.Min, "WITHSCORES"} + if opt.Offset != 0 || opt.Count != 0 { + args = append( + args, + "LIMIT", + strconv.FormatInt(opt.Offset, 10), + strconv.FormatInt(opt.Count, 10), + ) + } + cmd := NewZSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ZRevRank(key, member string) *IntCmd { + cmd := NewIntCmd("ZREVRANK", key, member) + c.Process(cmd) + return cmd +} + +func (c *Client) ZScore(key, member string) *FloatCmd { + cmd := NewFloatCmd("ZSCORE", key, member) + c.Process(cmd) + return cmd +} + +func (c *Client) ZUnionStore( + destination string, + store ZStore, + keys ...string, +) *IntCmd { + args := []string{"ZUNIONSTORE", destination, strconv.FormatInt(int64(len(keys)), 10)} + args = append(args, keys...) + if len(store.Weights) > 0 { + args = append(args, "WEIGHTS") + for _, weight := range store.Weights { + args = append(args, strconv.FormatInt(weight, 10)) + } + } + if store.Aggregate != "" { + args = append(args, "AGGREGATE", store.Aggregate) + } + cmd := NewIntCmd(args...) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) BgRewriteAOF() *StatusCmd { + cmd := NewStatusCmd("BGREWRITEAOF") + c.Process(cmd) + return cmd +} + +func (c *Client) BgSave() *StatusCmd { + cmd := NewStatusCmd("BGSAVE") + c.Process(cmd) + return cmd +} + +func (c *Client) ClientKill(ipPort string) *StatusCmd { + cmd := NewStatusCmd("CLIENT", "KILL", ipPort) + c.Process(cmd) + return cmd +} + +func (c *Client) ClientList() *StringCmd { + cmd := NewStringCmd("CLIENT", "LIST") + c.Process(cmd) + return cmd +} + +func (c *Client) ConfigGet(parameter string) *SliceCmd { + cmd := NewSliceCmd("CONFIG", "GET", parameter) + c.Process(cmd) + return cmd +} + +func (c *Client) ConfigResetStat() *StatusCmd { + cmd := NewStatusCmd("CONFIG", "RESETSTAT") + c.Process(cmd) + return cmd +} + +func (c *Client) ConfigSet(parameter, value string) *StatusCmd { + cmd := NewStatusCmd("CONFIG", "SET", parameter, value) + c.Process(cmd) + return cmd +} + +func (c *Client) DbSize() *IntCmd { + cmd := NewIntCmd("DBSIZE") + c.Process(cmd) + return cmd +} + +func (c *Client) FlushAll() *StatusCmd { + cmd := NewStatusCmd("FLUSHALL") + c.Process(cmd) + return cmd +} + +func (c *Client) FlushDb() *StatusCmd { + cmd := NewStatusCmd("FLUSHDB") + c.Process(cmd) + return cmd +} + +func (c *Client) Info() *StringCmd { + cmd := NewStringCmd("INFO") + c.Process(cmd) + return cmd +} + +func (c *Client) LastSave() *IntCmd { + cmd := NewIntCmd("LASTSAVE") + c.Process(cmd) + return cmd +} + +func (c *Client) Save() *StatusCmd { + cmd := NewStatusCmd("SAVE") + c.Process(cmd) + return cmd +} + +func (c *Client) shutdown(modifier string) *StatusCmd { + var args []string + if modifier == "" { + args = []string{"SHUTDOWN"} + } else { + args = []string{"SHUTDOWN", modifier} + } + cmd := NewStatusCmd(args...) + c.Process(cmd) + if err := cmd.Err(); err != nil { + if err == io.EOF { + // Server quit as expected. + cmd.err = nil + } + } else { + // Server did not quit. String reply contains the reason. + cmd.err = errorf(cmd.val) + cmd.val = "" + } + return cmd +} + +func (c *Client) Shutdown() *StatusCmd { + return c.shutdown("") +} + +func (c *Client) ShutdownSave() *StatusCmd { + return c.shutdown("SAVE") +} + +func (c *Client) ShutdownNoSave() *StatusCmd { + return c.shutdown("NOSAVE") +} + +func (c *Client) SlaveOf(host, port string) *StatusCmd { + cmd := NewStatusCmd("SLAVEOF", host, port) + c.Process(cmd) + return cmd +} + +func (c *Client) SlowLog() { + panic("not implemented") +} + +func (c *Client) Sync() { + panic("not implemented") +} + +func (c *Client) Time() *StringSliceCmd { + cmd := NewStringSliceCmd("TIME") + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) Eval(script string, keys []string, args []string) *Cmd { + cmdArgs := []string{"EVAL", script, strconv.FormatInt(int64(len(keys)), 10)} + cmdArgs = append(cmdArgs, keys...) + cmdArgs = append(cmdArgs, args...) + cmd := NewCmd(cmdArgs...) + c.Process(cmd) + return cmd +} + +func (c *Client) EvalSha(sha1 string, keys []string, args []string) *Cmd { + cmdArgs := []string{"EVALSHA", sha1, strconv.FormatInt(int64(len(keys)), 10)} + cmdArgs = append(cmdArgs, keys...) + cmdArgs = append(cmdArgs, args...) + cmd := NewCmd(cmdArgs...) + c.Process(cmd) + return cmd +} + +func (c *Client) ScriptExists(scripts ...string) *BoolSliceCmd { + args := append([]string{"SCRIPT", "EXISTS"}, scripts...) + cmd := NewBoolSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) ScriptFlush() *StatusCmd { + cmd := NewStatusCmd("SCRIPT", "FLUSH") + c.Process(cmd) + return cmd +} + +func (c *Client) ScriptKill() *StatusCmd { + cmd := NewStatusCmd("SCRIPT", "KILL") + c.Process(cmd) + return cmd +} + +func (c *Client) ScriptLoad(script string) *StringCmd { + cmd := NewStringCmd("SCRIPT", "LOAD", script) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) DebugObject(key string) *StringCmd { + cmd := NewStringCmd("DEBUG", "OBJECT", key) + c.Process(cmd) + return cmd +} + +//------------------------------------------------------------------------------ + +func (c *Client) PubSubChannels(pattern string) *StringSliceCmd { + args := []string{"PUBSUB", "CHANNELS"} + if pattern != "*" { + args = append(args, pattern) + } + cmd := NewStringSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) PubSubNumSub(channels ...string) *SliceCmd { + args := []string{"PUBSUB", "NUMSUB"} + args = append(args, channels...) + cmd := NewSliceCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Client) PubSubNumPat() *IntCmd { + cmd := NewIntCmd("PUBSUB", "NUMPAT") + c.Process(cmd) + return cmd +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/doc.go b/Godeps/_workspace/src/gopkg.in/redis.v2/doc.go new file mode 100644 index 00000000000..55262533a63 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/doc.go @@ -0,0 +1,4 @@ +/* +Package redis implements a Redis client. +*/ +package redis diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/error.go b/Godeps/_workspace/src/gopkg.in/redis.v2/error.go new file mode 100644 index 00000000000..667fffdc682 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/error.go @@ -0,0 +1,23 @@ +package redis + +import ( + "fmt" +) + +// Redis nil reply. +var Nil = errorf("redis: nil") + +// Redis transaction failed. +var TxFailedErr = errorf("redis: transaction failed") + +type redisError struct { + s string +} + +func errorf(s string, args ...interface{}) redisError { + return redisError{s: fmt.Sprintf(s, args...)} +} + +func (err redisError) Error() string { + return err.s +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/example_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/example_test.go new file mode 100644 index 00000000000..dbc95131033 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/example_test.go @@ -0,0 +1,180 @@ +package redis_test + +import ( + "fmt" + "strconv" + + "gopkg.in/redis.v2" +) + +var client *redis.Client + +func init() { + client = redis.NewTCPClient(&redis.Options{ + Addr: ":6379", + }) + client.FlushDb() +} + +func ExampleNewTCPClient() { + client := redis.NewTCPClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 0, // use default DB + }) + + pong, err := client.Ping().Result() + fmt.Println(pong, err) + // Output: PONG +} + +func ExampleNewFailoverClient() { + client := redis.NewFailoverClient(&redis.FailoverOptions{ + MasterName: "master", + SentinelAddrs: []string{":26379"}, + }) + + pong, err := client.Ping().Result() + fmt.Println(pong, err) + // Output: PONG +} + +func ExampleClient() { + if err := client.Set("foo", "bar").Err(); err != nil { + panic(err) + } + + v, err := client.Get("hello").Result() + fmt.Printf("%q %q %v", v, err, err == redis.Nil) + // Output: "" "redis: nil" true +} + +func ExampleClient_Incr() { + if err := client.Incr("counter").Err(); err != nil { + panic(err) + } + + n, err := client.Get("counter").Int64() + fmt.Println(n, err) + // Output: 1 +} + +func ExampleClient_Pipelined() { + cmds, err := client.Pipelined(func(c *redis.Pipeline) error { + c.Set("key1", "hello1") + c.Get("key1") + return nil + }) + fmt.Println(err) + set := cmds[0].(*redis.StatusCmd) + fmt.Println(set) + get := cmds[1].(*redis.StringCmd) + fmt.Println(get) + // Output: + // SET key1 hello1: OK + // GET key1: hello1 +} + +func ExamplePipeline() { + pipeline := client.Pipeline() + set := pipeline.Set("key1", "hello1") + get := pipeline.Get("key1") + cmds, err := pipeline.Exec() + fmt.Println(cmds, err) + fmt.Println(set) + fmt.Println(get) + // Output: [SET key1 hello1: OK GET key1: hello1] + // SET key1 hello1: OK + // GET key1: hello1 +} + +func ExampleMulti() { + incr := func(tx *redis.Multi) ([]redis.Cmder, error) { + s, err := tx.Get("key").Result() + if err != nil && err != redis.Nil { + return nil, err + } + n, _ := strconv.ParseInt(s, 10, 64) + + return tx.Exec(func() error { + tx.Set("key", strconv.FormatInt(n+1, 10)) + return nil + }) + } + + client.Del("key") + + tx := client.Multi() + defer tx.Close() + + watch := tx.Watch("key") + _ = watch.Err() + + for { + cmds, err := incr(tx) + if err == redis.TxFailedErr { + continue + } else if err != nil { + panic(err) + } + fmt.Println(cmds, err) + break + } + + // Output: [SET key 1: OK] +} + +func ExamplePubSub() { + pubsub := client.PubSub() + defer pubsub.Close() + + err := pubsub.Subscribe("mychannel") + _ = err + + msg, err := pubsub.Receive() + fmt.Println(msg, err) + + pub := client.Publish("mychannel", "hello") + _ = pub.Err() + + msg, err = pubsub.Receive() + fmt.Println(msg, err) + + // Output: subscribe: mychannel + // Message +} + +func ExampleScript() { + setnx := redis.NewScript(` + if redis.call("get", KEYS[1]) == false then + redis.call("set", KEYS[1], ARGV[1]) + return 1 + end + return 0 + `) + + v1, err := setnx.Run(client, []string{"keynx"}, []string{"foo"}).Result() + fmt.Println(v1.(int64), err) + + v2, err := setnx.Run(client, []string{"keynx"}, []string{"bar"}).Result() + fmt.Println(v2.(int64), err) + + get := client.Get("keynx") + fmt.Println(get) + + // Output: 1 + // 0 + // GET keynx: foo +} + +func Example_customCommand() { + Get := func(client *redis.Client, key string) *redis.StringCmd { + cmd := redis.NewStringCmd("GET", key) + client.Process(cmd) + return cmd + } + + v, err := Get(client, "key_does_not_exist").Result() + fmt.Printf("%q %s", v, err) + // Output: "" redis: nil +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/export_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/export_test.go new file mode 100644 index 00000000000..7f7fa67972b --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/export_test.go @@ -0,0 +1,5 @@ +package redis + +func (c *baseClient) Pool() pool { + return c.connPool +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/multi.go b/Godeps/_workspace/src/gopkg.in/redis.v2/multi.go new file mode 100644 index 00000000000..bff38dfaaa4 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/multi.go @@ -0,0 +1,138 @@ +package redis + +import ( + "errors" + "fmt" +) + +var errDiscard = errors.New("redis: Discard can be used only inside Exec") + +// Not thread-safe. +type Multi struct { + *Client +} + +func (c *Client) Multi() *Multi { + return &Multi{ + Client: &Client{ + baseClient: &baseClient{ + opt: c.opt, + connPool: newSingleConnPool(c.connPool, true), + }, + }, + } +} + +func (c *Multi) Close() error { + if err := c.Unwatch().Err(); err != nil { + return err + } + return c.Client.Close() +} + +func (c *Multi) Watch(keys ...string) *StatusCmd { + args := append([]string{"WATCH"}, keys...) + cmd := NewStatusCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Multi) Unwatch(keys ...string) *StatusCmd { + args := append([]string{"UNWATCH"}, keys...) + cmd := NewStatusCmd(args...) + c.Process(cmd) + return cmd +} + +func (c *Multi) Discard() error { + if c.cmds == nil { + return errDiscard + } + c.cmds = c.cmds[:1] + return nil +} + +// Exec always returns list of commands. If transaction fails +// TxFailedErr is returned. Otherwise Exec returns error of the first +// failed command or nil. +func (c *Multi) Exec(f func() error) ([]Cmder, error) { + c.cmds = []Cmder{NewStatusCmd("MULTI")} + if err := f(); err != nil { + return nil, err + } + c.cmds = append(c.cmds, NewSliceCmd("EXEC")) + + cmds := c.cmds + c.cmds = nil + + if len(cmds) == 2 { + return []Cmder{}, nil + } + + cn, err := c.conn() + if err != nil { + setCmdsErr(cmds[1:len(cmds)-1], err) + return cmds[1 : len(cmds)-1], err + } + + err = c.execCmds(cn, cmds) + if err != nil { + c.freeConn(cn, err) + return cmds[1 : len(cmds)-1], err + } + + c.putConn(cn) + return cmds[1 : len(cmds)-1], nil +} + +func (c *Multi) execCmds(cn *conn, cmds []Cmder) error { + err := c.writeCmd(cn, cmds...) + if err != nil { + setCmdsErr(cmds[1:len(cmds)-1], err) + return err + } + + statusCmd := NewStatusCmd() + + // Omit last command (EXEC). + cmdsLen := len(cmds) - 1 + + // Parse queued replies. + for i := 0; i < cmdsLen; i++ { + if err := statusCmd.parseReply(cn.rd); err != nil { + setCmdsErr(cmds[1:len(cmds)-1], err) + return err + } + } + + // Parse number of replies. + line, err := readLine(cn.rd) + if err != nil { + setCmdsErr(cmds[1:len(cmds)-1], err) + return err + } + if line[0] != '*' { + err := fmt.Errorf("redis: expected '*', but got line %q", line) + setCmdsErr(cmds[1:len(cmds)-1], err) + return err + } + if len(line) == 3 && line[1] == '-' && line[2] == '1' { + setCmdsErr(cmds[1:len(cmds)-1], TxFailedErr) + return TxFailedErr + } + + var firstCmdErr error + + // Parse replies. + // Loop starts from 1 to omit MULTI cmd. + for i := 1; i < cmdsLen; i++ { + cmd := cmds[i] + if err := cmd.parseReply(cn.rd); err != nil { + if firstCmdErr == nil { + firstCmdErr = err + } + } + } + + return firstCmdErr +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/parser.go b/Godeps/_workspace/src/gopkg.in/redis.v2/parser.go new file mode 100644 index 00000000000..b4c380c7644 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/parser.go @@ -0,0 +1,262 @@ +package redis + +import ( + "errors" + "fmt" + "strconv" + + "gopkg.in/bufio.v1" +) + +type multiBulkParser func(rd *bufio.Reader, n int64) (interface{}, error) + +var ( + errReaderTooSmall = errors.New("redis: reader is too small") +) + +//------------------------------------------------------------------------------ + +func appendArgs(buf []byte, args []string) []byte { + buf = append(buf, '*') + buf = strconv.AppendUint(buf, uint64(len(args)), 10) + buf = append(buf, '\r', '\n') + for _, arg := range args { + buf = append(buf, '$') + buf = strconv.AppendUint(buf, uint64(len(arg)), 10) + buf = append(buf, '\r', '\n') + buf = append(buf, arg...) + buf = append(buf, '\r', '\n') + } + return buf +} + +//------------------------------------------------------------------------------ + +func readLine(rd *bufio.Reader) ([]byte, error) { + line, isPrefix, err := rd.ReadLine() + if err != nil { + return line, err + } + if isPrefix { + return line, errReaderTooSmall + } + return line, nil +} + +func readN(rd *bufio.Reader, n int) ([]byte, error) { + b, err := rd.ReadN(n) + if err == bufio.ErrBufferFull { + tmp := make([]byte, n) + r := copy(tmp, b) + b = tmp + + for { + nn, err := rd.Read(b[r:]) + r += nn + if r >= n { + // Ignore error if we read enough. + break + } + if err != nil { + return nil, err + } + } + } else if err != nil { + return nil, err + } + return b, nil +} + +//------------------------------------------------------------------------------ + +func parseReq(rd *bufio.Reader) ([]string, error) { + line, err := readLine(rd) + if err != nil { + return nil, err + } + + if line[0] != '*' { + return []string{string(line)}, nil + } + numReplies, err := strconv.ParseInt(string(line[1:]), 10, 64) + if err != nil { + return nil, err + } + + args := make([]string, 0, numReplies) + for i := int64(0); i < numReplies; i++ { + line, err = readLine(rd) + if err != nil { + return nil, err + } + if line[0] != '$' { + return nil, fmt.Errorf("redis: expected '$', but got %q", line) + } + + argLen, err := strconv.ParseInt(string(line[1:]), 10, 32) + if err != nil { + return nil, err + } + + arg, err := readN(rd, int(argLen)+2) + if err != nil { + return nil, err + } + args = append(args, string(arg[:argLen])) + } + return args, nil +} + +//------------------------------------------------------------------------------ + +func parseReply(rd *bufio.Reader, p multiBulkParser) (interface{}, error) { + line, err := readLine(rd) + if err != nil { + return nil, err + } + + switch line[0] { + case '-': + return nil, errorf(string(line[1:])) + case '+': + return string(line[1:]), nil + case ':': + v, err := strconv.ParseInt(string(line[1:]), 10, 64) + if err != nil { + return nil, err + } + return v, nil + case '$': + if len(line) == 3 && line[1] == '-' && line[2] == '1' { + return nil, Nil + } + + replyLen, err := strconv.Atoi(string(line[1:])) + if err != nil { + return nil, err + } + + b, err := readN(rd, replyLen+2) + if err != nil { + return nil, err + } + return string(b[:replyLen]), nil + case '*': + if len(line) == 3 && line[1] == '-' && line[2] == '1' { + return nil, Nil + } + + repliesNum, err := strconv.ParseInt(string(line[1:]), 10, 64) + if err != nil { + return nil, err + } + + return p(rd, repliesNum) + } + return nil, fmt.Errorf("redis: can't parse %q", line) +} + +func parseSlice(rd *bufio.Reader, n int64) (interface{}, error) { + vals := make([]interface{}, 0, n) + for i := int64(0); i < n; i++ { + v, err := parseReply(rd, parseSlice) + if err == Nil { + vals = append(vals, nil) + } else if err != nil { + return nil, err + } else { + vals = append(vals, v) + } + } + return vals, nil +} + +func parseStringSlice(rd *bufio.Reader, n int64) (interface{}, error) { + vals := make([]string, 0, n) + for i := int64(0); i < n; i++ { + viface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + v, ok := viface.(string) + if !ok { + return nil, fmt.Errorf("got %T, expected string", viface) + } + vals = append(vals, v) + } + return vals, nil +} + +func parseBoolSlice(rd *bufio.Reader, n int64) (interface{}, error) { + vals := make([]bool, 0, n) + for i := int64(0); i < n; i++ { + viface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + v, ok := viface.(int64) + if !ok { + return nil, fmt.Errorf("got %T, expected int64", viface) + } + vals = append(vals, v == 1) + } + return vals, nil +} + +func parseStringStringMap(rd *bufio.Reader, n int64) (interface{}, error) { + m := make(map[string]string, n/2) + for i := int64(0); i < n; i += 2 { + keyiface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + key, ok := keyiface.(string) + if !ok { + return nil, fmt.Errorf("got %T, expected string", keyiface) + } + + valueiface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + value, ok := valueiface.(string) + if !ok { + return nil, fmt.Errorf("got %T, expected string", valueiface) + } + + m[key] = value + } + return m, nil +} + +func parseZSlice(rd *bufio.Reader, n int64) (interface{}, error) { + zz := make([]Z, n/2) + for i := int64(0); i < n; i += 2 { + z := &zz[i/2] + + memberiface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + member, ok := memberiface.(string) + if !ok { + return nil, fmt.Errorf("got %T, expected string", memberiface) + } + z.Member = member + + scoreiface, err := parseReply(rd, nil) + if err != nil { + return nil, err + } + scorestr, ok := scoreiface.(string) + if !ok { + return nil, fmt.Errorf("got %T, expected string", scoreiface) + } + score, err := strconv.ParseFloat(scorestr, 64) + if err != nil { + return nil, err + } + z.Score = score + } + return zz, nil +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/parser_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/parser_test.go new file mode 100644 index 00000000000..1b9e15810a8 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/parser_test.go @@ -0,0 +1,54 @@ +package redis + +import ( + "testing" + + "gopkg.in/bufio.v1" +) + +func BenchmarkParseReplyStatus(b *testing.B) { + benchmarkParseReply(b, "+OK\r\n", nil, false) +} + +func BenchmarkParseReplyInt(b *testing.B) { + benchmarkParseReply(b, ":1\r\n", nil, false) +} + +func BenchmarkParseReplyError(b *testing.B) { + benchmarkParseReply(b, "-Error message\r\n", nil, true) +} + +func BenchmarkParseReplyString(b *testing.B) { + benchmarkParseReply(b, "$5\r\nhello\r\n", nil, false) +} + +func BenchmarkParseReplySlice(b *testing.B) { + benchmarkParseReply(b, "*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n", parseSlice, false) +} + +func benchmarkParseReply(b *testing.B, reply string, p multiBulkParser, wanterr bool) { + b.StopTimer() + + buf := &bufio.Buffer{} + rd := bufio.NewReader(buf) + for i := 0; i < b.N; i++ { + buf.WriteString(reply) + } + + b.StartTimer() + + for i := 0; i < b.N; i++ { + _, err := parseReply(rd, p) + if !wanterr && err != nil { + panic(err) + } + } +} + +func BenchmarkAppendArgs(b *testing.B) { + buf := make([]byte, 0, 64) + args := []string{"hello", "world", "foo", "bar"} + for i := 0; i < b.N; i++ { + appendArgs(buf, args) + } +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/pipeline.go b/Godeps/_workspace/src/gopkg.in/redis.v2/pipeline.go new file mode 100644 index 00000000000..540d6c51d9b --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/pipeline.go @@ -0,0 +1,91 @@ +package redis + +// Not thread-safe. +type Pipeline struct { + *Client + + closed bool +} + +func (c *Client) Pipeline() *Pipeline { + return &Pipeline{ + Client: &Client{ + baseClient: &baseClient{ + opt: c.opt, + connPool: c.connPool, + + cmds: make([]Cmder, 0), + }, + }, + } +} + +func (c *Client) Pipelined(f func(*Pipeline) error) ([]Cmder, error) { + pc := c.Pipeline() + if err := f(pc); err != nil { + return nil, err + } + cmds, err := pc.Exec() + pc.Close() + return cmds, err +} + +func (c *Pipeline) Close() error { + c.closed = true + return nil +} + +func (c *Pipeline) Discard() error { + if c.closed { + return errClosed + } + c.cmds = c.cmds[:0] + return nil +} + +// Exec always returns list of commands and error of the first failed +// command if any. +func (c *Pipeline) Exec() ([]Cmder, error) { + if c.closed { + return nil, errClosed + } + + cmds := c.cmds + c.cmds = make([]Cmder, 0) + + if len(cmds) == 0 { + return []Cmder{}, nil + } + + cn, err := c.conn() + if err != nil { + setCmdsErr(cmds, err) + return cmds, err + } + + if err := c.execCmds(cn, cmds); err != nil { + c.freeConn(cn, err) + return cmds, err + } + + c.putConn(cn) + return cmds, nil +} + +func (c *Pipeline) execCmds(cn *conn, cmds []Cmder) error { + if err := c.writeCmd(cn, cmds...); err != nil { + setCmdsErr(cmds, err) + return err + } + + var firstCmdErr error + for _, cmd := range cmds { + if err := cmd.parseReply(cn.rd); err != nil { + if firstCmdErr == nil { + firstCmdErr = err + } + } + } + + return firstCmdErr +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/pool.go b/Godeps/_workspace/src/gopkg.in/redis.v2/pool.go new file mode 100644 index 00000000000..bca4d196335 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/pool.go @@ -0,0 +1,405 @@ +package redis + +import ( + "container/list" + "errors" + "log" + "net" + "sync" + "time" + + "gopkg.in/bufio.v1" +) + +var ( + errClosed = errors.New("redis: client is closed") + errRateLimited = errors.New("redis: you open connections too fast") +) + +var ( + zeroTime = time.Time{} +) + +type pool interface { + Get() (*conn, bool, error) + Put(*conn) error + Remove(*conn) error + Len() int + Size() int + Close() error + Filter(func(*conn) bool) +} + +//------------------------------------------------------------------------------ + +type conn struct { + netcn net.Conn + rd *bufio.Reader + buf []byte + + inUse bool + usedAt time.Time + + readTimeout time.Duration + writeTimeout time.Duration + + elem *list.Element +} + +func newConnFunc(dial func() (net.Conn, error)) func() (*conn, error) { + return func() (*conn, error) { + netcn, err := dial() + if err != nil { + return nil, err + } + cn := &conn{ + netcn: netcn, + buf: make([]byte, 0, 64), + } + cn.rd = bufio.NewReader(cn) + return cn, nil + } +} + +func (cn *conn) Read(b []byte) (int, error) { + if cn.readTimeout != 0 { + cn.netcn.SetReadDeadline(time.Now().Add(cn.readTimeout)) + } else { + cn.netcn.SetReadDeadline(zeroTime) + } + return cn.netcn.Read(b) +} + +func (cn *conn) Write(b []byte) (int, error) { + if cn.writeTimeout != 0 { + cn.netcn.SetWriteDeadline(time.Now().Add(cn.writeTimeout)) + } else { + cn.netcn.SetWriteDeadline(zeroTime) + } + return cn.netcn.Write(b) +} + +func (cn *conn) RemoteAddr() net.Addr { + return cn.netcn.RemoteAddr() +} + +func (cn *conn) Close() error { + return cn.netcn.Close() +} + +//------------------------------------------------------------------------------ + +type connPool struct { + dial func() (*conn, error) + rl *rateLimiter + + opt *options + + cond *sync.Cond + conns *list.List + + idleNum int + closed bool +} + +func newConnPool(dial func() (*conn, error), opt *options) *connPool { + return &connPool{ + dial: dial, + rl: newRateLimiter(time.Second, 2*opt.PoolSize), + + opt: opt, + + cond: sync.NewCond(&sync.Mutex{}), + conns: list.New(), + } +} + +func (p *connPool) new() (*conn, error) { + if !p.rl.Check() { + return nil, errRateLimited + } + return p.dial() +} + +func (p *connPool) Get() (*conn, bool, error) { + p.cond.L.Lock() + + if p.closed { + p.cond.L.Unlock() + return nil, false, errClosed + } + + if p.opt.IdleTimeout > 0 { + for el := p.conns.Front(); el != nil; el = el.Next() { + cn := el.Value.(*conn) + if cn.inUse { + break + } + if time.Since(cn.usedAt) > p.opt.IdleTimeout { + if err := p.remove(cn); err != nil { + log.Printf("remove failed: %s", err) + } + } + } + } + + for p.conns.Len() >= p.opt.PoolSize && p.idleNum == 0 { + p.cond.Wait() + } + + if p.idleNum > 0 { + elem := p.conns.Front() + cn := elem.Value.(*conn) + if cn.inUse { + panic("pool: precondition failed") + } + cn.inUse = true + p.conns.MoveToBack(elem) + p.idleNum-- + + p.cond.L.Unlock() + return cn, false, nil + } + + if p.conns.Len() < p.opt.PoolSize { + cn, err := p.new() + if err != nil { + p.cond.L.Unlock() + return nil, false, err + } + + cn.inUse = true + cn.elem = p.conns.PushBack(cn) + + p.cond.L.Unlock() + return cn, true, nil + } + + panic("not reached") +} + +func (p *connPool) Put(cn *conn) error { + if cn.rd.Buffered() != 0 { + b, _ := cn.rd.ReadN(cn.rd.Buffered()) + log.Printf("redis: connection has unread data: %q", b) + return p.Remove(cn) + } + + if p.opt.IdleTimeout > 0 { + cn.usedAt = time.Now() + } + + p.cond.L.Lock() + if p.closed { + p.cond.L.Unlock() + return errClosed + } + cn.inUse = false + p.conns.MoveToFront(cn.elem) + p.idleNum++ + p.cond.Signal() + p.cond.L.Unlock() + + return nil +} + +func (p *connPool) Remove(cn *conn) error { + p.cond.L.Lock() + if p.closed { + // Noop, connection is already closed. + p.cond.L.Unlock() + return nil + } + err := p.remove(cn) + p.cond.Signal() + p.cond.L.Unlock() + return err +} + +func (p *connPool) remove(cn *conn) error { + p.conns.Remove(cn.elem) + cn.elem = nil + if !cn.inUse { + p.idleNum-- + } + return cn.Close() +} + +// Len returns number of idle connections. +func (p *connPool) Len() int { + defer p.cond.L.Unlock() + p.cond.L.Lock() + return p.idleNum +} + +// Size returns number of connections in the pool. +func (p *connPool) Size() int { + defer p.cond.L.Unlock() + p.cond.L.Lock() + return p.conns.Len() +} + +func (p *connPool) Filter(f func(*conn) bool) { + p.cond.L.Lock() + for el, next := p.conns.Front(), p.conns.Front(); el != nil; el = next { + next = el.Next() + cn := el.Value.(*conn) + if !f(cn) { + p.remove(cn) + } + } + p.cond.L.Unlock() +} + +func (p *connPool) Close() error { + defer p.cond.L.Unlock() + p.cond.L.Lock() + if p.closed { + return nil + } + p.closed = true + p.rl.Close() + var retErr error + for { + e := p.conns.Front() + if e == nil { + break + } + if err := p.remove(e.Value.(*conn)); err != nil { + log.Printf("cn.Close failed: %s", err) + retErr = err + } + } + return retErr +} + +//------------------------------------------------------------------------------ + +type singleConnPool struct { + pool pool + + cnMtx sync.Mutex + cn *conn + + reusable bool + + closed bool +} + +func newSingleConnPool(pool pool, reusable bool) *singleConnPool { + return &singleConnPool{ + pool: pool, + reusable: reusable, + } +} + +func (p *singleConnPool) SetConn(cn *conn) { + p.cnMtx.Lock() + p.cn = cn + p.cnMtx.Unlock() +} + +func (p *singleConnPool) Get() (*conn, bool, error) { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + + if p.closed { + return nil, false, errClosed + } + if p.cn != nil { + return p.cn, false, nil + } + + cn, isNew, err := p.pool.Get() + if err != nil { + return nil, false, err + } + p.cn = cn + + return p.cn, isNew, nil +} + +func (p *singleConnPool) Put(cn *conn) error { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + if p.cn != cn { + panic("p.cn != cn") + } + if p.closed { + return errClosed + } + return nil +} + +func (p *singleConnPool) put() error { + err := p.pool.Put(p.cn) + p.cn = nil + return err +} + +func (p *singleConnPool) Remove(cn *conn) error { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + if p.cn == nil { + panic("p.cn == nil") + } + if p.cn != cn { + panic("p.cn != cn") + } + if p.closed { + return errClosed + } + return p.remove() +} + +func (p *singleConnPool) remove() error { + err := p.pool.Remove(p.cn) + p.cn = nil + return err +} + +func (p *singleConnPool) Len() int { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + if p.cn == nil { + return 0 + } + return 1 +} + +func (p *singleConnPool) Size() int { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + if p.cn == nil { + return 0 + } + return 1 +} + +func (p *singleConnPool) Filter(f func(*conn) bool) { + p.cnMtx.Lock() + if p.cn != nil { + if !f(p.cn) { + p.remove() + } + } + p.cnMtx.Unlock() +} + +func (p *singleConnPool) Close() error { + defer p.cnMtx.Unlock() + p.cnMtx.Lock() + if p.closed { + return nil + } + p.closed = true + var err error + if p.cn != nil { + if p.reusable { + err = p.put() + } else { + err = p.remove() + } + } + return err +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/pubsub.go b/Godeps/_workspace/src/gopkg.in/redis.v2/pubsub.go new file mode 100644 index 00000000000..6ac130bac45 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/pubsub.go @@ -0,0 +1,134 @@ +package redis + +import ( + "fmt" + "time" +) + +// Not thread-safe. +type PubSub struct { + *baseClient +} + +func (c *Client) PubSub() *PubSub { + return &PubSub{ + baseClient: &baseClient{ + opt: c.opt, + connPool: newSingleConnPool(c.connPool, false), + }, + } +} + +func (c *Client) Publish(channel, message string) *IntCmd { + req := NewIntCmd("PUBLISH", channel, message) + c.Process(req) + return req +} + +type Message struct { + Channel string + Payload string +} + +func (m *Message) String() string { + return fmt.Sprintf("Message<%s: %s>", m.Channel, m.Payload) +} + +type PMessage struct { + Channel string + Pattern string + Payload string +} + +func (m *PMessage) String() string { + return fmt.Sprintf("PMessage<%s: %s>", m.Channel, m.Payload) +} + +type Subscription struct { + Kind string + Channel string + Count int +} + +func (m *Subscription) String() string { + return fmt.Sprintf("%s: %s", m.Kind, m.Channel) +} + +func (c *PubSub) Receive() (interface{}, error) { + return c.ReceiveTimeout(0) +} + +func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) { + cn, err := c.conn() + if err != nil { + return nil, err + } + cn.readTimeout = timeout + + cmd := NewSliceCmd() + if err := cmd.parseReply(cn.rd); err != nil { + return nil, err + } + + reply := cmd.Val() + + msgName := reply[0].(string) + switch msgName { + case "subscribe", "unsubscribe", "psubscribe", "punsubscribe": + return &Subscription{ + Kind: msgName, + Channel: reply[1].(string), + Count: int(reply[2].(int64)), + }, nil + case "message": + return &Message{ + Channel: reply[1].(string), + Payload: reply[2].(string), + }, nil + case "pmessage": + return &PMessage{ + Pattern: reply[1].(string), + Channel: reply[2].(string), + Payload: reply[3].(string), + }, nil + } + return nil, fmt.Errorf("redis: unsupported message name: %q", msgName) +} + +func (c *PubSub) subscribe(cmd string, channels ...string) error { + cn, err := c.conn() + if err != nil { + return err + } + + args := append([]string{cmd}, channels...) + req := NewSliceCmd(args...) + return c.writeCmd(cn, req) +} + +func (c *PubSub) Subscribe(channels ...string) error { + return c.subscribe("SUBSCRIBE", channels...) +} + +func (c *PubSub) PSubscribe(patterns ...string) error { + return c.subscribe("PSUBSCRIBE", patterns...) +} + +func (c *PubSub) unsubscribe(cmd string, channels ...string) error { + cn, err := c.conn() + if err != nil { + return err + } + + args := append([]string{cmd}, channels...) + req := NewSliceCmd(args...) + return c.writeCmd(cn, req) +} + +func (c *PubSub) Unsubscribe(channels ...string) error { + return c.unsubscribe("UNSUBSCRIBE", channels...) +} + +func (c *PubSub) PUnsubscribe(patterns ...string) error { + return c.unsubscribe("PUNSUBSCRIBE", patterns...) +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit.go b/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit.go new file mode 100644 index 00000000000..20d85127077 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit.go @@ -0,0 +1,53 @@ +package redis + +import ( + "sync/atomic" + "time" +) + +type rateLimiter struct { + v int64 + + _closed int64 +} + +func newRateLimiter(limit time.Duration, bucketSize int) *rateLimiter { + rl := &rateLimiter{ + v: int64(bucketSize), + } + go rl.loop(limit, int64(bucketSize)) + return rl +} + +func (rl *rateLimiter) loop(limit time.Duration, bucketSize int64) { + for { + if rl.closed() { + break + } + if v := atomic.LoadInt64(&rl.v); v < bucketSize { + atomic.AddInt64(&rl.v, 1) + } + time.Sleep(limit) + } +} + +func (rl *rateLimiter) Check() bool { + for { + if v := atomic.LoadInt64(&rl.v); v > 0 { + if atomic.CompareAndSwapInt64(&rl.v, v, v-1) { + return true + } + } else { + return false + } + } +} + +func (rl *rateLimiter) Close() error { + atomic.StoreInt64(&rl._closed, 1) + return nil +} + +func (rl *rateLimiter) closed() bool { + return atomic.LoadInt64(&rl._closed) == 1 +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit_test.go new file mode 100644 index 00000000000..2f0d41a2eb9 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/rate_limit_test.go @@ -0,0 +1,31 @@ +package redis + +import ( + "sync" + "testing" + "time" +) + +func TestRateLimiter(t *testing.T) { + var n = 100000 + if testing.Short() { + n = 1000 + } + rl := newRateLimiter(time.Minute, n) + + wg := &sync.WaitGroup{} + for i := 0; i < n; i++ { + wg.Add(1) + go func() { + if !rl.Check() { + panic("check failed") + } + wg.Done() + }() + } + wg.Wait() + + if rl.Check() && rl.Check() { + t.Fatal("check passed") + } +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/redis.go b/Godeps/_workspace/src/gopkg.in/redis.v2/redis.go new file mode 100644 index 00000000000..0d15dc8f854 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/redis.go @@ -0,0 +1,231 @@ +package redis + +import ( + "log" + "net" + "time" +) + +type baseClient struct { + connPool pool + opt *options + cmds []Cmder +} + +func (c *baseClient) writeCmd(cn *conn, cmds ...Cmder) error { + buf := cn.buf[:0] + for _, cmd := range cmds { + buf = appendArgs(buf, cmd.args()) + } + + _, err := cn.Write(buf) + return err +} + +func (c *baseClient) conn() (*conn, error) { + cn, isNew, err := c.connPool.Get() + if err != nil { + return nil, err + } + + if isNew { + if err := c.initConn(cn); err != nil { + c.removeConn(cn) + return nil, err + } + } + + return cn, nil +} + +func (c *baseClient) initConn(cn *conn) error { + if c.opt.Password == "" && c.opt.DB == 0 { + return nil + } + + pool := newSingleConnPool(c.connPool, false) + pool.SetConn(cn) + + // Client is not closed because we want to reuse underlying connection. + client := &Client{ + baseClient: &baseClient{ + opt: c.opt, + connPool: pool, + }, + } + + if c.opt.Password != "" { + if err := client.Auth(c.opt.Password).Err(); err != nil { + return err + } + } + + if c.opt.DB > 0 { + if err := client.Select(c.opt.DB).Err(); err != nil { + return err + } + } + + return nil +} + +func (c *baseClient) freeConn(cn *conn, ei error) error { + if cn.rd.Buffered() > 0 { + return c.connPool.Remove(cn) + } + if _, ok := ei.(redisError); ok { + return c.connPool.Put(cn) + } + return c.connPool.Remove(cn) +} + +func (c *baseClient) removeConn(cn *conn) { + if err := c.connPool.Remove(cn); err != nil { + log.Printf("pool.Remove failed: %s", err) + } +} + +func (c *baseClient) putConn(cn *conn) { + if err := c.connPool.Put(cn); err != nil { + log.Printf("pool.Put failed: %s", err) + } +} + +func (c *baseClient) Process(cmd Cmder) { + if c.cmds == nil { + c.run(cmd) + } else { + c.cmds = append(c.cmds, cmd) + } +} + +func (c *baseClient) run(cmd Cmder) { + cn, err := c.conn() + if err != nil { + cmd.setErr(err) + return + } + + if timeout := cmd.writeTimeout(); timeout != nil { + cn.writeTimeout = *timeout + } else { + cn.writeTimeout = c.opt.WriteTimeout + } + + if timeout := cmd.readTimeout(); timeout != nil { + cn.readTimeout = *timeout + } else { + cn.readTimeout = c.opt.ReadTimeout + } + + if err := c.writeCmd(cn, cmd); err != nil { + c.freeConn(cn, err) + cmd.setErr(err) + return + } + + if err := cmd.parseReply(cn.rd); err != nil { + c.freeConn(cn, err) + return + } + + c.putConn(cn) +} + +// Close closes the client, releasing any open resources. +func (c *baseClient) Close() error { + return c.connPool.Close() +} + +//------------------------------------------------------------------------------ + +type options struct { + Password string + DB int64 + + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + + PoolSize int + IdleTimeout time.Duration +} + +type Options struct { + Network string + Addr string + + // Dialer creates new network connection and has priority over + // Network and Addr options. + Dialer func() (net.Conn, error) + + Password string + DB int64 + + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + + PoolSize int + IdleTimeout time.Duration +} + +func (opt *Options) getPoolSize() int { + if opt.PoolSize == 0 { + return 10 + } + return opt.PoolSize +} + +func (opt *Options) getDialTimeout() time.Duration { + if opt.DialTimeout == 0 { + return 5 * time.Second + } + return opt.DialTimeout +} + +func (opt *Options) options() *options { + return &options{ + DB: opt.DB, + Password: opt.Password, + + DialTimeout: opt.getDialTimeout(), + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + + PoolSize: opt.getPoolSize(), + IdleTimeout: opt.IdleTimeout, + } +} + +type Client struct { + *baseClient +} + +func NewClient(clOpt *Options) *Client { + opt := clOpt.options() + dialer := clOpt.Dialer + if dialer == nil { + dialer = func() (net.Conn, error) { + return net.DialTimeout(clOpt.Network, clOpt.Addr, opt.DialTimeout) + } + } + return &Client{ + baseClient: &baseClient{ + opt: opt, + connPool: newConnPool(newConnFunc(dialer), opt), + }, + } +} + +// Deprecated. Use NewClient instead. +func NewTCPClient(opt *Options) *Client { + opt.Network = "tcp" + return NewClient(opt) +} + +// Deprecated. Use NewClient instead. +func NewUnixClient(opt *Options) *Client { + opt.Network = "unix" + return NewClient(opt) +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/redis_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/redis_test.go new file mode 100644 index 00000000000..49f84d0e1e4 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/redis_test.go @@ -0,0 +1,3333 @@ +package redis_test + +import ( + "bytes" + "fmt" + "io" + "net" + "sort" + "strconv" + "sync" + "testing" + "time" + + "gopkg.in/redis.v2" + + . "gopkg.in/check.v1" +) + +const redisAddr = ":6379" + +//------------------------------------------------------------------------------ + +func sortStrings(slice []string) []string { + sort.Strings(slice) + return slice +} + +//------------------------------------------------------------------------------ + +type RedisConnectorTest struct{} + +var _ = Suite(&RedisConnectorTest{}) + +func (t *RedisConnectorTest) TestShutdown(c *C) { + c.Skip("shutdowns server") + + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + + shutdown := client.Shutdown() + c.Check(shutdown.Err(), Equals, io.EOF) + c.Check(shutdown.Val(), Equals, "") + + ping := client.Ping() + c.Check(ping.Err(), ErrorMatches, "dial tcp :[0-9]+: connection refused") + c.Check(ping.Val(), Equals, "") +} + +func (t *RedisConnectorTest) TestNewTCPClient(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + ping := client.Ping() + c.Check(ping.Err(), IsNil) + c.Check(ping.Val(), Equals, "PONG") + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestNewUnixClient(c *C) { + c.Skip("not available on Travis CI") + + client := redis.NewUnixClient(&redis.Options{ + Addr: "/tmp/redis.sock", + }) + ping := client.Ping() + c.Check(ping.Err(), IsNil) + c.Check(ping.Val(), Equals, "PONG") + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestDialer(c *C) { + client := redis.NewClient(&redis.Options{ + Dialer: func() (net.Conn, error) { + return net.Dial("tcp", redisAddr) + }, + }) + ping := client.Ping() + c.Check(ping.Err(), IsNil) + c.Check(ping.Val(), Equals, "PONG") + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestClose(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + c.Assert(client.Close(), IsNil) + + ping := client.Ping() + c.Assert(ping.Err(), Not(IsNil)) + c.Assert(ping.Err().Error(), Equals, "redis: client is closed") + + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestPubSubClose(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + + pubsub := client.PubSub() + c.Assert(pubsub.Close(), IsNil) + + _, err := pubsub.Receive() + c.Assert(err, Not(IsNil)) + c.Assert(err.Error(), Equals, "redis: client is closed") + + ping := client.Ping() + c.Assert(ping.Err(), IsNil) + + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestMultiClose(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + + multi := client.Multi() + c.Assert(multi.Close(), IsNil) + + _, err := multi.Exec(func() error { + multi.Ping() + return nil + }) + c.Assert(err, Not(IsNil)) + c.Assert(err.Error(), Equals, "redis: client is closed") + + ping := client.Ping() + c.Assert(ping.Err(), IsNil) + + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestPipelineClose(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + + _, err := client.Pipelined(func(pipeline *redis.Pipeline) error { + c.Assert(pipeline.Close(), IsNil) + pipeline.Ping() + return nil + }) + c.Assert(err, Not(IsNil)) + c.Assert(err.Error(), Equals, "redis: client is closed") + + ping := client.Ping() + c.Assert(ping.Err(), IsNil) + + c.Assert(client.Close(), IsNil) +} + +func (t *RedisConnectorTest) TestIdleTimeout(c *C) { + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + IdleTimeout: time.Nanosecond, + }) + for i := 0; i < 10; i++ { + c.Assert(client.Ping().Err(), IsNil) + } +} + +func (t *RedisConnectorTest) TestSelectDb(c *C) { + client1 := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + DB: 1, + }) + c.Assert(client1.Set("key", "db1").Err(), IsNil) + + client2 := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + DB: 2, + }) + c.Assert(client2.Get("key").Err(), Equals, redis.Nil) +} + +//------------------------------------------------------------------------------ + +type RedisConnPoolTest struct { + client *redis.Client +} + +var _ = Suite(&RedisConnPoolTest{}) + +func (t *RedisConnPoolTest) SetUpTest(c *C) { + t.client = redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) +} + +func (t *RedisConnPoolTest) TearDownTest(c *C) { + c.Assert(t.client.FlushDb().Err(), IsNil) + c.Assert(t.client.Close(), IsNil) +} + +func (t *RedisConnPoolTest) TestConnPoolMaxSize(c *C) { + wg := &sync.WaitGroup{} + for i := 0; i < 1000; i++ { + wg.Add(1) + go func() { + ping := t.client.Ping() + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") + wg.Done() + }() + } + wg.Wait() + + c.Assert(t.client.Pool().Size(), Equals, 10) + c.Assert(t.client.Pool().Len(), Equals, 10) +} + +func (t *RedisConnPoolTest) TestConnPoolMaxSizeOnPipelineClient(c *C) { + const N = 1000 + + wg := &sync.WaitGroup{} + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + pipeline := t.client.Pipeline() + ping := pipeline.Ping() + cmds, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 1) + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") + + c.Assert(pipeline.Close(), IsNil) + + wg.Done() + }() + } + wg.Wait() + + c.Assert(t.client.Pool().Size(), Equals, 10) + c.Assert(t.client.Pool().Len(), Equals, 10) +} + +func (t *RedisConnPoolTest) TestConnPoolMaxSizeOnMultiClient(c *C) { + const N = 1000 + + wg := &sync.WaitGroup{} + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + multi := t.client.Multi() + var ping *redis.StatusCmd + cmds, err := multi.Exec(func() error { + ping = multi.Ping() + return nil + }) + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 1) + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") + + c.Assert(multi.Close(), IsNil) + + wg.Done() + }() + } + wg.Wait() + + c.Assert(t.client.Pool().Size(), Equals, 10) + c.Assert(t.client.Pool().Len(), Equals, 10) +} + +func (t *RedisConnPoolTest) TestConnPoolMaxSizeOnPubSub(c *C) { + const N = 10 + + wg := &sync.WaitGroup{} + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + defer wg.Done() + pubsub := t.client.PubSub() + c.Assert(pubsub.Subscribe(), IsNil) + c.Assert(pubsub.Close(), IsNil) + }() + } + wg.Wait() + + c.Assert(t.client.Pool().Size(), Equals, 0) + c.Assert(t.client.Pool().Len(), Equals, 0) +} + +func (t *RedisConnPoolTest) TestConnPoolRemovesBrokenConn(c *C) { + cn, _, err := t.client.Pool().Get() + c.Assert(err, IsNil) + c.Assert(cn.Close(), IsNil) + c.Assert(t.client.Pool().Put(cn), IsNil) + + ping := t.client.Ping() + c.Assert(ping.Err().Error(), Equals, "use of closed network connection") + c.Assert(ping.Val(), Equals, "") + + ping = t.client.Ping() + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") + + c.Assert(t.client.Pool().Size(), Equals, 1) + c.Assert(t.client.Pool().Len(), Equals, 1) +} + +func (t *RedisConnPoolTest) TestConnPoolReusesConn(c *C) { + for i := 0; i < 1000; i++ { + ping := t.client.Ping() + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") + } + + c.Assert(t.client.Pool().Size(), Equals, 1) + c.Assert(t.client.Pool().Len(), Equals, 1) +} + +//------------------------------------------------------------------------------ + +type RedisTest struct { + client *redis.Client +} + +var _ = Suite(&RedisTest{}) + +func Test(t *testing.T) { TestingT(t) } + +func (t *RedisTest) SetUpTest(c *C) { + t.client = redis.NewTCPClient(&redis.Options{ + Addr: ":6379", + }) + + // This is much faster than Flushall. + c.Assert(t.client.Select(1).Err(), IsNil) + c.Assert(t.client.FlushDb().Err(), IsNil) + c.Assert(t.client.Select(0).Err(), IsNil) + c.Assert(t.client.FlushDb().Err(), IsNil) +} + +func (t *RedisTest) TearDownTest(c *C) { + c.Assert(t.client.Close(), IsNil) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdStringMethod(c *C) { + set := t.client.Set("foo", "bar") + c.Assert(set.String(), Equals, "SET foo bar: OK") + + get := t.client.Get("foo") + c.Assert(get.String(), Equals, "GET foo: bar") +} + +func (t *RedisTest) TestCmdStringMethodError(c *C) { + get2 := t.client.Get("key_does_not_exists") + c.Assert(get2.String(), Equals, "GET key_does_not_exists: redis: nil") +} + +func (t *RedisTest) TestRunWithouthCheckingErrVal(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") + + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") +} + +func (t *RedisTest) TestGetSpecChars(c *C) { + set := t.client.Set("key", "hello1\r\nhello2\r\n") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello1\r\nhello2\r\n") +} + +func (t *RedisTest) TestGetBigVal(c *C) { + val := string(bytes.Repeat([]byte{'*'}, 1<<16)) + + set := t.client.Set("key", val) + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, val) +} + +func (t *RedisTest) TestManyKeys(c *C) { + var n = 100000 + + for i := 0; i < n; i++ { + t.client.Set("keys.key"+strconv.Itoa(i), "hello"+strconv.Itoa(i)) + } + keys := t.client.Keys("keys.*") + c.Assert(keys.Err(), IsNil) + c.Assert(len(keys.Val()), Equals, n) +} + +func (t *RedisTest) TestManyKeys2(c *C) { + var n = 100000 + + keys := []string{"non-existent-key"} + for i := 0; i < n; i++ { + key := "keys.key" + strconv.Itoa(i) + t.client.Set(key, "hello"+strconv.Itoa(i)) + keys = append(keys, key) + } + keys = append(keys, "non-existent-key") + + mget := t.client.MGet(keys...) + c.Assert(mget.Err(), IsNil) + c.Assert(len(mget.Val()), Equals, n+2) + vals := mget.Val() + for i := 0; i < n; i++ { + c.Assert(vals[i+1], Equals, "hello"+strconv.Itoa(i)) + } + c.Assert(vals[0], Equals, nil) + c.Assert(vals[n+1], Equals, nil) +} + +func (t *RedisTest) TestStringCmdHelpers(c *C) { + set := t.client.Set("key", "10") + c.Assert(set.Err(), IsNil) + + n, err := t.client.Get("key").Int64() + c.Assert(err, IsNil) + c.Assert(n, Equals, int64(10)) + + un, err := t.client.Get("key").Uint64() + c.Assert(err, IsNil) + c.Assert(un, Equals, uint64(10)) + + f, err := t.client.Get("key").Float64() + c.Assert(err, IsNil) + c.Assert(f, Equals, float64(10)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestAuth(c *C) { + auth := t.client.Auth("password") + c.Assert(auth.Err(), ErrorMatches, "ERR Client sent AUTH, but no password is set") + c.Assert(auth.Val(), Equals, "") +} + +func (t *RedisTest) TestEcho(c *C) { + echo := t.client.Echo("hello") + c.Assert(echo.Err(), IsNil) + c.Assert(echo.Val(), Equals, "hello") +} + +func (t *RedisTest) TestPing(c *C) { + ping := t.client.Ping() + c.Assert(ping.Err(), IsNil) + c.Assert(ping.Val(), Equals, "PONG") +} + +func (t *RedisTest) TestSelect(c *C) { + sel := t.client.Select(1) + c.Assert(sel.Err(), IsNil) + c.Assert(sel.Val(), Equals, "OK") +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdKeysDel(c *C) { + set := t.client.Set("key1", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + set = t.client.Set("key2", "World") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + del := t.client.Del("key1", "key2", "key3") + c.Assert(del.Err(), IsNil) + c.Assert(del.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestCmdKeysDump(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + dump := t.client.Dump("key") + c.Assert(dump.Err(), IsNil) + c.Assert(dump.Val(), Equals, "\x00\x05hello\x06\x00\xf5\x9f\xb7\xf6\x90a\x1c\x99") +} + +func (t *RedisTest) TestCmdKeysExists(c *C) { + set := t.client.Set("key1", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + exists := t.client.Exists("key1") + c.Assert(exists.Err(), IsNil) + c.Assert(exists.Val(), Equals, true) + + exists = t.client.Exists("key2") + c.Assert(exists.Err(), IsNil) + c.Assert(exists.Val(), Equals, false) +} + +func (t *RedisTest) TestCmdKeysExpire(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expire := t.client.Expire("key", 10*time.Second) + c.Assert(expire.Err(), IsNil) + c.Assert(expire.Val(), Equals, true) + + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, 10*time.Second) + + set = t.client.Set("key", "Hello World") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + ttl = t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val() < 0, Equals, true) +} + +func (t *RedisTest) TestCmdKeysExpireAt(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + exists := t.client.Exists("key") + c.Assert(exists.Err(), IsNil) + c.Assert(exists.Val(), Equals, true) + + expireAt := t.client.ExpireAt("key", time.Now().Add(-time.Hour)) + c.Assert(expireAt.Err(), IsNil) + c.Assert(expireAt.Val(), Equals, true) + + exists = t.client.Exists("key") + c.Assert(exists.Err(), IsNil) + c.Assert(exists.Val(), Equals, false) +} + +func (t *RedisTest) TestCmdKeysKeys(c *C) { + mset := t.client.MSet("one", "1", "two", "2", "three", "3", "four", "4") + c.Assert(mset.Err(), IsNil) + c.Assert(mset.Val(), Equals, "OK") + + keys := t.client.Keys("*o*") + c.Assert(keys.Err(), IsNil) + c.Assert(sortStrings(keys.Val()), DeepEquals, []string{"four", "one", "two"}) + + keys = t.client.Keys("t??") + c.Assert(keys.Err(), IsNil) + c.Assert(keys.Val(), DeepEquals, []string{"two"}) + + keys = t.client.Keys("*") + c.Assert(keys.Err(), IsNil) + c.Assert( + sortStrings(keys.Val()), + DeepEquals, + []string{"four", "one", "three", "two"}, + ) +} + +func (t *RedisTest) TestCmdKeysMigrate(c *C) { + migrate := t.client.Migrate("localhost", "6380", "key", 0, 0) + c.Assert(migrate.Err(), IsNil) + c.Assert(migrate.Val(), Equals, "NOKEY") + + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + migrate = t.client.Migrate("localhost", "6380", "key", 0, 0) + c.Assert(migrate.Err(), ErrorMatches, "IOERR error or timeout writing to target instance") + c.Assert(migrate.Val(), Equals, "") +} + +func (t *RedisTest) TestCmdKeysMove(c *C) { + move := t.client.Move("key", 1) + c.Assert(move.Err(), IsNil) + c.Assert(move.Val(), Equals, false) + + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + move = t.client.Move("key", 1) + c.Assert(move.Err(), IsNil) + c.Assert(move.Val(), Equals, true) + + get := t.client.Get("key") + c.Assert(get.Err(), Equals, redis.Nil) + c.Assert(get.Val(), Equals, "") + + sel := t.client.Select(1) + c.Assert(sel.Err(), IsNil) + c.Assert(sel.Val(), Equals, "OK") + + get = t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdKeysObject(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + refCount := t.client.ObjectRefCount("key") + c.Assert(refCount.Err(), IsNil) + c.Assert(refCount.Val(), Equals, int64(1)) + + enc := t.client.ObjectEncoding("key") + c.Assert(enc.Err(), IsNil) + c.Assert(enc.Val(), Equals, "raw") + + idleTime := t.client.ObjectIdleTime("key") + c.Assert(idleTime.Err(), IsNil) + c.Assert(idleTime.Val(), Equals, time.Duration(0)) +} + +func (t *RedisTest) TestCmdKeysPersist(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expire := t.client.Expire("key", 10*time.Second) + c.Assert(expire.Err(), IsNil) + c.Assert(expire.Val(), Equals, true) + + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, 10*time.Second) + + persist := t.client.Persist("key") + c.Assert(persist.Err(), IsNil) + c.Assert(persist.Val(), Equals, true) + + ttl = t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val() < 0, Equals, true) +} + +func (t *RedisTest) TestCmdKeysPExpire(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expiration := 900 * time.Millisecond + pexpire := t.client.PExpire("key", expiration) + c.Assert(pexpire.Err(), IsNil) + c.Assert(pexpire.Val(), Equals, true) + + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, time.Second) + + pttl := t.client.PTTL("key") + c.Assert(pttl.Err(), IsNil) + c.Assert(pttl.Val() <= expiration, Equals, true) + c.Assert(pttl.Val() >= expiration-time.Millisecond, Equals, true) +} + +func (t *RedisTest) TestCmdKeysPExpireAt(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expiration := 900 * time.Millisecond + pexpireat := t.client.PExpireAt("key", time.Now().Add(expiration)) + c.Assert(pexpireat.Err(), IsNil) + c.Assert(pexpireat.Val(), Equals, true) + + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, time.Second) + + pttl := t.client.PTTL("key") + c.Assert(pttl.Err(), IsNil) + c.Assert(pttl.Val() <= expiration, Equals, true) + c.Assert(pttl.Val() >= expiration-time.Millisecond, Equals, true) +} + +func (t *RedisTest) TestCmdKeysPTTL(c *C) { + set := t.client.Set("key", "Hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expiration := time.Second + expire := t.client.Expire("key", expiration) + c.Assert(expire.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + pttl := t.client.PTTL("key") + c.Assert(pttl.Err(), IsNil) + c.Assert(pttl.Val() <= expiration, Equals, true) + c.Assert(pttl.Val() >= expiration-time.Millisecond, Equals, true) +} + +func (t *RedisTest) TestCmdKeysRandomKey(c *C) { + randomKey := t.client.RandomKey() + c.Assert(randomKey.Err(), Equals, redis.Nil) + c.Assert(randomKey.Val(), Equals, "") + + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + randomKey = t.client.RandomKey() + c.Assert(randomKey.Err(), IsNil) + c.Assert(randomKey.Val(), Equals, "key") +} + +func (t *RedisTest) TestCmdKeysRename(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + status := t.client.Rename("key", "key1") + c.Assert(status.Err(), IsNil) + c.Assert(status.Val(), Equals, "OK") + + get := t.client.Get("key1") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdKeysRenameNX(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + renameNX := t.client.RenameNX("key", "key1") + c.Assert(renameNX.Err(), IsNil) + c.Assert(renameNX.Val(), Equals, true) + + get := t.client.Get("key1") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdKeysRestore(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + dump := t.client.Dump("key") + c.Assert(dump.Err(), IsNil) + + del := t.client.Del("key") + c.Assert(del.Err(), IsNil) + + restore := t.client.Restore("key", 0, dump.Val()) + c.Assert(restore.Err(), IsNil) + c.Assert(restore.Val(), Equals, "OK") + + type_ := t.client.Type("key") + c.Assert(type_.Err(), IsNil) + c.Assert(type_.Val(), Equals, "string") + + lRange := t.client.Get("key") + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdKeysSort(c *C) { + lPush := t.client.LPush("list", "1") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(1)) + lPush = t.client.LPush("list", "3") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(2)) + lPush = t.client.LPush("list", "2") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(3)) + + sort := t.client.Sort("list", redis.Sort{Offset: 0, Count: 2, Order: "ASC"}) + c.Assert(sort.Err(), IsNil) + c.Assert(sort.Val(), DeepEquals, []string{"1", "2"}) +} + +func (t *RedisTest) TestCmdKeysSortBy(c *C) { + lPush := t.client.LPush("list", "1") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(1)) + lPush = t.client.LPush("list", "3") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(2)) + lPush = t.client.LPush("list", "2") + c.Assert(lPush.Err(), IsNil) + c.Assert(lPush.Val(), Equals, int64(3)) + + set := t.client.Set("weight_1", "5") + c.Assert(set.Err(), IsNil) + set = t.client.Set("weight_2", "2") + c.Assert(set.Err(), IsNil) + set = t.client.Set("weight_3", "8") + c.Assert(set.Err(), IsNil) + + sort := t.client.Sort("list", redis.Sort{Offset: 0, Count: 2, Order: "ASC", By: "weight_*"}) + c.Assert(sort.Err(), IsNil) + c.Assert(sort.Val(), DeepEquals, []string{"2", "1"}) +} + +func (t *RedisTest) TestCmdKeysTTL(c *C) { + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val() < 0, Equals, true) + + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + expire := t.client.Expire("key", 60*time.Second) + c.Assert(expire.Err(), IsNil) + c.Assert(expire.Val(), Equals, true) + + ttl = t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, 60*time.Second) +} + +func (t *RedisTest) TestCmdKeysType(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + type_ := t.client.Type("key") + c.Assert(type_.Err(), IsNil) + c.Assert(type_.Val(), Equals, "string") +} + +func (t *RedisTest) TestCmdScan(c *C) { + for i := 0; i < 1000; i++ { + set := t.client.Set(fmt.Sprintf("key%d", i), "hello") + c.Assert(set.Err(), IsNil) + } + + cursor, keys, err := t.client.Scan(0, "", 0).Result() + c.Assert(err, IsNil) + c.Assert(cursor > 0, Equals, true) + c.Assert(len(keys) > 0, Equals, true) +} + +func (t *RedisTest) TestCmdSScan(c *C) { + for i := 0; i < 1000; i++ { + sadd := t.client.SAdd("myset", fmt.Sprintf("member%d", i)) + c.Assert(sadd.Err(), IsNil) + } + + cursor, keys, err := t.client.SScan("myset", 0, "", 0).Result() + c.Assert(err, IsNil) + c.Assert(cursor > 0, Equals, true) + c.Assert(len(keys) > 0, Equals, true) +} + +func (t *RedisTest) TestCmdHScan(c *C) { + for i := 0; i < 1000; i++ { + sadd := t.client.HSet("myhash", fmt.Sprintf("key%d", i), "hello") + c.Assert(sadd.Err(), IsNil) + } + + cursor, keys, err := t.client.HScan("myhash", 0, "", 0).Result() + c.Assert(err, IsNil) + c.Assert(cursor > 0, Equals, true) + c.Assert(len(keys) > 0, Equals, true) +} + +func (t *RedisTest) TestCmdZScan(c *C) { + for i := 0; i < 1000; i++ { + sadd := t.client.ZAdd("myset", redis.Z{float64(i), fmt.Sprintf("member%d", i)}) + c.Assert(sadd.Err(), IsNil) + } + + cursor, keys, err := t.client.ZScan("myset", 0, "", 0).Result() + c.Assert(err, IsNil) + c.Assert(cursor > 0, Equals, true) + c.Assert(len(keys) > 0, Equals, true) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestStringsAppend(c *C) { + exists := t.client.Exists("key") + c.Assert(exists.Err(), IsNil) + c.Assert(exists.Val(), Equals, false) + + append := t.client.Append("key", "Hello") + c.Assert(append.Err(), IsNil) + c.Assert(append.Val(), Equals, int64(5)) + + append = t.client.Append("key", " World") + c.Assert(append.Err(), IsNil) + c.Assert(append.Val(), Equals, int64(11)) + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "Hello World") +} + +func (t *RedisTest) TestStringsBitCount(c *C) { + set := t.client.Set("key", "foobar") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + bitCount := t.client.BitCount("key", nil) + c.Assert(bitCount.Err(), IsNil) + c.Assert(bitCount.Val(), Equals, int64(26)) + + bitCount = t.client.BitCount("key", &redis.BitCount{0, 0}) + c.Assert(bitCount.Err(), IsNil) + c.Assert(bitCount.Val(), Equals, int64(4)) + + bitCount = t.client.BitCount("key", &redis.BitCount{1, 1}) + c.Assert(bitCount.Err(), IsNil) + c.Assert(bitCount.Val(), Equals, int64(6)) +} + +func (t *RedisTest) TestStringsBitOpAnd(c *C) { + set := t.client.Set("key1", "1") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + set = t.client.Set("key2", "0") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + bitOpAnd := t.client.BitOpAnd("dest", "key1", "key2") + c.Assert(bitOpAnd.Err(), IsNil) + c.Assert(bitOpAnd.Val(), Equals, int64(1)) + + get := t.client.Get("dest") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "0") +} + +func (t *RedisTest) TestStringsBitOpOr(c *C) { + set := t.client.Set("key1", "1") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + set = t.client.Set("key2", "0") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + bitOpOr := t.client.BitOpOr("dest", "key1", "key2") + c.Assert(bitOpOr.Err(), IsNil) + c.Assert(bitOpOr.Val(), Equals, int64(1)) + + get := t.client.Get("dest") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "1") +} + +func (t *RedisTest) TestStringsBitOpXor(c *C) { + set := t.client.Set("key1", "\xff") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + set = t.client.Set("key2", "\x0f") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + bitOpXor := t.client.BitOpXor("dest", "key1", "key2") + c.Assert(bitOpXor.Err(), IsNil) + c.Assert(bitOpXor.Val(), Equals, int64(1)) + + get := t.client.Get("dest") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "\xf0") +} + +func (t *RedisTest) TestStringsBitOpNot(c *C) { + set := t.client.Set("key1", "\x00") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + bitOpNot := t.client.BitOpNot("dest", "key1") + c.Assert(bitOpNot.Err(), IsNil) + c.Assert(bitOpNot.Val(), Equals, int64(1)) + + get := t.client.Get("dest") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "\xff") +} + +func (t *RedisTest) TestStringsDecr(c *C) { + set := t.client.Set("key", "10") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + decr := t.client.Decr("key") + c.Assert(decr.Err(), IsNil) + c.Assert(decr.Val(), Equals, int64(9)) + + set = t.client.Set("key", "234293482390480948029348230948") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + decr = t.client.Decr("key") + c.Assert(decr.Err(), ErrorMatches, "ERR value is not an integer or out of range") + c.Assert(decr.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestStringsDecrBy(c *C) { + set := t.client.Set("key", "10") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + decrBy := t.client.DecrBy("key", 5) + c.Assert(decrBy.Err(), IsNil) + c.Assert(decrBy.Val(), Equals, int64(5)) +} + +func (t *RedisTest) TestStringsGet(c *C) { + get := t.client.Get("_") + c.Assert(get.Err(), Equals, redis.Nil) + c.Assert(get.Val(), Equals, "") + + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + get = t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestStringsGetBit(c *C) { + setBit := t.client.SetBit("key", 7, 1) + c.Assert(setBit.Err(), IsNil) + c.Assert(setBit.Val(), Equals, int64(0)) + + getBit := t.client.GetBit("key", 0) + c.Assert(getBit.Err(), IsNil) + c.Assert(getBit.Val(), Equals, int64(0)) + + getBit = t.client.GetBit("key", 7) + c.Assert(getBit.Err(), IsNil) + c.Assert(getBit.Val(), Equals, int64(1)) + + getBit = t.client.GetBit("key", 100) + c.Assert(getBit.Err(), IsNil) + c.Assert(getBit.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestStringsGetRange(c *C) { + set := t.client.Set("key", "This is a string") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + getRange := t.client.GetRange("key", 0, 3) + c.Assert(getRange.Err(), IsNil) + c.Assert(getRange.Val(), Equals, "This") + + getRange = t.client.GetRange("key", -3, -1) + c.Assert(getRange.Err(), IsNil) + c.Assert(getRange.Val(), Equals, "ing") + + getRange = t.client.GetRange("key", 0, -1) + c.Assert(getRange.Err(), IsNil) + c.Assert(getRange.Val(), Equals, "This is a string") + + getRange = t.client.GetRange("key", 10, 100) + c.Assert(getRange.Err(), IsNil) + c.Assert(getRange.Val(), Equals, "string") +} + +func (t *RedisTest) TestStringsGetSet(c *C) { + incr := t.client.Incr("key") + c.Assert(incr.Err(), IsNil) + c.Assert(incr.Val(), Equals, int64(1)) + + getSet := t.client.GetSet("key", "0") + c.Assert(getSet.Err(), IsNil) + c.Assert(getSet.Val(), Equals, "1") + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "0") +} + +func (t *RedisTest) TestStringsIncr(c *C) { + set := t.client.Set("key", "10") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + incr := t.client.Incr("key") + c.Assert(incr.Err(), IsNil) + c.Assert(incr.Val(), Equals, int64(11)) + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "11") +} + +func (t *RedisTest) TestStringsIncrBy(c *C) { + set := t.client.Set("key", "10") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + incrBy := t.client.IncrBy("key", 5) + c.Assert(incrBy.Err(), IsNil) + c.Assert(incrBy.Val(), Equals, int64(15)) +} + +func (t *RedisTest) TestIncrByFloat(c *C) { + set := t.client.Set("key", "10.50") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + incrByFloat := t.client.IncrByFloat("key", 0.1) + c.Assert(incrByFloat.Err(), IsNil) + c.Assert(incrByFloat.Val(), Equals, 10.6) + + set = t.client.Set("key", "5.0e3") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + incrByFloat = t.client.IncrByFloat("key", 2.0e2) + c.Assert(incrByFloat.Err(), IsNil) + c.Assert(incrByFloat.Val(), Equals, float64(5200)) +} + +func (t *RedisTest) TestIncrByFloatOverflow(c *C) { + incrByFloat := t.client.IncrByFloat("key", 996945661) + c.Assert(incrByFloat.Err(), IsNil) + c.Assert(incrByFloat.Val(), Equals, float64(996945661)) +} + +func (t *RedisTest) TestStringsMSetMGet(c *C) { + mSet := t.client.MSet("key1", "hello1", "key2", "hello2") + c.Assert(mSet.Err(), IsNil) + c.Assert(mSet.Val(), Equals, "OK") + + mGet := t.client.MGet("key1", "key2", "_") + c.Assert(mGet.Err(), IsNil) + c.Assert(mGet.Val(), DeepEquals, []interface{}{"hello1", "hello2", nil}) +} + +func (t *RedisTest) TestStringsMSetNX(c *C) { + mSetNX := t.client.MSetNX("key1", "hello1", "key2", "hello2") + c.Assert(mSetNX.Err(), IsNil) + c.Assert(mSetNX.Val(), Equals, true) + + mSetNX = t.client.MSetNX("key2", "hello1", "key3", "hello2") + c.Assert(mSetNX.Err(), IsNil) + c.Assert(mSetNX.Val(), Equals, false) +} + +func (t *RedisTest) TestStringsPSetEx(c *C) { + expiration := 50 * time.Millisecond + psetex := t.client.PSetEx("key", expiration, "hello") + c.Assert(psetex.Err(), IsNil) + c.Assert(psetex.Val(), Equals, "OK") + + pttl := t.client.PTTL("key") + c.Assert(pttl.Err(), IsNil) + c.Assert(pttl.Val() <= expiration, Equals, true) + c.Assert(pttl.Val() >= expiration-time.Millisecond, Equals, true) + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestStringsSetGet(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestStringsSetEx(c *C) { + setEx := t.client.SetEx("key", 10*time.Second, "hello") + c.Assert(setEx.Err(), IsNil) + c.Assert(setEx.Val(), Equals, "OK") + + ttl := t.client.TTL("key") + c.Assert(ttl.Err(), IsNil) + c.Assert(ttl.Val(), Equals, 10*time.Second) +} + +func (t *RedisTest) TestStringsSetNX(c *C) { + setNX := t.client.SetNX("key", "hello") + c.Assert(setNX.Err(), IsNil) + c.Assert(setNX.Val(), Equals, true) + + setNX = t.client.SetNX("key", "hello2") + c.Assert(setNX.Err(), IsNil) + c.Assert(setNX.Val(), Equals, false) + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestStringsSetRange(c *C) { + set := t.client.Set("key", "Hello World") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + range_ := t.client.SetRange("key", 6, "Redis") + c.Assert(range_.Err(), IsNil) + c.Assert(range_.Val(), Equals, int64(11)) + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "Hello Redis") +} + +func (t *RedisTest) TestStringsStrLen(c *C) { + set := t.client.Set("key", "hello") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + strLen := t.client.StrLen("key") + c.Assert(strLen.Err(), IsNil) + c.Assert(strLen.Val(), Equals, int64(5)) + + strLen = t.client.StrLen("_") + c.Assert(strLen.Err(), IsNil) + c.Assert(strLen.Val(), Equals, int64(0)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdHDel(c *C) { + hSet := t.client.HSet("hash", "key", "hello") + c.Assert(hSet.Err(), IsNil) + + hDel := t.client.HDel("hash", "key") + c.Assert(hDel.Err(), IsNil) + c.Assert(hDel.Val(), Equals, int64(1)) + + hDel = t.client.HDel("hash", "key") + c.Assert(hDel.Err(), IsNil) + c.Assert(hDel.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestCmdHExists(c *C) { + hSet := t.client.HSet("hash", "key", "hello") + c.Assert(hSet.Err(), IsNil) + + hExists := t.client.HExists("hash", "key") + c.Assert(hExists.Err(), IsNil) + c.Assert(hExists.Val(), Equals, true) + + hExists = t.client.HExists("hash", "key1") + c.Assert(hExists.Err(), IsNil) + c.Assert(hExists.Val(), Equals, false) +} + +func (t *RedisTest) TestCmdHGet(c *C) { + hSet := t.client.HSet("hash", "key", "hello") + c.Assert(hSet.Err(), IsNil) + + hGet := t.client.HGet("hash", "key") + c.Assert(hGet.Err(), IsNil) + c.Assert(hGet.Val(), Equals, "hello") + + hGet = t.client.HGet("hash", "key1") + c.Assert(hGet.Err(), Equals, redis.Nil) + c.Assert(hGet.Val(), Equals, "") +} + +func (t *RedisTest) TestCmdHGetAll(c *C) { + hSet := t.client.HSet("hash", "key1", "hello1") + c.Assert(hSet.Err(), IsNil) + hSet = t.client.HSet("hash", "key2", "hello2") + c.Assert(hSet.Err(), IsNil) + + hGetAll := t.client.HGetAll("hash") + c.Assert(hGetAll.Err(), IsNil) + c.Assert(hGetAll.Val(), DeepEquals, []string{"key1", "hello1", "key2", "hello2"}) +} + +func (t *RedisTest) TestCmdHGetAllMap(c *C) { + hSet := t.client.HSet("hash", "key1", "hello1") + c.Assert(hSet.Err(), IsNil) + hSet = t.client.HSet("hash", "key2", "hello2") + c.Assert(hSet.Err(), IsNil) + + hGetAll := t.client.HGetAllMap("hash") + c.Assert(hGetAll.Err(), IsNil) + c.Assert(hGetAll.Val(), DeepEquals, map[string]string{"key1": "hello1", "key2": "hello2"}) +} + +func (t *RedisTest) TestCmdHIncrBy(c *C) { + hSet := t.client.HSet("hash", "key", "5") + c.Assert(hSet.Err(), IsNil) + + hIncrBy := t.client.HIncrBy("hash", "key", 1) + c.Assert(hIncrBy.Err(), IsNil) + c.Assert(hIncrBy.Val(), Equals, int64(6)) + + hIncrBy = t.client.HIncrBy("hash", "key", -1) + c.Assert(hIncrBy.Err(), IsNil) + c.Assert(hIncrBy.Val(), Equals, int64(5)) + + hIncrBy = t.client.HIncrBy("hash", "key", -10) + c.Assert(hIncrBy.Err(), IsNil) + c.Assert(hIncrBy.Val(), Equals, int64(-5)) +} + +func (t *RedisTest) TestCmdHIncrByFloat(c *C) { + hSet := t.client.HSet("hash", "field", "10.50") + c.Assert(hSet.Err(), IsNil) + c.Assert(hSet.Val(), Equals, true) + + hIncrByFloat := t.client.HIncrByFloat("hash", "field", 0.1) + c.Assert(hIncrByFloat.Err(), IsNil) + c.Assert(hIncrByFloat.Val(), Equals, 10.6) + + hSet = t.client.HSet("hash", "field", "5.0e3") + c.Assert(hSet.Err(), IsNil) + c.Assert(hSet.Val(), Equals, false) + + hIncrByFloat = t.client.HIncrByFloat("hash", "field", 2.0e2) + c.Assert(hIncrByFloat.Err(), IsNil) + c.Assert(hIncrByFloat.Val(), Equals, float64(5200)) +} + +func (t *RedisTest) TestCmdHKeys(c *C) { + hkeys := t.client.HKeys("hash") + c.Assert(hkeys.Err(), IsNil) + c.Assert(hkeys.Val(), DeepEquals, []string{}) + + hset := t.client.HSet("hash", "key1", "hello1") + c.Assert(hset.Err(), IsNil) + hset = t.client.HSet("hash", "key2", "hello2") + c.Assert(hset.Err(), IsNil) + + hkeys = t.client.HKeys("hash") + c.Assert(hkeys.Err(), IsNil) + c.Assert(hkeys.Val(), DeepEquals, []string{"key1", "key2"}) +} + +func (t *RedisTest) TestCmdHLen(c *C) { + hSet := t.client.HSet("hash", "key1", "hello1") + c.Assert(hSet.Err(), IsNil) + hSet = t.client.HSet("hash", "key2", "hello2") + c.Assert(hSet.Err(), IsNil) + + hLen := t.client.HLen("hash") + c.Assert(hLen.Err(), IsNil) + c.Assert(hLen.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestCmdHMGet(c *C) { + hSet := t.client.HSet("hash", "key1", "hello1") + c.Assert(hSet.Err(), IsNil) + hSet = t.client.HSet("hash", "key2", "hello2") + c.Assert(hSet.Err(), IsNil) + + hMGet := t.client.HMGet("hash", "key1", "key2", "_") + c.Assert(hMGet.Err(), IsNil) + c.Assert(hMGet.Val(), DeepEquals, []interface{}{"hello1", "hello2", nil}) +} + +func (t *RedisTest) TestCmdHMSet(c *C) { + hMSet := t.client.HMSet("hash", "key1", "hello1", "key2", "hello2") + c.Assert(hMSet.Err(), IsNil) + c.Assert(hMSet.Val(), Equals, "OK") + + hGet := t.client.HGet("hash", "key1") + c.Assert(hGet.Err(), IsNil) + c.Assert(hGet.Val(), Equals, "hello1") + + hGet = t.client.HGet("hash", "key2") + c.Assert(hGet.Err(), IsNil) + c.Assert(hGet.Val(), Equals, "hello2") +} + +func (t *RedisTest) TestCmdHSet(c *C) { + hSet := t.client.HSet("hash", "key", "hello") + c.Assert(hSet.Err(), IsNil) + c.Assert(hSet.Val(), Equals, true) + + hGet := t.client.HGet("hash", "key") + c.Assert(hGet.Err(), IsNil) + c.Assert(hGet.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdHSetNX(c *C) { + hSetNX := t.client.HSetNX("hash", "key", "hello") + c.Assert(hSetNX.Err(), IsNil) + c.Assert(hSetNX.Val(), Equals, true) + + hSetNX = t.client.HSetNX("hash", "key", "hello") + c.Assert(hSetNX.Err(), IsNil) + c.Assert(hSetNX.Val(), Equals, false) + + hGet := t.client.HGet("hash", "key") + c.Assert(hGet.Err(), IsNil) + c.Assert(hGet.Val(), Equals, "hello") +} + +func (t *RedisTest) TestCmdHVals(c *C) { + hSet := t.client.HSet("hash", "key1", "hello1") + c.Assert(hSet.Err(), IsNil) + hSet = t.client.HSet("hash", "key2", "hello2") + c.Assert(hSet.Err(), IsNil) + + hVals := t.client.HVals("hash") + c.Assert(hVals.Err(), IsNil) + c.Assert(hVals.Val(), DeepEquals, []string{"hello1", "hello2"}) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdListsBLPop(c *C) { + rPush := t.client.RPush("list1", "a", "b", "c") + c.Assert(rPush.Err(), IsNil) + + bLPop := t.client.BLPop(0, "list1", "list2") + c.Assert(bLPop.Err(), IsNil) + c.Assert(bLPop.Val(), DeepEquals, []string{"list1", "a"}) +} + +func (t *RedisTest) TestCmdListsBLPopBlocks(c *C) { + started := make(chan bool) + done := make(chan bool) + go func() { + started <- true + bLPop := t.client.BLPop(0, "list") + c.Assert(bLPop.Err(), IsNil) + c.Assert(bLPop.Val(), DeepEquals, []string{"list", "a"}) + done <- true + }() + <-started + + select { + case <-done: + c.Error("BLPop is not blocked") + case <-time.After(time.Second): + // ok + } + + rPush := t.client.RPush("list", "a") + c.Assert(rPush.Err(), IsNil) + + select { + case <-done: + // ok + case <-time.After(time.Second): + c.Error("BLPop is still blocked") + // ok + } +} + +func (t *RedisTest) TestCmdListsBLPopTimeout(c *C) { + bLPop := t.client.BLPop(1, "list1") + c.Assert(bLPop.Err(), Equals, redis.Nil) + c.Assert(bLPop.Val(), IsNil) +} + +func (t *RedisTest) TestCmdListsBRPop(c *C) { + rPush := t.client.RPush("list1", "a", "b", "c") + c.Assert(rPush.Err(), IsNil) + + bRPop := t.client.BRPop(0, "list1", "list2") + c.Assert(bRPop.Err(), IsNil) + c.Assert(bRPop.Val(), DeepEquals, []string{"list1", "c"}) +} + +func (t *RedisTest) TestCmdListsBRPopBlocks(c *C) { + started := make(chan bool) + done := make(chan bool) + go func() { + started <- true + brpop := t.client.BRPop(0, "list") + c.Assert(brpop.Err(), IsNil) + c.Assert(brpop.Val(), DeepEquals, []string{"list", "a"}) + done <- true + }() + <-started + + select { + case <-done: + c.Error("BRPop is not blocked") + case <-time.After(time.Second): + // ok + } + + rPush := t.client.RPush("list", "a") + c.Assert(rPush.Err(), IsNil) + + select { + case <-done: + // ok + case <-time.After(time.Second): + c.Error("BRPop is still blocked") + // ok + } +} + +func (t *RedisTest) TestCmdListsBRPopLPush(c *C) { + rPush := t.client.RPush("list1", "a", "b", "c") + c.Assert(rPush.Err(), IsNil) + + bRPopLPush := t.client.BRPopLPush("list1", "list2", 0) + c.Assert(bRPopLPush.Err(), IsNil) + c.Assert(bRPopLPush.Val(), Equals, "c") +} + +func (t *RedisTest) TestCmdListsLIndex(c *C) { + lPush := t.client.LPush("list", "World") + c.Assert(lPush.Err(), IsNil) + lPush = t.client.LPush("list", "Hello") + c.Assert(lPush.Err(), IsNil) + + lIndex := t.client.LIndex("list", 0) + c.Assert(lIndex.Err(), IsNil) + c.Assert(lIndex.Val(), Equals, "Hello") + + lIndex = t.client.LIndex("list", -1) + c.Assert(lIndex.Err(), IsNil) + c.Assert(lIndex.Val(), Equals, "World") + + lIndex = t.client.LIndex("list", 3) + c.Assert(lIndex.Err(), Equals, redis.Nil) + c.Assert(lIndex.Val(), Equals, "") +} + +func (t *RedisTest) TestCmdListsLInsert(c *C) { + rPush := t.client.RPush("list", "Hello") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "World") + c.Assert(rPush.Err(), IsNil) + + lInsert := t.client.LInsert("list", "BEFORE", "World", "There") + c.Assert(lInsert.Err(), IsNil) + c.Assert(lInsert.Val(), Equals, int64(3)) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"Hello", "There", "World"}) +} + +func (t *RedisTest) TestCmdListsLLen(c *C) { + lPush := t.client.LPush("list", "World") + c.Assert(lPush.Err(), IsNil) + lPush = t.client.LPush("list", "Hello") + c.Assert(lPush.Err(), IsNil) + + lLen := t.client.LLen("list") + c.Assert(lLen.Err(), IsNil) + c.Assert(lLen.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestCmdListsLPop(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + lPop := t.client.LPop("list") + c.Assert(lPop.Err(), IsNil) + c.Assert(lPop.Val(), Equals, "one") + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"two", "three"}) +} + +func (t *RedisTest) TestCmdListsLPush(c *C) { + lPush := t.client.LPush("list", "World") + c.Assert(lPush.Err(), IsNil) + lPush = t.client.LPush("list", "Hello") + c.Assert(lPush.Err(), IsNil) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"Hello", "World"}) +} + +func (t *RedisTest) TestCmdListsLPushX(c *C) { + lPush := t.client.LPush("list", "World") + c.Assert(lPush.Err(), IsNil) + + lPushX := t.client.LPushX("list", "Hello") + c.Assert(lPushX.Err(), IsNil) + c.Assert(lPushX.Val(), Equals, int64(2)) + + lPushX = t.client.LPushX("list2", "Hello") + c.Assert(lPushX.Err(), IsNil) + c.Assert(lPushX.Val(), Equals, int64(0)) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"Hello", "World"}) + + lRange = t.client.LRange("list2", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{}) +} + +func (t *RedisTest) TestCmdListsLRange(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + lRange := t.client.LRange("list", 0, 0) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"one"}) + + lRange = t.client.LRange("list", -3, 2) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"one", "two", "three"}) + + lRange = t.client.LRange("list", -100, 100) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"one", "two", "three"}) + + lRange = t.client.LRange("list", 5, 10) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{}) +} + +func (t *RedisTest) TestCmdListsLRem(c *C) { + rPush := t.client.RPush("list", "hello") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "hello") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "key") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "hello") + c.Assert(rPush.Err(), IsNil) + + lRem := t.client.LRem("list", -2, "hello") + c.Assert(lRem.Err(), IsNil) + c.Assert(lRem.Val(), Equals, int64(2)) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"hello", "key"}) +} + +func (t *RedisTest) TestCmdListsLSet(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + lSet := t.client.LSet("list", 0, "four") + c.Assert(lSet.Err(), IsNil) + c.Assert(lSet.Val(), Equals, "OK") + + lSet = t.client.LSet("list", -2, "five") + c.Assert(lSet.Err(), IsNil) + c.Assert(lSet.Val(), Equals, "OK") + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"four", "five", "three"}) +} + +func (t *RedisTest) TestCmdListsLTrim(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + lTrim := t.client.LTrim("list", 1, -1) + c.Assert(lTrim.Err(), IsNil) + c.Assert(lTrim.Val(), Equals, "OK") + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"two", "three"}) +} + +func (t *RedisTest) TestCmdListsRPop(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + rPop := t.client.RPop("list") + c.Assert(rPop.Err(), IsNil) + c.Assert(rPop.Val(), Equals, "three") + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"one", "two"}) +} + +func (t *RedisTest) TestCmdListsRPopLPush(c *C) { + rPush := t.client.RPush("list", "one") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "two") + c.Assert(rPush.Err(), IsNil) + rPush = t.client.RPush("list", "three") + c.Assert(rPush.Err(), IsNil) + + rPopLPush := t.client.RPopLPush("list", "list2") + c.Assert(rPopLPush.Err(), IsNil) + c.Assert(rPopLPush.Val(), Equals, "three") + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"one", "two"}) + + lRange = t.client.LRange("list2", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"three"}) +} + +func (t *RedisTest) TestCmdListsRPush(c *C) { + rPush := t.client.RPush("list", "Hello") + c.Assert(rPush.Err(), IsNil) + c.Assert(rPush.Val(), Equals, int64(1)) + + rPush = t.client.RPush("list", "World") + c.Assert(rPush.Err(), IsNil) + c.Assert(rPush.Val(), Equals, int64(2)) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"Hello", "World"}) +} + +func (t *RedisTest) TestCmdListsRPushX(c *C) { + rPush := t.client.RPush("list", "Hello") + c.Assert(rPush.Err(), IsNil) + c.Assert(rPush.Val(), Equals, int64(1)) + + rPushX := t.client.RPushX("list", "World") + c.Assert(rPushX.Err(), IsNil) + c.Assert(rPushX.Val(), Equals, int64(2)) + + rPushX = t.client.RPushX("list2", "World") + c.Assert(rPushX.Err(), IsNil) + c.Assert(rPushX.Val(), Equals, int64(0)) + + lRange := t.client.LRange("list", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{"Hello", "World"}) + + lRange = t.client.LRange("list2", 0, -1) + c.Assert(lRange.Err(), IsNil) + c.Assert(lRange.Val(), DeepEquals, []string{}) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestSAdd(c *C) { + sAdd := t.client.SAdd("set", "Hello") + c.Assert(sAdd.Err(), IsNil) + c.Assert(sAdd.Val(), Equals, int64(1)) + + sAdd = t.client.SAdd("set", "World") + c.Assert(sAdd.Err(), IsNil) + c.Assert(sAdd.Val(), Equals, int64(1)) + + sAdd = t.client.SAdd("set", "World") + c.Assert(sAdd.Err(), IsNil) + c.Assert(sAdd.Val(), Equals, int64(0)) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sortStrings(sMembers.Val()), DeepEquals, []string{"Hello", "World"}) +} + +func (t *RedisTest) TestSCard(c *C) { + sAdd := t.client.SAdd("set", "Hello") + c.Assert(sAdd.Err(), IsNil) + c.Assert(sAdd.Val(), Equals, int64(1)) + + sAdd = t.client.SAdd("set", "World") + c.Assert(sAdd.Err(), IsNil) + c.Assert(sAdd.Val(), Equals, int64(1)) + + sCard := t.client.SCard("set") + c.Assert(sCard.Err(), IsNil) + c.Assert(sCard.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestSDiff(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sDiff := t.client.SDiff("set1", "set2") + c.Assert(sDiff.Err(), IsNil) + c.Assert(sortStrings(sDiff.Val()), DeepEquals, []string{"a", "b"}) +} + +func (t *RedisTest) TestSDiffStore(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sDiffStore := t.client.SDiffStore("set", "set1", "set2") + c.Assert(sDiffStore.Err(), IsNil) + c.Assert(sDiffStore.Val(), Equals, int64(2)) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sortStrings(sMembers.Val()), DeepEquals, []string{"a", "b"}) +} + +func (t *RedisTest) TestSInter(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sInter := t.client.SInter("set1", "set2") + c.Assert(sInter.Err(), IsNil) + c.Assert(sInter.Val(), DeepEquals, []string{"c"}) +} + +func (t *RedisTest) TestSInterStore(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sInterStore := t.client.SInterStore("set", "set1", "set2") + c.Assert(sInterStore.Err(), IsNil) + c.Assert(sInterStore.Val(), Equals, int64(1)) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sMembers.Val(), DeepEquals, []string{"c"}) +} + +func (t *RedisTest) TestIsMember(c *C) { + sAdd := t.client.SAdd("set", "one") + c.Assert(sAdd.Err(), IsNil) + + sIsMember := t.client.SIsMember("set", "one") + c.Assert(sIsMember.Err(), IsNil) + c.Assert(sIsMember.Val(), Equals, true) + + sIsMember = t.client.SIsMember("set", "two") + c.Assert(sIsMember.Err(), IsNil) + c.Assert(sIsMember.Val(), Equals, false) +} + +func (t *RedisTest) TestSMembers(c *C) { + sAdd := t.client.SAdd("set", "Hello") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "World") + c.Assert(sAdd.Err(), IsNil) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sortStrings(sMembers.Val()), DeepEquals, []string{"Hello", "World"}) +} + +func (t *RedisTest) TestSMove(c *C) { + sAdd := t.client.SAdd("set1", "one") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "two") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "three") + c.Assert(sAdd.Err(), IsNil) + + sMove := t.client.SMove("set1", "set2", "two") + c.Assert(sMove.Err(), IsNil) + c.Assert(sMove.Val(), Equals, true) + + sMembers := t.client.SMembers("set1") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sMembers.Val(), DeepEquals, []string{"one"}) + + sMembers = t.client.SMembers("set2") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sortStrings(sMembers.Val()), DeepEquals, []string{"three", "two"}) +} + +func (t *RedisTest) TestSPop(c *C) { + sAdd := t.client.SAdd("set", "one") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "two") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "three") + c.Assert(sAdd.Err(), IsNil) + + sPop := t.client.SPop("set") + c.Assert(sPop.Err(), IsNil) + c.Assert(sPop.Val(), Not(Equals), "") + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sMembers.Val(), HasLen, 2) +} + +func (t *RedisTest) TestSRandMember(c *C) { + sAdd := t.client.SAdd("set", "one") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "two") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "three") + c.Assert(sAdd.Err(), IsNil) + + sRandMember := t.client.SRandMember("set") + c.Assert(sRandMember.Err(), IsNil) + c.Assert(sRandMember.Val(), Not(Equals), "") + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sMembers.Val(), HasLen, 3) +} + +func (t *RedisTest) TestSRem(c *C) { + sAdd := t.client.SAdd("set", "one") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "two") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set", "three") + c.Assert(sAdd.Err(), IsNil) + + sRem := t.client.SRem("set", "one") + c.Assert(sRem.Err(), IsNil) + c.Assert(sRem.Val(), Equals, int64(1)) + + sRem = t.client.SRem("set", "four") + c.Assert(sRem.Err(), IsNil) + c.Assert(sRem.Val(), Equals, int64(0)) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert( + sortStrings(sMembers.Val()), + DeepEquals, + []string{"three", "two"}, + ) +} + +func (t *RedisTest) TestSUnion(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sUnion := t.client.SUnion("set1", "set2") + c.Assert(sUnion.Err(), IsNil) + c.Assert(sUnion.Val(), HasLen, 5) +} + +func (t *RedisTest) TestSUnionStore(c *C) { + sAdd := t.client.SAdd("set1", "a") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "b") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set1", "c") + c.Assert(sAdd.Err(), IsNil) + + sAdd = t.client.SAdd("set2", "c") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "d") + c.Assert(sAdd.Err(), IsNil) + sAdd = t.client.SAdd("set2", "e") + c.Assert(sAdd.Err(), IsNil) + + sUnionStore := t.client.SUnionStore("set", "set1", "set2") + c.Assert(sUnionStore.Err(), IsNil) + c.Assert(sUnionStore.Val(), Equals, int64(5)) + + sMembers := t.client.SMembers("set") + c.Assert(sMembers.Err(), IsNil) + c.Assert(sMembers.Val(), HasLen, 5) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestZAdd(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + c.Assert(zAdd.Val(), Equals, int64(1)) + + zAdd = t.client.ZAdd("zset", redis.Z{1, "uno"}) + c.Assert(zAdd.Err(), IsNil) + c.Assert(zAdd.Val(), Equals, int64(1)) + + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + c.Assert(zAdd.Val(), Equals, int64(1)) + + zAdd = t.client.ZAdd("zset", redis.Z{3, "two"}) + c.Assert(zAdd.Err(), IsNil) + c.Assert(zAdd.Val(), Equals, int64(0)) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}, {1, "uno"}, {3, "two"}}) +} + +func (t *RedisTest) TestZCard(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + + zCard := t.client.ZCard("zset") + c.Assert(zCard.Err(), IsNil) + c.Assert(zCard.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestZCount(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zCount := t.client.ZCount("zset", "-inf", "+inf") + c.Assert(zCount.Err(), IsNil) + c.Assert(zCount.Val(), Equals, int64(3)) + + zCount = t.client.ZCount("zset", "(1", "3") + c.Assert(zCount.Err(), IsNil) + c.Assert(zCount.Val(), Equals, int64(2)) +} + +func (t *RedisTest) TestZIncrBy(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + + zIncrBy := t.client.ZIncrBy("zset", 2, "one") + c.Assert(zIncrBy.Err(), IsNil) + c.Assert(zIncrBy.Val(), Equals, float64(3)) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}, {3, "one"}}) +} + +func (t *RedisTest) TestZInterStore(c *C) { + zAdd := t.client.ZAdd("zset1", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset1", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + + zAdd = t.client.ZAdd("zset2", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset2", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset3", redis.Z{3, "two"}) + c.Assert(zAdd.Err(), IsNil) + + zInterStore := t.client.ZInterStore( + "out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2") + c.Assert(zInterStore.Err(), IsNil) + c.Assert(zInterStore.Val(), Equals, int64(2)) + + val, err := t.client.ZRangeWithScores("out", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{5, "one"}, {10, "two"}}) +} + +func (t *RedisTest) TestZRange(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRange := t.client.ZRange("zset", 0, -1) + c.Assert(zRange.Err(), IsNil) + c.Assert(zRange.Val(), DeepEquals, []string{"one", "two", "three"}) + + zRange = t.client.ZRange("zset", 2, 3) + c.Assert(zRange.Err(), IsNil) + c.Assert(zRange.Val(), DeepEquals, []string{"three"}) + + zRange = t.client.ZRange("zset", -2, -1) + c.Assert(zRange.Err(), IsNil) + c.Assert(zRange.Val(), DeepEquals, []string{"two", "three"}) +} + +func (t *RedisTest) TestZRangeWithScores(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}, {2, "two"}, {3, "three"}}) + + val, err = t.client.ZRangeWithScores("zset", 2, 3).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{3, "three"}}) + + val, err = t.client.ZRangeWithScores("zset", -2, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}, {3, "three"}}) +} + +func (t *RedisTest) TestZRangeByScore(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRangeByScore := t.client.ZRangeByScore("zset", redis.ZRangeByScore{ + Min: "-inf", + Max: "+inf", + }) + c.Assert(zRangeByScore.Err(), IsNil) + c.Assert(zRangeByScore.Val(), DeepEquals, []string{"one", "two", "three"}) + + zRangeByScore = t.client.ZRangeByScore("zset", redis.ZRangeByScore{ + Min: "1", + Max: "2", + }) + c.Assert(zRangeByScore.Err(), IsNil) + c.Assert(zRangeByScore.Val(), DeepEquals, []string{"one", "two"}) + + zRangeByScore = t.client.ZRangeByScore("zset", redis.ZRangeByScore{ + Min: "(1", + Max: "2", + }) + c.Assert(zRangeByScore.Err(), IsNil) + c.Assert(zRangeByScore.Val(), DeepEquals, []string{"two"}) + + zRangeByScore = t.client.ZRangeByScore("zset", redis.ZRangeByScore{ + Min: "(1", + Max: "(2", + }) + c.Assert(zRangeByScore.Err(), IsNil) + c.Assert(zRangeByScore.Val(), DeepEquals, []string{}) +} + +func (t *RedisTest) TestZRangeByScoreWithScoresMap(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + val, err := t.client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{ + Min: "-inf", + Max: "+inf", + }).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}, {2, "two"}, {3, "three"}}) + + val, err = t.client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{ + Min: "1", + Max: "2", + }).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}, {2, "two"}}) + + val, err = t.client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{ + Min: "(1", + Max: "2", + }).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}}) + + val, err = t.client.ZRangeByScoreWithScores("zset", redis.ZRangeByScore{ + Min: "(1", + Max: "(2", + }).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{}) +} + +func (t *RedisTest) TestZRank(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRank := t.client.ZRank("zset", "three") + c.Assert(zRank.Err(), IsNil) + c.Assert(zRank.Val(), Equals, int64(2)) + + zRank = t.client.ZRank("zset", "four") + c.Assert(zRank.Err(), Equals, redis.Nil) + c.Assert(zRank.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestZRem(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRem := t.client.ZRem("zset", "two") + c.Assert(zRem.Err(), IsNil) + c.Assert(zRem.Val(), Equals, int64(1)) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}, {3, "three"}}) +} + +func (t *RedisTest) TestZRemRangeByRank(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRemRangeByRank := t.client.ZRemRangeByRank("zset", 0, 1) + c.Assert(zRemRangeByRank.Err(), IsNil) + c.Assert(zRemRangeByRank.Val(), Equals, int64(2)) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{3, "three"}}) +} + +func (t *RedisTest) TestZRemRangeByScore(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRemRangeByScore := t.client.ZRemRangeByScore("zset", "-inf", "(2") + c.Assert(zRemRangeByScore.Err(), IsNil) + c.Assert(zRemRangeByScore.Val(), Equals, int64(1)) + + val, err := t.client.ZRangeWithScores("zset", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}, {3, "three"}}) +} + +func (t *RedisTest) TestZRevRange(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRevRange := t.client.ZRevRange("zset", "0", "-1") + c.Assert(zRevRange.Err(), IsNil) + c.Assert(zRevRange.Val(), DeepEquals, []string{"three", "two", "one"}) + + zRevRange = t.client.ZRevRange("zset", "2", "3") + c.Assert(zRevRange.Err(), IsNil) + c.Assert(zRevRange.Val(), DeepEquals, []string{"one"}) + + zRevRange = t.client.ZRevRange("zset", "-2", "-1") + c.Assert(zRevRange.Err(), IsNil) + c.Assert(zRevRange.Val(), DeepEquals, []string{"two", "one"}) +} + +func (t *RedisTest) TestZRevRangeWithScoresMap(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + val, err := t.client.ZRevRangeWithScores("zset", "0", "-1").Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{3, "three"}, {2, "two"}, {1, "one"}}) + + val, err = t.client.ZRevRangeWithScores("zset", "2", "3").Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{1, "one"}}) + + val, err = t.client.ZRevRangeWithScores("zset", "-2", "-1").Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}, {1, "one"}}) +} + +func (t *RedisTest) TestZRevRangeByScore(c *C) { + zadd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zadd.Err(), IsNil) + zadd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zadd.Err(), IsNil) + zadd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zadd.Err(), IsNil) + + vals, err := t.client.ZRevRangeByScore( + "zset", redis.ZRangeByScore{Max: "+inf", Min: "-inf"}).Result() + c.Assert(err, IsNil) + c.Assert(vals, DeepEquals, []string{"three", "two", "one"}) + + vals, err = t.client.ZRevRangeByScore( + "zset", redis.ZRangeByScore{Max: "2", Min: "(1"}).Result() + c.Assert(err, IsNil) + c.Assert(vals, DeepEquals, []string{"two"}) + + vals, err = t.client.ZRevRangeByScore( + "zset", redis.ZRangeByScore{Max: "(2", Min: "(1"}).Result() + c.Assert(err, IsNil) + c.Assert(vals, DeepEquals, []string{}) +} + +func (t *RedisTest) TestZRevRangeByScoreWithScores(c *C) { + zadd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zadd.Err(), IsNil) + zadd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zadd.Err(), IsNil) + zadd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zadd.Err(), IsNil) + + vals, err := t.client.ZRevRangeByScoreWithScores( + "zset", redis.ZRangeByScore{Max: "+inf", Min: "-inf"}).Result() + c.Assert(err, IsNil) + c.Assert(vals, DeepEquals, []redis.Z{{3, "three"}, {2, "two"}, {1, "one"}}) +} + +func (t *RedisTest) TestZRevRangeByScoreWithScoresMap(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + val, err := t.client.ZRevRangeByScoreWithScores( + "zset", redis.ZRangeByScore{Max: "+inf", Min: "-inf"}).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{3, "three"}, {2, "two"}, {1, "one"}}) + + val, err = t.client.ZRevRangeByScoreWithScores( + "zset", redis.ZRangeByScore{Max: "2", Min: "(1"}).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{2, "two"}}) + + val, err = t.client.ZRevRangeByScoreWithScores( + "zset", redis.ZRangeByScore{Max: "(2", Min: "(1"}).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{}) +} + +func (t *RedisTest) TestZRevRank(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zRevRank := t.client.ZRevRank("zset", "one") + c.Assert(zRevRank.Err(), IsNil) + c.Assert(zRevRank.Val(), Equals, int64(2)) + + zRevRank = t.client.ZRevRank("zset", "four") + c.Assert(zRevRank.Err(), Equals, redis.Nil) + c.Assert(zRevRank.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestZScore(c *C) { + zAdd := t.client.ZAdd("zset", redis.Z{1.001, "one"}) + c.Assert(zAdd.Err(), IsNil) + + zScore := t.client.ZScore("zset", "one") + c.Assert(zScore.Err(), IsNil) + c.Assert(zScore.Val(), Equals, float64(1.001)) +} + +func (t *RedisTest) TestZUnionStore(c *C) { + zAdd := t.client.ZAdd("zset1", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset1", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + + zAdd = t.client.ZAdd("zset2", redis.Z{1, "one"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset2", redis.Z{2, "two"}) + c.Assert(zAdd.Err(), IsNil) + zAdd = t.client.ZAdd("zset2", redis.Z{3, "three"}) + c.Assert(zAdd.Err(), IsNil) + + zUnionStore := t.client.ZUnionStore( + "out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2") + c.Assert(zUnionStore.Err(), IsNil) + c.Assert(zUnionStore.Val(), Equals, int64(3)) + + val, err := t.client.ZRangeWithScores("out", 0, -1).Result() + c.Assert(err, IsNil) + c.Assert(val, DeepEquals, []redis.Z{{5, "one"}, {9, "three"}, {10, "two"}}) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestPatternPubSub(c *C) { + pubsub := t.client.PubSub() + defer func() { + c.Assert(pubsub.Close(), IsNil) + }() + + c.Assert(pubsub.PSubscribe("mychannel*"), IsNil) + + pub := t.client.Publish("mychannel1", "hello") + c.Assert(pub.Err(), IsNil) + c.Assert(pub.Val(), Equals, int64(1)) + + c.Assert(pubsub.PUnsubscribe("mychannel*"), IsNil) + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "psubscribe") + c.Assert(subscr.Channel, Equals, "mychannel*") + c.Assert(subscr.Count, Equals, 1) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.PMessage) + c.Assert(subscr.Channel, Equals, "mychannel1") + c.Assert(subscr.Pattern, Equals, "mychannel*") + c.Assert(subscr.Payload, Equals, "hello") + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "punsubscribe") + c.Assert(subscr.Channel, Equals, "mychannel*") + c.Assert(subscr.Count, Equals, 0) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err.(net.Error).Timeout(), Equals, true) + c.Assert(msgi, IsNil) + } +} + +func (t *RedisTest) TestPubSub(c *C) { + pubsub := t.client.PubSub() + defer func() { + c.Assert(pubsub.Close(), IsNil) + }() + + c.Assert(pubsub.Subscribe("mychannel", "mychannel2"), IsNil) + + pub := t.client.Publish("mychannel", "hello") + c.Assert(pub.Err(), IsNil) + c.Assert(pub.Val(), Equals, int64(1)) + + pub = t.client.Publish("mychannel2", "hello2") + c.Assert(pub.Err(), IsNil) + c.Assert(pub.Val(), Equals, int64(1)) + + c.Assert(pubsub.Unsubscribe("mychannel", "mychannel2"), IsNil) + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "subscribe") + c.Assert(subscr.Channel, Equals, "mychannel") + c.Assert(subscr.Count, Equals, 1) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "subscribe") + c.Assert(subscr.Channel, Equals, "mychannel2") + c.Assert(subscr.Count, Equals, 2) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Message) + c.Assert(subscr.Channel, Equals, "mychannel") + c.Assert(subscr.Payload, Equals, "hello") + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + msg := msgi.(*redis.Message) + c.Assert(msg.Channel, Equals, "mychannel2") + c.Assert(msg.Payload, Equals, "hello2") + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "unsubscribe") + c.Assert(subscr.Channel, Equals, "mychannel") + c.Assert(subscr.Count, Equals, 1) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err, IsNil) + subscr := msgi.(*redis.Subscription) + c.Assert(subscr.Kind, Equals, "unsubscribe") + c.Assert(subscr.Channel, Equals, "mychannel2") + c.Assert(subscr.Count, Equals, 0) + } + + { + msgi, err := pubsub.ReceiveTimeout(time.Second) + c.Assert(err.(net.Error).Timeout(), Equals, true) + c.Assert(msgi, IsNil) + } +} + +func (t *RedisTest) TestPubSubChannels(c *C) { + channels, err := t.client.PubSubChannels("mychannel*").Result() + c.Assert(err, IsNil) + c.Assert(channels, HasLen, 0) + c.Assert(channels, Not(IsNil)) + + pubsub := t.client.PubSub() + defer pubsub.Close() + + c.Assert(pubsub.Subscribe("mychannel", "mychannel2"), IsNil) + + channels, err = t.client.PubSubChannels("mychannel*").Result() + c.Assert(err, IsNil) + c.Assert(sortStrings(channels), DeepEquals, []string{"mychannel", "mychannel2"}) + + channels, err = t.client.PubSubChannels("").Result() + c.Assert(err, IsNil) + c.Assert(channels, HasLen, 0) + + channels, err = t.client.PubSubChannels("*").Result() + c.Assert(err, IsNil) + c.Assert(len(channels) >= 2, Equals, true) +} + +func (t *RedisTest) TestPubSubNumSub(c *C) { + pubsub := t.client.PubSub() + defer pubsub.Close() + + c.Assert(pubsub.Subscribe("mychannel", "mychannel2"), IsNil) + + channels, err := t.client.PubSubNumSub("mychannel", "mychannel2", "mychannel3").Result() + c.Assert(err, IsNil) + c.Assert( + channels, + DeepEquals, + []interface{}{"mychannel", int64(1), "mychannel2", int64(1), "mychannel3", int64(0)}, + ) +} + +func (t *RedisTest) TestPubSubNumPat(c *C) { + num, err := t.client.PubSubNumPat().Result() + c.Assert(err, IsNil) + c.Assert(num, Equals, int64(0)) + + pubsub := t.client.PubSub() + defer pubsub.Close() + + c.Assert(pubsub.PSubscribe("mychannel*"), IsNil) + + num, err = t.client.PubSubNumPat().Result() + c.Assert(err, IsNil) + c.Assert(num, Equals, int64(1)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestPipeline(c *C) { + set := t.client.Set("key2", "hello2") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + pipeline := t.client.Pipeline() + defer func() { + c.Assert(pipeline.Close(), IsNil) + }() + + set = pipeline.Set("key1", "hello1") + get := pipeline.Get("key2") + incr := pipeline.Incr("key3") + getNil := pipeline.Get("key4") + + cmds, err := pipeline.Exec() + c.Assert(err, Equals, redis.Nil) + c.Assert(cmds, HasLen, 4) + + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello2") + + c.Assert(incr.Err(), IsNil) + c.Assert(incr.Val(), Equals, int64(1)) + + c.Assert(getNil.Err(), Equals, redis.Nil) + c.Assert(getNil.Val(), Equals, "") +} + +func (t *RedisTest) TestPipelineDiscardQueued(c *C) { + pipeline := t.client.Pipeline() + + pipeline.Get("key") + pipeline.Discard() + cmds, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 0) + + c.Assert(pipeline.Close(), IsNil) +} + +func (t *RedisTest) TestPipelined(c *C) { + var get *redis.StringCmd + cmds, err := t.client.Pipelined(func(pipe *redis.Pipeline) error { + get = pipe.Get("foo") + return nil + }) + c.Assert(err, Equals, redis.Nil) + c.Assert(cmds, HasLen, 1) + c.Assert(cmds[0], Equals, get) + c.Assert(get.Err(), Equals, redis.Nil) + c.Assert(get.Val(), Equals, "") +} + +func (t *RedisTest) TestPipelineErrValNotSet(c *C) { + pipeline := t.client.Pipeline() + defer func() { + c.Assert(pipeline.Close(), IsNil) + }() + + get := pipeline.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "") +} + +func (t *RedisTest) TestPipelineRunQueuedOnEmptyQueue(c *C) { + pipeline := t.client.Pipeline() + defer func() { + c.Assert(pipeline.Close(), IsNil) + }() + + cmds, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 0) +} + +// TODO: make thread safe? +func (t *RedisTest) TestPipelineIncr(c *C) { + const N = 20000 + key := "TestPipelineIncr" + + pipeline := t.client.Pipeline() + + wg := &sync.WaitGroup{} + wg.Add(N) + for i := 0; i < N; i++ { + pipeline.Incr(key) + wg.Done() + } + wg.Wait() + + cmds, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(len(cmds), Equals, 20000) + for _, cmd := range cmds { + if cmd.Err() != nil { + c.Errorf("got %v, expected nil", cmd.Err()) + } + } + + get := t.client.Get(key) + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, strconv.Itoa(N)) + + c.Assert(pipeline.Close(), IsNil) +} + +func (t *RedisTest) TestPipelineEcho(c *C) { + const N = 1000 + + wg := &sync.WaitGroup{} + wg.Add(N) + for i := 0; i < N; i++ { + go func(i int) { + pipeline := t.client.Pipeline() + + msg1 := "echo" + strconv.Itoa(i) + msg2 := "echo" + strconv.Itoa(i+1) + + echo1 := pipeline.Echo(msg1) + echo2 := pipeline.Echo(msg2) + + cmds, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 2) + + c.Assert(echo1.Err(), IsNil) + c.Assert(echo1.Val(), Equals, msg1) + + c.Assert(echo2.Err(), IsNil) + c.Assert(echo2.Val(), Equals, msg2) + + c.Assert(pipeline.Close(), IsNil) + + wg.Done() + }(i) + } + wg.Wait() +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestMultiExec(c *C) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + var ( + set *redis.StatusCmd + get *redis.StringCmd + ) + cmds, err := multi.Exec(func() error { + set = multi.Set("key", "hello") + get = multi.Get("key") + return nil + }) + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 2) + + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello") +} + +func (t *RedisTest) TestMultiExecDiscard(c *C) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + cmds, err := multi.Exec(func() error { + multi.Set("key1", "hello1") + multi.Discard() + multi.Set("key2", "hello2") + return nil + }) + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 1) + + get := t.client.Get("key1") + c.Assert(get.Err(), Equals, redis.Nil) + c.Assert(get.Val(), Equals, "") + + get = t.client.Get("key2") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "hello2") +} + +func (t *RedisTest) TestMultiExecEmpty(c *C) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + cmds, err := multi.Exec(func() error { return nil }) + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 0) + + ping := multi.Ping() + c.Check(ping.Err(), IsNil) + c.Check(ping.Val(), Equals, "PONG") +} + +func (t *RedisTest) TestMultiExecOnEmptyQueue(c *C) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + cmds, err := multi.Exec(func() error { return nil }) + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 0) +} + +func (t *RedisTest) TestMultiExecIncr(c *C) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + cmds, err := multi.Exec(func() error { + for i := int64(0); i < 20000; i++ { + multi.Incr("key") + } + return nil + }) + c.Assert(err, IsNil) + c.Assert(len(cmds), Equals, 20000) + for _, cmd := range cmds { + if cmd.Err() != nil { + c.Errorf("got %v, expected nil", cmd.Err()) + } + } + + get := t.client.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Equals, "20000") +} + +func (t *RedisTest) transactionalIncr(c *C) ([]redis.Cmder, error) { + multi := t.client.Multi() + defer func() { + c.Assert(multi.Close(), IsNil) + }() + + watch := multi.Watch("key") + c.Assert(watch.Err(), IsNil) + c.Assert(watch.Val(), Equals, "OK") + + get := multi.Get("key") + c.Assert(get.Err(), IsNil) + c.Assert(get.Val(), Not(Equals), redis.Nil) + + v, err := strconv.ParseInt(get.Val(), 10, 64) + c.Assert(err, IsNil) + + return multi.Exec(func() error { + multi.Set("key", strconv.FormatInt(v+1, 10)) + return nil + }) +} + +func (t *RedisTest) TestWatchUnwatch(c *C) { + var n = 10000 + if testing.Short() { + n = 1000 + } + + set := t.client.Set("key", "0") + c.Assert(set.Err(), IsNil) + + wg := &sync.WaitGroup{} + for i := 0; i < n; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + cmds, err := t.transactionalIncr(c) + if err == redis.TxFailedErr { + continue + } + c.Assert(err, IsNil) + c.Assert(cmds, HasLen, 1) + c.Assert(cmds[0].Err(), IsNil) + break + } + }() + } + wg.Wait() + + val, err := t.client.Get("key").Int64() + c.Assert(err, IsNil) + c.Assert(val, Equals, int64(n)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestRaceEcho(c *C) { + var n = 10000 + if testing.Short() { + n = 1000 + } + + wg := &sync.WaitGroup{} + wg.Add(n) + for i := 0; i < n; i++ { + go func(i int) { + msg := "echo" + strconv.Itoa(i) + echo := t.client.Echo(msg) + c.Assert(echo.Err(), IsNil) + c.Assert(echo.Val(), Equals, msg) + wg.Done() + }(i) + } + wg.Wait() +} + +func (t *RedisTest) TestRaceIncr(c *C) { + var n = 10000 + if testing.Short() { + n = 1000 + } + + wg := &sync.WaitGroup{} + wg.Add(n) + for i := 0; i < n; i++ { + go func() { + incr := t.client.Incr("TestRaceIncr") + if err := incr.Err(); err != nil { + panic(err) + } + wg.Done() + }() + } + wg.Wait() + + val, err := t.client.Get("TestRaceIncr").Result() + c.Assert(err, IsNil) + c.Assert(val, Equals, strconv.Itoa(n)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdBgRewriteAOF(c *C) { + r := t.client.BgRewriteAOF() + c.Assert(r.Err(), IsNil) + c.Assert(r.Val(), Equals, "Background append only file rewriting started") +} + +func (t *RedisTest) TestCmdBgSave(c *C) { + // workaround for "ERR Can't BGSAVE while AOF log rewriting is in progress" + time.Sleep(time.Second) + + r := t.client.BgSave() + c.Assert(r.Err(), IsNil) + c.Assert(r.Val(), Equals, "Background saving started") +} + +func (t *RedisTest) TestCmdClientKill(c *C) { + r := t.client.ClientKill("1.1.1.1:1111") + c.Assert(r.Err(), ErrorMatches, "ERR No such client") + c.Assert(r.Val(), Equals, "") +} + +func (t *RedisTest) TestCmdConfigGet(c *C) { + r := t.client.ConfigGet("*") + c.Assert(r.Err(), IsNil) + c.Assert(len(r.Val()) > 0, Equals, true) +} + +func (t *RedisTest) TestCmdConfigResetStat(c *C) { + r := t.client.ConfigResetStat() + c.Assert(r.Err(), IsNil) + c.Assert(r.Val(), Equals, "OK") +} + +func (t *RedisTest) TestCmdConfigSet(c *C) { + configGet := t.client.ConfigGet("maxmemory") + c.Assert(configGet.Err(), IsNil) + c.Assert(configGet.Val(), HasLen, 2) + c.Assert(configGet.Val()[0], Equals, "maxmemory") + + configSet := t.client.ConfigSet("maxmemory", configGet.Val()[1].(string)) + c.Assert(configSet.Err(), IsNil) + c.Assert(configSet.Val(), Equals, "OK") +} + +func (t *RedisTest) TestCmdDbSize(c *C) { + dbSize := t.client.DbSize() + c.Assert(dbSize.Err(), IsNil) + c.Assert(dbSize.Val(), Equals, int64(0)) +} + +func (t *RedisTest) TestCmdFlushAll(c *C) { + // TODO +} + +func (t *RedisTest) TestCmdFlushDb(c *C) { + // TODO +} + +func (t *RedisTest) TestCmdInfo(c *C) { + info := t.client.Info() + c.Assert(info.Err(), IsNil) + c.Assert(info.Val(), Not(Equals), "") +} + +func (t *RedisTest) TestCmdLastSave(c *C) { + lastSave := t.client.LastSave() + c.Assert(lastSave.Err(), IsNil) + c.Assert(lastSave.Val(), Not(Equals), 0) +} + +func (t *RedisTest) TestCmdSave(c *C) { + save := t.client.Save() + c.Assert(save.Err(), IsNil) + c.Assert(save.Val(), Equals, "OK") +} + +func (t *RedisTest) TestSlaveOf(c *C) { + slaveOf := t.client.SlaveOf("localhost", "8888") + c.Assert(slaveOf.Err(), IsNil) + c.Assert(slaveOf.Val(), Equals, "OK") + + slaveOf = t.client.SlaveOf("NO", "ONE") + c.Assert(slaveOf.Err(), IsNil) + c.Assert(slaveOf.Val(), Equals, "OK") +} + +func (t *RedisTest) TestTime(c *C) { + time := t.client.Time() + c.Assert(time.Err(), IsNil) + c.Assert(time.Val(), HasLen, 2) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestScriptingEval(c *C) { + eval := t.client.Eval( + "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", + []string{"key1", "key2"}, + []string{"first", "second"}, + ) + c.Assert(eval.Err(), IsNil) + c.Assert(eval.Val(), DeepEquals, []interface{}{"key1", "key2", "first", "second"}) + + eval = t.client.Eval( + "return redis.call('set',KEYS[1],'bar')", + []string{"foo"}, + []string{}, + ) + c.Assert(eval.Err(), IsNil) + c.Assert(eval.Val(), Equals, "OK") + + eval = t.client.Eval("return 10", []string{}, []string{}) + c.Assert(eval.Err(), IsNil) + c.Assert(eval.Val(), Equals, int64(10)) + + eval = t.client.Eval("return {1,2,{3,'Hello World!'}}", []string{}, []string{}) + c.Assert(eval.Err(), IsNil) + // DeepEquals can't compare nested slices. + c.Assert( + fmt.Sprintf("%#v", eval.Val()), + Equals, + `[]interface {}{1, 2, []interface {}{3, "Hello World!"}}`, + ) +} + +func (t *RedisTest) TestScriptingEvalSha(c *C) { + set := t.client.Set("foo", "bar") + c.Assert(set.Err(), IsNil) + c.Assert(set.Val(), Equals, "OK") + + eval := t.client.Eval("return redis.call('get','foo')", nil, nil) + c.Assert(eval.Err(), IsNil) + c.Assert(eval.Val(), Equals, "bar") + + evalSha := t.client.EvalSha("6b1bf486c81ceb7edf3c093f4c48582e38c0e791", nil, nil) + c.Assert(evalSha.Err(), IsNil) + c.Assert(evalSha.Val(), Equals, "bar") + + evalSha = t.client.EvalSha("ffffffffffffffffffffffffffffffffffffffff", nil, nil) + c.Assert(evalSha.Err(), ErrorMatches, "NOSCRIPT No matching script. Please use EVAL.") + c.Assert(evalSha.Val(), Equals, nil) +} + +func (t *RedisTest) TestScriptingScriptExists(c *C) { + scriptLoad := t.client.ScriptLoad("return 1") + c.Assert(scriptLoad.Err(), IsNil) + c.Assert(scriptLoad.Val(), Equals, "e0e1f9fabfc9d4800c877a703b823ac0578ff8db") + + scriptExists := t.client.ScriptExists( + "e0e1f9fabfc9d4800c877a703b823ac0578ff8db", + "ffffffffffffffffffffffffffffffffffffffff", + ) + c.Assert(scriptExists.Err(), IsNil) + c.Assert(scriptExists.Val(), DeepEquals, []bool{true, false}) +} + +func (t *RedisTest) TestScriptingScriptFlush(c *C) { + scriptFlush := t.client.ScriptFlush() + c.Assert(scriptFlush.Err(), IsNil) + c.Assert(scriptFlush.Val(), Equals, "OK") +} + +func (t *RedisTest) TestScriptingScriptKill(c *C) { + scriptKill := t.client.ScriptKill() + c.Assert(scriptKill.Err(), ErrorMatches, ".*No scripts in execution right now.") + c.Assert(scriptKill.Val(), Equals, "") +} + +func (t *RedisTest) TestScriptingScriptLoad(c *C) { + scriptLoad := t.client.ScriptLoad("return redis.call('get','foo')") + c.Assert(scriptLoad.Err(), IsNil) + c.Assert(scriptLoad.Val(), Equals, "6b1bf486c81ceb7edf3c093f4c48582e38c0e791") +} + +func (t *RedisTest) TestScriptingNewScript(c *C) { + s := redis.NewScript("return 1") + run := s.Run(t.client, nil, nil) + c.Assert(run.Err(), IsNil) + c.Assert(run.Val(), Equals, int64(1)) +} + +func (t *RedisTest) TestScriptingEvalAndPipeline(c *C) { + pipeline := t.client.Pipeline() + s := redis.NewScript("return 1") + run := s.Eval(pipeline, nil, nil) + _, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(run.Err(), IsNil) + c.Assert(run.Val(), Equals, int64(1)) +} + +func (t *RedisTest) TestScriptingEvalShaAndPipeline(c *C) { + s := redis.NewScript("return 1") + c.Assert(s.Load(t.client).Err(), IsNil) + + pipeline := t.client.Pipeline() + run := s.Eval(pipeline, nil, nil) + _, err := pipeline.Exec() + c.Assert(err, IsNil) + c.Assert(run.Err(), IsNil) + c.Assert(run.Val(), Equals, int64(1)) +} + +//------------------------------------------------------------------------------ + +func (t *RedisTest) TestCmdDebugObject(c *C) { + { + debug := t.client.DebugObject("foo") + c.Assert(debug.Err(), Not(IsNil)) + c.Assert(debug.Err().Error(), Equals, "ERR no such key") + } + + { + t.client.Set("foo", "bar") + debug := t.client.DebugObject("foo") + c.Assert(debug.Err(), IsNil) + c.Assert(debug.Val(), FitsTypeOf, "") + c.Assert(debug.Val(), Not(Equals), "") + } +} + +//------------------------------------------------------------------------------ + +func BenchmarkRedisPing(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.Ping().Err(); err != nil { + panic(err) + } + } +} + +func BenchmarkRedisSet(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.Set("key", "hello").Err(); err != nil { + panic(err) + } + } +} + +func BenchmarkRedisGetNil(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + if err := client.FlushDb().Err(); err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.Get("key").Err(); err != redis.Nil { + b.Fatal(err) + } + } +} + +func BenchmarkRedisGet(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + if err := client.Set("key", "hello").Err(); err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.Get("key").Err(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkRedisMGet(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + if err := client.MSet("key1", "hello1", "key2", "hello2").Err(); err != nil { + b.Fatal(err) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.MGet("key1", "key2").Err(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkSetExpire(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + b.StartTimer() + + for i := 0; i < b.N; i++ { + if err := client.Set("key", "hello").Err(); err != nil { + b.Fatal(err) + } + if err := client.Expire("key", time.Second).Err(); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkPipeline(b *testing.B) { + b.StopTimer() + client := redis.NewTCPClient(&redis.Options{ + Addr: redisAddr, + }) + b.StartTimer() + + for i := 0; i < b.N; i++ { + _, err := client.Pipelined(func(pipe *redis.Pipeline) error { + pipe.Set("key", "hello") + pipe.Expire("key", time.Second) + return nil + }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/script.go b/Godeps/_workspace/src/gopkg.in/redis.v2/script.go new file mode 100644 index 00000000000..96c35f5149e --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/script.go @@ -0,0 +1,52 @@ +package redis + +import ( + "crypto/sha1" + "encoding/hex" + "io" + "strings" +) + +type scripter interface { + Eval(script string, keys []string, args []string) *Cmd + EvalSha(sha1 string, keys []string, args []string) *Cmd + ScriptExists(scripts ...string) *BoolSliceCmd + ScriptLoad(script string) *StringCmd +} + +type Script struct { + src, hash string +} + +func NewScript(src string) *Script { + h := sha1.New() + io.WriteString(h, src) + return &Script{ + src: src, + hash: hex.EncodeToString(h.Sum(nil)), + } +} + +func (s *Script) Load(c scripter) *StringCmd { + return c.ScriptLoad(s.src) +} + +func (s *Script) Exists(c scripter) *BoolSliceCmd { + return c.ScriptExists(s.src) +} + +func (s *Script) Eval(c scripter, keys []string, args []string) *Cmd { + return c.Eval(s.src, keys, args) +} + +func (s *Script) EvalSha(c scripter, keys []string, args []string) *Cmd { + return c.EvalSha(s.hash, keys, args) +} + +func (s *Script) Run(c *Client, keys []string, args []string) *Cmd { + r := s.EvalSha(c, keys, args) + if err := r.Err(); err != nil && strings.HasPrefix(err.Error(), "NOSCRIPT ") { + return s.Eval(c, keys, args) + } + return r +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel.go b/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel.go new file mode 100644 index 00000000000..d3ffeca9a59 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel.go @@ -0,0 +1,291 @@ +package redis + +import ( + "errors" + "log" + "net" + "strings" + "sync" + "time" +) + +//------------------------------------------------------------------------------ + +type FailoverOptions struct { + MasterName string + SentinelAddrs []string + + Password string + DB int64 + + PoolSize int + + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration +} + +func (opt *FailoverOptions) getPoolSize() int { + if opt.PoolSize == 0 { + return 10 + } + return opt.PoolSize +} + +func (opt *FailoverOptions) getDialTimeout() time.Duration { + if opt.DialTimeout == 0 { + return 5 * time.Second + } + return opt.DialTimeout +} + +func (opt *FailoverOptions) options() *options { + return &options{ + DB: opt.DB, + Password: opt.Password, + + DialTimeout: opt.getDialTimeout(), + ReadTimeout: opt.ReadTimeout, + WriteTimeout: opt.WriteTimeout, + + PoolSize: opt.getPoolSize(), + IdleTimeout: opt.IdleTimeout, + } +} + +func NewFailoverClient(failoverOpt *FailoverOptions) *Client { + opt := failoverOpt.options() + failover := &sentinelFailover{ + masterName: failoverOpt.MasterName, + sentinelAddrs: failoverOpt.SentinelAddrs, + + opt: opt, + } + return &Client{ + baseClient: &baseClient{ + opt: opt, + connPool: failover.Pool(), + }, + } +} + +//------------------------------------------------------------------------------ + +type sentinelClient struct { + *baseClient +} + +func newSentinel(clOpt *Options) *sentinelClient { + opt := clOpt.options() + opt.Password = "" + opt.DB = 0 + dialer := func() (net.Conn, error) { + return net.DialTimeout("tcp", clOpt.Addr, opt.DialTimeout) + } + return &sentinelClient{ + baseClient: &baseClient{ + opt: opt, + connPool: newConnPool(newConnFunc(dialer), opt), + }, + } +} + +func (c *sentinelClient) PubSub() *PubSub { + return &PubSub{ + baseClient: &baseClient{ + opt: c.opt, + connPool: newSingleConnPool(c.connPool, false), + }, + } +} + +func (c *sentinelClient) GetMasterAddrByName(name string) *StringSliceCmd { + cmd := NewStringSliceCmd("SENTINEL", "get-master-addr-by-name", name) + c.Process(cmd) + return cmd +} + +func (c *sentinelClient) Sentinels(name string) *SliceCmd { + cmd := NewSliceCmd("SENTINEL", "sentinels", name) + c.Process(cmd) + return cmd +} + +type sentinelFailover struct { + masterName string + sentinelAddrs []string + + opt *options + + pool pool + poolOnce sync.Once + + lock sync.RWMutex + _sentinel *sentinelClient +} + +func (d *sentinelFailover) dial() (net.Conn, error) { + addr, err := d.MasterAddr() + if err != nil { + return nil, err + } + return net.DialTimeout("tcp", addr, d.opt.DialTimeout) +} + +func (d *sentinelFailover) Pool() pool { + d.poolOnce.Do(func() { + d.pool = newConnPool(newConnFunc(d.dial), d.opt) + }) + return d.pool +} + +func (d *sentinelFailover) MasterAddr() (string, error) { + defer d.lock.Unlock() + d.lock.Lock() + + // Try last working sentinel. + if d._sentinel != nil { + addr, err := d._sentinel.GetMasterAddrByName(d.masterName).Result() + if err != nil { + log.Printf("redis-sentinel: GetMasterAddrByName %q failed: %s", d.masterName, err) + d.resetSentinel() + } else { + addr := net.JoinHostPort(addr[0], addr[1]) + log.Printf("redis-sentinel: %q addr is %s", d.masterName, addr) + return addr, nil + } + } + + for i, sentinelAddr := range d.sentinelAddrs { + sentinel := newSentinel(&Options{ + Addr: sentinelAddr, + + DB: d.opt.DB, + Password: d.opt.Password, + + DialTimeout: d.opt.DialTimeout, + ReadTimeout: d.opt.ReadTimeout, + WriteTimeout: d.opt.WriteTimeout, + + PoolSize: d.opt.PoolSize, + IdleTimeout: d.opt.IdleTimeout, + }) + masterAddr, err := sentinel.GetMasterAddrByName(d.masterName).Result() + if err != nil { + log.Printf("redis-sentinel: GetMasterAddrByName %q failed: %s", d.masterName, err) + sentinel.Close() + continue + } + + // Push working sentinel to the top. + d.sentinelAddrs[0], d.sentinelAddrs[i] = d.sentinelAddrs[i], d.sentinelAddrs[0] + + d.setSentinel(sentinel) + addr := net.JoinHostPort(masterAddr[0], masterAddr[1]) + log.Printf("redis-sentinel: %q addr is %s", d.masterName, addr) + return addr, nil + } + + return "", errors.New("redis: all sentinels are unreachable") +} + +func (d *sentinelFailover) setSentinel(sentinel *sentinelClient) { + d.discoverSentinels(sentinel) + d._sentinel = sentinel + go d.listen() +} + +func (d *sentinelFailover) discoverSentinels(sentinel *sentinelClient) { + sentinels, err := sentinel.Sentinels(d.masterName).Result() + if err != nil { + log.Printf("redis-sentinel: Sentinels %q failed: %s", d.masterName, err) + return + } + for _, sentinel := range sentinels { + vals := sentinel.([]interface{}) + for i := 0; i < len(vals); i += 2 { + key := vals[i].(string) + if key == "name" { + sentinelAddr := vals[i+1].(string) + if !contains(d.sentinelAddrs, sentinelAddr) { + log.Printf( + "redis-sentinel: discovered new %q sentinel: %s", + d.masterName, sentinelAddr, + ) + d.sentinelAddrs = append(d.sentinelAddrs, sentinelAddr) + } + } + } + } +} + +func (d *sentinelFailover) listen() { + var pubsub *PubSub + for { + if pubsub == nil { + pubsub = d._sentinel.PubSub() + if err := pubsub.Subscribe("+switch-master"); err != nil { + log.Printf("redis-sentinel: Subscribe failed: %s", err) + d.lock.Lock() + d.resetSentinel() + d.lock.Unlock() + return + } + } + + msgIface, err := pubsub.Receive() + if err != nil { + log.Printf("redis-sentinel: Receive failed: %s", err) + pubsub.Close() + return + } + + switch msg := msgIface.(type) { + case *Message: + switch msg.Channel { + case "+switch-master": + parts := strings.Split(msg.Payload, " ") + if parts[0] != d.masterName { + log.Printf("redis-sentinel: ignore new %s addr", parts[0]) + continue + } + addr := net.JoinHostPort(parts[3], parts[4]) + log.Printf( + "redis-sentinel: new %q addr is %s", + d.masterName, addr, + ) + d.pool.Filter(func(cn *conn) bool { + if cn.RemoteAddr().String() != addr { + log.Printf( + "redis-sentinel: closing connection to old master %s", + cn.RemoteAddr(), + ) + return false + } + return true + }) + default: + log.Printf("redis-sentinel: unsupported message: %s", msg) + } + case *Subscription: + // Ignore. + default: + log.Printf("redis-sentinel: unsupported message: %s", msgIface) + } + } +} + +func (d *sentinelFailover) resetSentinel() { + d._sentinel.Close() + d._sentinel = nil +} + +func contains(slice []string, str string) bool { + for _, s := range slice { + if s == str { + return true + } + } + return false +} diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel_test.go b/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel_test.go new file mode 100644 index 00000000000..ede59bd51e2 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/sentinel_test.go @@ -0,0 +1,185 @@ +package redis_test + +import ( + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" + "text/template" + "time" + + "gopkg.in/redis.v2" +) + +func startRedis(port string) (*exec.Cmd, error) { + cmd := exec.Command("redis-server", "--port", port) + if false { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + if err := cmd.Start(); err != nil { + return nil, err + } + return cmd, nil +} + +func startRedisSlave(port, slave string) (*exec.Cmd, error) { + cmd := exec.Command("redis-server", "--port", port, "--slaveof", "127.0.0.1", slave) + if false { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + if err := cmd.Start(); err != nil { + return nil, err + } + return cmd, nil +} + +func startRedisSentinel(port, masterName, masterPort string) (*exec.Cmd, error) { + dir, err := ioutil.TempDir("", "sentinel") + if err != nil { + return nil, err + } + + sentinelConfFilepath := filepath.Join(dir, "sentinel.conf") + tpl, err := template.New("sentinel.conf").Parse(sentinelConf) + if err != nil { + return nil, err + } + + data := struct { + Port string + MasterName string + MasterPort string + }{ + Port: port, + MasterName: masterName, + MasterPort: masterPort, + } + if err := writeTemplateToFile(sentinelConfFilepath, tpl, data); err != nil { + return nil, err + } + + cmd := exec.Command("redis-server", sentinelConfFilepath, "--sentinel") + if true { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + if err := cmd.Start(); err != nil { + return nil, err + } + + return cmd, nil +} + +func writeTemplateToFile(path string, t *template.Template, data interface{}) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return t.Execute(f, data) +} + +func TestSentinel(t *testing.T) { + masterName := "mymaster" + masterPort := "8123" + slavePort := "8124" + sentinelPort := "8125" + + masterCmd, err := startRedis(masterPort) + if err != nil { + t.Fatal(err) + } + defer masterCmd.Process.Kill() + + // Wait for master to start. + time.Sleep(200 * time.Millisecond) + + master := redis.NewTCPClient(&redis.Options{ + Addr: ":" + masterPort, + }) + if err := master.Ping().Err(); err != nil { + t.Fatal(err) + } + + slaveCmd, err := startRedisSlave(slavePort, masterPort) + if err != nil { + t.Fatal(err) + } + defer slaveCmd.Process.Kill() + + // Wait for slave to start. + time.Sleep(200 * time.Millisecond) + + slave := redis.NewTCPClient(&redis.Options{ + Addr: ":" + slavePort, + }) + if err := slave.Ping().Err(); err != nil { + t.Fatal(err) + } + + sentinelCmd, err := startRedisSentinel(sentinelPort, masterName, masterPort) + if err != nil { + t.Fatal(err) + } + defer sentinelCmd.Process.Kill() + + // Wait for sentinel to start. + time.Sleep(200 * time.Millisecond) + + sentinel := redis.NewTCPClient(&redis.Options{ + Addr: ":" + sentinelPort, + }) + if err := sentinel.Ping().Err(); err != nil { + t.Fatal(err) + } + defer sentinel.Shutdown() + + client := redis.NewFailoverClient(&redis.FailoverOptions{ + MasterName: masterName, + SentinelAddrs: []string{":" + sentinelPort}, + }) + + if err := client.Set("foo", "master").Err(); err != nil { + t.Fatal(err) + } + + val, err := master.Get("foo").Result() + if err != nil { + t.Fatal(err) + } + if val != "master" { + t.Fatalf(`got %q, expected "master"`, val) + } + + // Kill Redis master. + if err := masterCmd.Process.Kill(); err != nil { + t.Fatal(err) + } + if err := master.Ping().Err(); err == nil { + t.Fatalf("master was not killed") + } + + // Wait for Redis sentinel to elect new master. + time.Sleep(5 * time.Second) + + // Check that client picked up new master. + val, err = client.Get("foo").Result() + if err != nil { + t.Fatal(err) + } + if val != "master" { + t.Fatalf(`got %q, expected "master"`, val) + } +} + +var sentinelConf = ` +port {{ .Port }} + +sentinel monitor {{ .MasterName }} 127.0.0.1 {{ .MasterPort }} 1 +sentinel down-after-milliseconds {{ .MasterName }} 1000 +sentinel failover-timeout {{ .MasterName }} 2000 +sentinel parallel-syncs {{ .MasterName }} 1 +` diff --git a/Godeps/_workspace/src/gopkg.in/redis.v2/testdata/sentinel.conf b/Godeps/_workspace/src/gopkg.in/redis.v2/testdata/sentinel.conf new file mode 100644 index 00000000000..3da90b380f7 --- /dev/null +++ b/Godeps/_workspace/src/gopkg.in/redis.v2/testdata/sentinel.conf @@ -0,0 +1,6 @@ +port 26379 + +sentinel monitor master 127.0.0.1 6379 1 +sentinel down-after-milliseconds master 2000 +sentinel failover-timeout master 5000 +sentinel parallel-syncs master 4 diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/.gitignore b/Godeps/_workspace/src/gopkgs.com/pool.v1/.gitignore new file mode 100644 index 00000000000..836562412fe --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/LICENSE b/Godeps/_workspace/src/gopkgs.com/pool.v1/LICENSE new file mode 100644 index 00000000000..ad410e11302 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/README.md b/Godeps/_workspace/src/gopkgs.com/pool.v1/README.md new file mode 100644 index 00000000000..f9c561dc067 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/README.md @@ -0,0 +1,13 @@ +pool +==== + +sync.Pool compatibility layer for for Go - falls back to a channel based pool in Go < 1.3 + + +Please, use the following import path to ensure a stable API: + +```go + import "gopkgs.com/pool.v1" +``` + +View other available versions, documentation and examples at http://gopkgs.com/pool diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/doc.go b/Godeps/_workspace/src/gopkgs.com/pool.v1/doc.go new file mode 100644 index 00000000000..7546db7ac21 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/doc.go @@ -0,0 +1,3 @@ +// Package pool provides a sync.Pool compatibility layer, which +// falls back to a channel based pool on Go < 1.3. +package pool diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/example_test.go b/Godeps/_workspace/src/gopkgs.com/pool.v1/example_test.go new file mode 100644 index 00000000000..dff9d348b3f --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/example_test.go @@ -0,0 +1,23 @@ +package pool_test + +import ( + "fmt" + + "gopkgs.com/pool.v1" +) + +func ExamplePool() { + p := pool.New(0) + p.Put("Hello") + fmt.Println(p.Get()) + // OutPut: Hello +} + +func ExamplePoolNew() { + p := pool.New(0) + p.New = func() interface{} { + return "World!" + } + fmt.Println(p.Get()) + // OutPut: World! +} diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/gopkgs.go b/Godeps/_workspace/src/gopkgs.com/pool.v1/gopkgs.go new file mode 100644 index 00000000000..394a97806d6 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/gopkgs.go @@ -0,0 +1,24 @@ +package pool + +import ( + "fmt" + "reflect" +) + +// gopkgs.go: v1 + +// NOTE: This file is autogenerated by gopkgs.com. +const ( + goPkgsSrcPath = "github.com/rainycape/pool" + goPkgsName = "pool" + goPkgsErrFmt = "invalid import path %s - please use gopkgs.com/%s.v1 or see http://gopkgs.com/%s" +) + +type goPkgsCheck struct{} + +func init() { + typ := reflect.TypeOf(goPkgsCheck{}) + if typ.PkgPath() == goPkgsSrcPath { + panic(fmt.Errorf(goPkgsErrFmt, typ.PkgPath(), goPkgsName, goPkgsName)) + } +} diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/pool.go b/Godeps/_workspace/src/gopkgs.com/pool.v1/pool.go new file mode 100644 index 00000000000..269a2afd919 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/pool.go @@ -0,0 +1,37 @@ +// +build go1.3,!appengine + +package pool + +import ( + "sync" +) + +// Pool is a thin compatibility type to allow Go +// libraries to use the new sync.Pool in Go 1.3, +// while remaining compatible with lower Go versions. +// For more information, see the sync.Pool type. +type Pool sync.Pool + +// New returns a new Pool. The size argument is +// ignored on Go >= 1.3. In Go < 1.3, if size is +// zero, it's set to runtime.GOMAXPROCS(0) * 2. +func New(size int) *Pool { + return &Pool{} +} + +// Get returns an arbitrary previously Put value, removing +// it from the pool, or nil if there are no such values. Note +// that callers should not assume anything about the Get return +// value, since the runtime might decide to collect the elements +// from the pool at any time. +// +// If there are no elements to return and the New() field is non-nil, +// Get returns the result of calling it. +func (p *Pool) Get() interface{} { + return (*sync.Pool)(p).Get() +} + +// Put adds x to the pool. +func (p *Pool) Put(x interface{}) { + (*sync.Pool)(p).Put(x) +} diff --git a/Godeps/_workspace/src/gopkgs.com/pool.v1/pool_go1.2.go b/Godeps/_workspace/src/gopkgs.com/pool.v1/pool_go1.2.go new file mode 100644 index 00000000000..11c4e490a30 --- /dev/null +++ b/Godeps/_workspace/src/gopkgs.com/pool.v1/pool_go1.2.go @@ -0,0 +1,57 @@ +// +build !go1.3 appengine + +package pool + +import ( + "runtime" +) + +// Pool is a thin compatibility type to allow Go +// libraries to use the new sync.Pool in Go 1.3, +// while remaining compatible with lower Go versions. +// For more information, see the sync.Pool type. +type Pool struct { + ch chan interface{} + // New specifies a function to generate + // a new value, when Get would otherwise + // return nil. + New func() interface{} +} + +// New returns a new Pool. The size argument is +// ignored on Go >= 1.3. In Go < 1.3, if size is +// zero, it's set to runtime.GOMAXPROCS(0) * 2. +func New(size int) *Pool { + if size == 0 { + size = runtime.GOMAXPROCS(0) * 2 + } + return &Pool{ch: make(chan interface{}, size)} +} + +// Get returns an arbitrary previously Put value, removing +// it from the pool, or nil if there are no such values. Note +// that callers should not assume anything about the Get return +// value, since the runtime might decide to collect the elements +// from the pool at any time. +// +// If there are no elements to return and the New() field is non-nil, +// Get returns the result of calling it. +func (p *Pool) Get() interface{} { + select { + case x := <-p.ch: + return x + default: + } + if p.New != nil { + return p.New() + } + return nil +} + +// Put adds x to the pool. +func (p *Pool) Put(x interface{}) { + select { + case p.ch <- x: + default: + } +} diff --git a/Gruntfile.js b/Gruntfile.js index b99c5ed9c1e..43cb1f09bd1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,16 +1,21 @@ /* jshint node:true */ 'use strict'; module.exports = function (grunt) { - + var os = require('os'); var config = { pkg: grunt.file.readJSON('package.json'), baseDir: '.', - srcDir: 'src', + srcDir: 'public', destDir: 'dist', tempDir: 'tmp', - arch: grunt.option('arch') || 'x86_64', + arch: os.arch(), + platform: process.platform.replace('win32', 'windows'), }; + if (process.platform.match(/^win/)) { + config.arch = process.env.hasOwnProperty('ProgramFiles(x86)') ? 'x64' : 'x86'; + } + config.pkg.version = grunt.option('pkgVer') || config.pkg.version; // load plugins @@ -33,7 +38,6 @@ module.exports = function (grunt) { // Merge that object with what with whatever we have here loadConfig(config,'./tasks/options/'); - // pass the config to grunt grunt.initConfig(config); }; diff --git a/README.md b/README.md index a55874632dd..bd178eddbdd 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,98 @@ -[Grafana](http://grafana.org) [![Build Status](https://api.travis-ci.org/grafana/grafana.svg)](https://travis-ci.org/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/grafana/grafana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[Grafana](http://grafana.org) [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) [![Coverage Status](https://coveralls.io/repos/grafana/grafana/badge.png)](https://coveralls.io/r/grafana/grafana) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/grafana/grafana?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ================ [Website](http://grafana.org) | -[Twitter](http://twitter.com/grafana) | -[IRC](http://webchat.freenode.net/?channels=grafana) | +[Twitter](https://twitter.com/grafana) | +[IRC](https://webchat.freenode.net/?channels=grafana) | [Email](mailto:contact@grafana.org) -Grafana is An open source, feature rich metrics dashboard and graph editor for +Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, InfluxDB & OpenTSDB. ![](http://grafana.org/assets/img/start_page_bg.png) -# Grafana 2.0 Alpha branch [![Circle CI](https://circleci.com/gh/grafana/grafana.svg?style=svg)](https://circleci.com/gh/grafana/grafana) +## Grafana 2.0 +The develop branch has now been merged with master. For 1.9 users this is a big change as Grafana is no longer +a standalone frontend only web application. Grafana 2.0 comes with a backend. +- [Install instructions](http://docs.grafana.org/v2.0/installation/) +- [Migrating from 1.x to 2.x](http://docs.grafana.org/v2.0/installation/migrating_to2/) +- [What's New in Grafana 2.0](http://docs.grafana.org/v2.0/guides/whats-new-in-v2/) -Grafana 2.0 comes with a backend written in Go. It is not ready for production use yet as there is still a lot of small -issues to fix and polish missing. But feedback on what is done and bug reports would be greatly appreciated. +## Features +### Graphite Target Editor +- Graphite target expression parser +- Feature rich query composer +- Quickly add and edit functions & parameters +- Templated queries +- [See it in action](http://docs.grafana.org/datasources/graphite/) -## Try it out with docker -``` -docker run -i -p 3000:3000 grafana/grafana:develop -``` -The default admin user is admin/admin. +### Graphing +- Fast rendering, even over large timespans +- Click and drag to zoom +- Multiple Y-axis, logarithmic scales +- Bars, Lines, Points +- Smart Y-axis formating +- Series toggles & color selector +- Legend values, and formatting options +- Grid thresholds, axis labels +- [Annotations](http://docs.grafana.org/reference/annotations/) +- Any panel can be rendered to PNG (server side using phantomjs) -## building and running +### Dashboards +- Create, edit, save & search dashboards +- Change column spans and row heights +- Drag and drop panels to rearrange +- [Templating](http://docs.grafana.org/reference/templating/) +- [Scripted dashboards](http://docs.grafana.org/reference/scripting/) +- [Dashboard playlists](http://docs.grafana.org/reference/playlist/) +- [Time range controls](http://docs.grafana.org/reference/timerange/) +- [Share snapshots publicly](http://docs.grafana.org/v2.0/reference/sharing/) + +### InfluxDB +- Use InfluxDB as a metric data source, annotation source +- Query editor with series and column typeahead, easy group by and function selection + +### OpenTSDB +- Use as metric data source +- Query editor with metric name typeahead and tag filtering + +## Requirements +There are no dependencies except an external time series data store. For dashboards and user accounts Grafana can use an embedded +database (sqlite3) or you can use an external SQL data base like MySQL or Postgres. + +## Installation +Head to [grafana.org](http://docs.grafana.org/installation/) and [download](http://grafana.org/download/) +the latest release. + +If you have any problems please read the [troubleshooting guide](http://docs.grafana.org/installation/troubleshooting/). + +## Documentation & Support +Be sure to read the [getting started guide](http://docs.grafana.org/guides/gettingstarted/) and the other feature guides. + +## Run from master +If you want to build a package your self, or contribute. Here is a guide for how to do that. You can always find +the latest master builds [here](http://grafana.org/download/builds) + +### Dependencies + +- Go 1.4 +- NodeJS + +### Get Code ``` go get github.com/grafana/grafana ``` -The above will give an error saying there is no go code. That is because the new backend parts are in the develop branch. -Building +### Building the backend ``` cd $GOPATH/src/github.com/grafana/grafana -git checkout -t origin/develop go run build.go setup (only needed once to install godep) godep restore (will pull down all golang lib dependecies in your current GOPATH) go build . ``` +### Building frontend assets + To build less to css for the frontend you will need a recent version of of node (v0.12.0), npm (v2.5.0) and grunt (v0.4.5). Run the following: @@ -46,100 +102,31 @@ npm install -g grunt-cli grunt ``` +### Recompile backend on source change To rebuild on source change (requires that you executed godep restore) ``` go get github.com/Unknwon/bra bra run ``` -Running +### Running ``` -./bin/grafana web +./grafana ``` + Open grafana in your browser (default http://localhost:3000) and login with admin user (default user/pass = admin/admin). -### Config -Create a grafana.custom.ini in the conf directory to override default configuration options. +### Dev config + +Create a custom.ini in the conf directory to override default configuration options. You only need to add the options you want to override. Config files are applied in the order of: 1. grafana.ini -2. grafana.dev.ini (if found) -3. grafana.custom.ini +2. dev.ini (if found) +3. custom.ini -### Docs -There is no docs for the configuration and new UI views, or the account / user model yet. But a quick note -is that Grafana 2.0 has a multi tenant account & user model where Dashboards, data sources, api keys, etc are -tied to an account and not to a specific user. Users are coupled to accounts via an account user role (Admin, Editor, Viewer). -The default configuration is set to a single account mode to make this user & account model easier to handle in a single account setup. -User sign ups are automatically added to a main account with the Editor role (this can be overriden in the config file). The default -grafana admin user that is created on first startup also creates the main account. - - -## Features -### Graphite Target Editor -- Graphite target expression parser -- Feature rich query composer -- Quickly add and edit functions & parameters -- Templated queries -- [See it in action](http://grafana.org/docs/features/graphite) - -### Graphing -- Fast rendering, even over large timespans. -- Click and drag to zoom. -- Multiple Y-axis. -- Bars, Lines, Points. -- Smart Y-axis formating -- Series toggles & color selector -- Legend values, and formating options -- Grid thresholds, axis labels -- [Annotations](http://grafana.org/docs/features/annotations) - -### Dashboards -- Create, edit, save & search dashboards -- Change column spans and row heights -- Drag and drop panels to rearrange -- Use InfluxDB or Elasticsearch as dashboard storage -- Import & export dashboard (json file) -- Import dashboard from Graphite -- Templating -- [Scripted dashboards](http://grafana.org/docs/features/scripted_dashboards) -- [Dashboard playlists](http://grafana.org/docs/features/playlist) -- [Time range controls](http://grafana.org/docs/features/time_range) - -### InfluxDB -- Use InfluxDB as a metric data source, annotation source and for dashboard storage -- Query editor with series and column typeahead, easy group by and function selection - -### OpenTSDB -- Use as metric data source -- Query editor with metric name typeahead and tag filtering - -## Requirements -There are no dependencies, Grafana is a client side application that runs in your browser. It only needs a time series store where it can fetch metrics. If you use InfluxDB Grafana can use it to store dashboards. If you use Graphite or OpenTSDB you can use Elasticsearch to store dashboards or just use json files stored on disk. - -## Installation -Head to [grafana.org](http://grafana.org) and [download](http://grafana.org/download/) -the latest release. - -Then follow the quick [setup & config guide](http://grafana.org/docs/). If you have any problems please -read the [troubleshooting guide](http://grafana.org/docs/troubleshooting). - -## Documentation & Support -Be sure to read the [getting started guide](http://grafana.org/docs/features/intro) and the other -feature guides. - -## Run from master -Grafana uses nodejs and grunt for asset management (css & javascript), unit test runner and javascript syntax verification. -- clone repository -- install nodejs -- npm install (in project root) -- npm install -g grunt-cli -- grunt (runt default task that will generate css files) -- grunt build (creates optimized & minified release) -- grunt release (same as grunt build but will also create tar & zip package) -- grunt test (executes jshint and unit tests) - -## Contribute +## Create a pull requests +Before or after your create a pull requests, sign the [contributor license aggrement](/docs/contributing/cla.html).## Contribute If you have any idea for an improvement or found a bug do not hesitate to open an issue. And if you have time clone this repo and submit a pull request and help me make Grafana the kickass metrics & devops dashboard we all dream about! diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..993f8f2882c --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +version: "{build}" + +os: Windows Server 2012 R2 + +clone_folder: c:\gopath\src\github.com\grafana\grafana + +environment: + nodejs_version: "0.12.2" + GOPATH: c:\gopath + +install: + # install nodejs and npm + - ps: Install-Product node $env:nodejs_version + - npm install + - npm install -g grunt-cli + # install gcc (needed for sqlite3) + - choco install -y mingw + - set PATH=C:\tools\mingw64\bin;%PATH% + - echo %PATH% + - echo %GOPATH% + - go version + - go env + - go run build.go setup + +build_script: + - go run build.go build + - grunt release + +artifacts: + - path: dist/* + name: binzip diff --git a/build.go b/build.go index f7d77129d92..d14b1458586 100644 --- a/build.go +++ b/build.go @@ -22,16 +22,16 @@ import ( ) var ( - versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`) - goarch string - goos string - version string = "v1" - race bool - workingDir string - - installRoot = "/opt/grafana" - configRoot = "/etc/grafana" - grafanaLogDir = "/var/log/grafana" + versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`) + goarch string + goos string + version string = "v1" + // deb & rpm does not support semver so have to handle their version a little differently + linuxPackageVersion string = "v1" + linuxPackageIteration string = "" + race bool + workingDir string + serverBinaryName string = "grafana-server" ) const minGoVersion = 1.3 @@ -43,7 +43,7 @@ func main() { ensureGoPath() readVersionFromPackageJson() - log.Printf("Version: %s\n", version) + log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration) flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH") flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS") @@ -73,8 +73,8 @@ func main() { case "package": //verifyGitRepoIsClean() - grunt("release", "--pkgVer="+version) - createRpmAndDeb() + grunt("release") + createLinuxPackages() case "latest": makeLatestDistCopies() @@ -90,8 +90,8 @@ func main() { func makeLatestDistCopies() { runError("cp", "dist/grafana_"+version+"_amd64.deb", "dist/grafana_latest_amd64.deb") - runError("cp", "dist/grafana-"+strings.Replace(version, "-", "_", 5)+"-1.x86_64.rpm", "dist/grafana-latest-1.x84_64.rpm") - runError("cp", "dist/grafana-"+version+".x86_64.tar.gz", "dist/grafana-latest.x84_64.tar.gz") + runError("cp", "dist/grafana-"+strings.Replace(version, "-", "_", 5)+"-1.x86_64.rpm", "dist/grafana-latest-1.x86_64.rpm") + runError("cp", "dist/grafana-"+version+".linux-x64.tar.gz", "dist/grafana-latest.linux-x64.tar.gz") } func readVersionFromPackageJson() { @@ -110,25 +110,102 @@ func readVersionFromPackageJson() { } version = jsonObj["version"].(string) + linuxPackageVersion = version + linuxPackageIteration = "" + + // handle pre version stuff (deb / rpm does not support semver) + parts := strings.Split(version, "-") + + if len(parts) > 1 { + linuxPackageVersion = parts[0] + linuxPackageIteration = parts[1] + } } -func createRpmAndDeb() { +type linuxPackageOptions struct { + packageType string + homeDir string + binPath string + configDir string + configFilePath string + etcDefaultPath string + etcDefaultFilePath string + initdScriptFilePath string + systemdServiceFilePath string + + postinstSrc string + initdScriptSrc string + defaultFileSrc string + systemdFileSrc string + + depends []string +} + +func createLinuxPackages() { + createPackage(linuxPackageOptions{ + packageType: "deb", + homeDir: "/usr/share/grafana", + binPath: "/usr/sbin/grafana-server", + configDir: "/etc/grafana", + configFilePath: "/etc/grafana/grafana.ini", + etcDefaultPath: "/etc/default", + etcDefaultFilePath: "/etc/default/grafana-server", + initdScriptFilePath: "/etc/init.d/grafana-server", + systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service", + + postinstSrc: "packaging/deb/control/postinst", + initdScriptSrc: "packaging/deb/init.d/grafana-server", + defaultFileSrc: "packaging/deb/default/grafana-server", + systemdFileSrc: "packaging/deb/systemd/grafana-server.service", + + depends: []string{"adduser", "libfontconfig"}, + }) + + createPackage(linuxPackageOptions{ + packageType: "rpm", + homeDir: "/usr/share/grafana", + binPath: "/usr/sbin/grafana-server", + configDir: "/etc/grafana", + configFilePath: "/etc/grafana/grafana.ini", + etcDefaultPath: "/etc/sysconfig", + etcDefaultFilePath: "/etc/sysconfig/grafana-server", + initdScriptFilePath: "/etc/init.d/grafana-server", + systemdServiceFilePath: "/usr/lib/systemd/system/grafana-server.service", + + postinstSrc: "packaging/rpm/control/postinst", + initdScriptSrc: "packaging/rpm/init.d/grafana-server", + defaultFileSrc: "packaging/rpm/sysconfig/grafana-server", + systemdFileSrc: "packaging/rpm/systemd/grafana-server.service", + + depends: []string{"initscripts", "fontconfig"}, + }) +} + +func createPackage(options linuxPackageOptions) { packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack") - postInstallScriptPath, _ := ioutil.TempFile("", "postinstall") - versionFolder := filepath.Join(packageRoot, installRoot, "versions", version) - configDir := filepath.Join(packageRoot, configRoot) + // create directories + runPrint("mkdir", "-p", filepath.Join(packageRoot, options.homeDir)) + runPrint("mkdir", "-p", filepath.Join(packageRoot, options.configDir)) + runPrint("mkdir", "-p", filepath.Join(packageRoot, "/etc/init.d")) + runPrint("mkdir", "-p", filepath.Join(packageRoot, options.etcDefaultPath)) + runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/lib/systemd/system")) + runPrint("mkdir", "-p", filepath.Join(packageRoot, "/usr/sbin")) - runError("mkdir", "-p", versionFolder) - runError("mkdir", "-p", configDir) - - // copy sample ini file to /etc/opt/grafana - configFile := filepath.Join(configDir, "grafana.ini") - runError("cp", "conf/sample.ini", configFile) + // copy binary + runPrint("cp", "-p", filepath.Join(workingDir, "tmp/bin/"+serverBinaryName), filepath.Join(packageRoot, options.binPath)) + // copy init.d script + runPrint("cp", "-p", options.initdScriptSrc, filepath.Join(packageRoot, options.initdScriptFilePath)) + // copy environment var file + runPrint("cp", "-p", options.defaultFileSrc, filepath.Join(packageRoot, options.etcDefaultFilePath)) + // copy systemd file + runPrint("cp", "-p", options.systemdFileSrc, filepath.Join(packageRoot, options.systemdServiceFilePath)) // copy release files - runError("cp", "-a", filepath.Join(workingDir, "tmp")+"/.", versionFolder) - - GeneratePostInstallScript(postInstallScriptPath.Name()) + runPrint("cp", "-a", filepath.Join(workingDir, "tmp")+"/.", filepath.Join(packageRoot, options.homeDir)) + // remove bin path + runPrint("rm", "-rf", filepath.Join(packageRoot, options.homeDir, "bin")) + // copy sample ini file to /etc/opt/grafana + runPrint("cp", "conf/sample.ini", filepath.Join(packageRoot, options.configFilePath)) args := []string{ "-s", "dir", @@ -138,50 +215,29 @@ func createRpmAndDeb() { "--url", "http://grafana.org", "--license", "Apache 2.0", "--maintainer", "contact@grafana.org", - "--config-files", filepath.Join(configRoot, "grafana.ini"), - "--after-install", postInstallScriptPath.Name(), + "--config-files", options.configFilePath, + "--config-files", options.initdScriptFilePath, + "--config-files", options.etcDefaultFilePath, + "--config-files", options.systemdServiceFilePath, + "--after-install", options.postinstSrc, "--name", "grafana", - "--version", version, + "--version", linuxPackageVersion, "-p", "./dist", - ".", } - fmt.Println("Creating debian package") - runPrint("fpm", append([]string{"-t", "deb"}, args...)...) + if linuxPackageIteration != "" { + args = append(args, "--iteration", linuxPackageIteration) + } - fmt.Println("Creating redhat/centos package") - runPrint("fpm", append([]string{"-t", "rpm"}, args...)...) -} + // add dependenciesj + for _, dep := range options.depends { + args = append(args, "--depends", dep) + } -func GeneratePostInstallScript(path string) { - content := ` -rm -f $INSTALL_ROOT_DIR/current -ln -s $INSTALL_ROOT_DIR/versions/$VERSION/ $INSTALL_ROOT_DIR/current + args = append(args, ".") -if [ ! -L /etc/init.d/grafana ]; then - ln -sfn $INSTALL_ROOT_DIR/current/scripts/init.sh /etc/init.d/grafana -fi - -chmod +x /etc/init.d/grafana -if which update-rc.d > /dev/null 2>&1 ; then - update-rc.d -f grafana remove - update-rc.d grafana defaults -else - chkconfig --add grafana -fi - -if ! id grafana >/dev/null 2>&1; then - useradd --system -U -M grafana -fi -chown -R -L grafana:grafana $INSTALL_ROOT_DIR -chmod -R a+rX $INSTALL_ROOT_DIR -mkdir -p $GRAFANA_LOG_DIR -chown -R -L grafana:grafana $GRAFANA_LOG_DIR -` - content = strings.Replace(content, "$INSTALL_ROOT_DIR", installRoot, -1) - content = strings.Replace(content, "$VERSION", version, -1) - content = strings.Replace(content, "$GRAFANA_LOG_DIR", grafanaLogDir, -1) - ioutil.WriteFile(path, []byte(content), 0644) + fmt.Println("Creating package: ", options.packageType) + runPrint("fpm", append([]string{"-t", options.packageType}, args...)...) } func verifyGitRepoIsClean() { @@ -220,6 +276,7 @@ func grunt(params ...string) { func setup() { runPrint("go", "get", "-v", "github.com/tools/godep") + runPrint("go", "get", "-v", "github.com/blang/semver") runPrint("go", "get", "-v", "github.com/mattn/go-sqlite3") runPrint("go", "install", "-v", "github.com/mattn/go-sqlite3") } @@ -230,7 +287,7 @@ func test(pkg string) { } func build(pkg string, tags []string) { - binary := "./bin/grafana" + binary := "./bin/" + serverBinaryName if goos == "windows" { binary += ".exe" } diff --git a/conf/defaults.ini b/conf/defaults.ini index 5318e89b2ad..6bb3fb80857 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1,87 +1,140 @@ -app_name = Grafana +##################### Grafana Configuration Defaults ##################### +# +# Do not modify this file in grafana installs +# + app_mode = production +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +# +data = data +# +# Directory where grafana can store logs +# +logs = data/log + +#################################### Server #################################### [server] -; protocol (http or https) +# Protocol (http or https) protocol = http -; the ip address to bind to, empty will bind to all interfaces + +# The ip address to bind to, empty will bind to all interfaces http_addr = -; the http port to use + +# The http port to use http_port = 3000 -; The public facing domain name used to access grafana from a browser + +# The public facing domain name used to access grafana from a browser domain = localhost -; the full public facing url + +# The full public facing url root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Log web requests router_logging = false -; the path relative to the binary where the static (html/js/css) files are placed + +# the path relative working path static_root_path = public + +# enable gzip enable_gzip = false -; if https protocol + +# https certs & key file cert_file = cert_key = +#################################### Database #################################### [database] -; Either "mysql", "postgres" or "sqlite3", it's your choice +# Either "mysql", "postgres" or "sqlite3", it's your choice type = sqlite3 host = 127.0.0.1:3306 name = grafana user = root password = -; For "postgres" only, either "disable", "require" or "verify-full" + +# For "postgres" only, either "disable", "require" or "verify-full" ssl_mode = disable -; For "sqlite3" only -path = data/grafana.db +# For "sqlite3" only, path relative to data_path setting +path = grafana.db + +#################################### Session #################################### [session] -; Either "memory", "file", "redis", "mysql", default is "memory" +# Either "memory", "file", "redis", "mysql", "postgresql", default is "file" provider = file -; Provider config options -; memory: not have any config yet -; file: session file path, e.g. `data/sessions` -; redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,grafana` -; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` -provider_config = data/sessions -; Session cookie name -cookie_name = grafana_sess -; If you use session in https only, default is false -cookie_secure = false -; Session life time, default is 86400 -session_life_time = 86400 -; session id hash func, Either "sha1", "sha256" or "md5" default is sha1 -session_id_hashfunc = sha1 -; Session hash key, default is use random string -session_id_hashkey = +# Provider config options +# memory: not have any config yet +# file: session dir path, is relative to grafana data_path +# redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,grafana` +# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1)/database_name` + +provider_config = sessions + +# Session cookie name +cookie_name = grafana_sess + +# If you use session in https only, default is false +cookie_secure = false + +# Session life time, default is 86400 +session_life_time = 86400 + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +google_analytics_ua_id = + +#################################### Security #################################### [security] -; default admin user, created on startup +# default admin user, created on startup admin_user = admin -; default admin password, can be changed before first start of grafana, or in profile settings + +# default admin password, can be changed before first start of grafana, or in profile settings admin_password = admin -; used for signing + +# used for signing secret_key = SW2YcwTIb9zpOOhoPsMm -; Auto-login remember days + +# Auto-login remember days login_remember_days = 7 cookie_username = grafana_user cookie_remember_name = grafana_remember +#################################### Users #################################### [users] -; disable user signup / registration +# disable user signup / registration allow_sign_up = true -; Allow non admin users to create organizations + +# Allow non admin users to create organizations allow_org_create = true + # Set to true to automatically assign new users to the default organization (id 1) auto_assign_org = true -; Default role new users will be automatically assigned (if disabled above is set to true) + +# Default role new users will be automatically assigned (if disabled above is set to true) auto_assign_org_role = Viewer +#################################### Anonymous Auth ########################## [auth.anonymous] -; enable anonymous access +# enable anonymous access enabled = false -; specify organization name that should be used for unauthenticated users + +# specify organization name that should be used for unauthenticated users org_name = Main Org. -; specify role for unauthenticated users + +# specify role for unauthenticated users org_role = Viewer +#################################### Github Auth ########################## [auth.github] enabled = false client_id = some_id @@ -89,7 +142,11 @@ client_secret = some_secret scopes = user:email auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token +api_url = https://api.github.com/user +allowed_domains = +allow_sign_up = false +#################################### Google Auth ########################## [auth.google] enabled = false client_id = some_client_id @@ -97,35 +154,45 @@ client_secret = some_client_secret scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email auth_url = https://accounts.google.com/o/oauth2/auth token_url = https://accounts.google.com/o/oauth2/token +api_url = https://www.googleapis.com/oauth2/v1/userinfo +allowed_domains = +allow_sign_up = false +#################################### Logging ########################## [log] -root_path = data/log -; Either "console", "file", default is "console" -; Use comma to separate multiple modes, e.g. "console, file" -mode = console -; Buffer length of channel, keep it as it is if you don't know what it is. +# Either "console", "file", default is "console" +# Use comma to separate multiple modes, e.g. "console, file" +mode = console, file + +# Buffer length of channel, keep it as it is if you don't know what it is. buffer_len = 10000 -; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" + +# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" level = Info -; For "console" mode only +# For "console" mode only [log.console] level = -; For "file" mode only +# For "file" mode only [log.file] level = -; This enables automated log rotate(switch of following options), default is true +# This enables automated log rotate(switch of following options), default is true log_rotate = true -; Max line number of single file, default is 1000000 + +# Max line number of single file, default is 1000000 max_lines = 1000000 -; Max size shift of single file, default is 28 means 1 << 28, 256MB + +# Max size shift of single file, default is 28 means 1 << 28, 256MB max_lines_shift = 28 -; Segment log daily, default is true + +# Segment log daily, default is true daily_rotate = true -; Expired days of log file(delete after max days), default is 7 + +# Expired days of log file(delete after max days), default is 7 max_days = 7 +#################################### AMPQ Event Publisher ########################## [event_publisher] enabled = false rabbitmq_url = amqp://localhost/ diff --git a/conf/dev.ini b/conf/dev.ini deleted file mode 100644 index 6b94bf11691..00000000000 --- a/conf/dev.ini +++ /dev/null @@ -1,11 +0,0 @@ -app_mode = development - -[server] -router_logging = false -static_root_path = src - -[log] -level = Trace - - - diff --git a/conf/sample.ini b/conf/sample.ini index 4e4c335ae18..68bd3eb3a1d 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1,70 +1,198 @@ -# Sample grafana config for deb & rpm packages -# You only need to specify overrides here -# Defaults are in the /opt/grafana/current/conf/defaults.ini file -# This file is never ovewritten when upgrading grafana via deb or rpm package +##################### Grafana Configuration Example ##################### +# +# Everything has defaults so you only need to uncomment things you want to +# change -app_mode = production +; app_mode = production +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +# +;data = /var/lib/grafana +# +# Directory where grafana can store logs +# +;logs = /var/log/grafana + +#################################### Server #################################### [server] -; protocol (http or https) -protocol = http -; the ip address to bind to, empty will bind to all interfaces -http_addr = -; the http port to use -http_port = 3000 -; The public facing domain name used to access grafana from a browser -domain = localhost -; the full public facing url -root_url = %(protocol)s://%(domain)s:%(http_port)s/ -router_logging = false -; the path relative to grafana process working directory -static_root_path = public -enable_gzip = false +# Protocol (http or https) +;protocol = http +# The ip address to bind to, empty will bind to all interfaces +;http_addr = + +# The http port to use +;http_port = 3000 + +# The public facing domain name used to access grafana from a browser +;domain = localhost + +# The full public facing url +;root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Log web requests +;router_logging = false + +# the path relative working path +;static_root_path = public + +# enable gzip +;enable_gzip = false + +# https certs & key file +;cert_file = +;cert_key = + +#################################### Database #################################### [database] -; Either "mysql", "postgres" or "sqlite3", it's your choice -type = sqlite3 -host = 127.0.0.1:3306 -name = grafana -user = root -password = -; For "postgres" only, either "disable", "require" or "verify-full" -ssl_mode = disable -; For "sqlite3" only -path = /opt/grafana/data/grafana.db +# Either "mysql", "postgres" or "sqlite3", it's your choice +;type = sqlite3 +;host = 127.0.0.1:3306 +;name = grafana +;user = root +;password = +# For "postgres" only, either "disable", "require" or "verify-full" +;ssl_mode = disable + +# For "sqlite3" only, path relative to data_path setting +;path = grafana.db + +#################################### Session #################################### +[session] +# Either "memory", "file", "redis", "mysql", "postgresql", default is "file" +;provider = file + +# Provider config options +# memory: not have any config yet +# file: session dir path, is relative to grafana data_path +# redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,grafana` +# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1)/database_name` +;provider_config = sessions + +# Session cookie name +;cookie_name = grafana_sess + +# If you use session in https only, default is false +;cookie_secure = false + +# Session life time, default is 86400 +;session_life_time = 86400 + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +;reporting_enabled = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +;google_analytics_ua_id = + +#################################### Security #################################### [security] -; default admin user, created on startup -admin_user = admin -; default admin password, can be changed before first start of grafana, or in profile settings -admin_password = admin -; used for signing -secret_key = SW2YcwTIb9zpOOhoPsMm -; Auto-login remember days -login_remember_days = 7 -cookie_username = grafana_user -cookie_remember_name = grafana_remember +# default admin user, created on startup +;admin_user = admin +# default admin password, can be changed before first start of grafana, or in profile settings +;admin_password = admin + +# used for signing +;secret_key = SW2YcwTIb9zpOOhoPsMm + +# Auto-login remember days +;login_remember_days = 7 +;cookie_username = grafana_user +;cookie_remember_name = grafana_remember + +#################################### Users #################################### [users] -; disable user signup / registration -allow_sign_up = true -; Allow non admin users to create organizations -allow_org_create = true +# disable user signup / registration +;allow_sign_up = true + +# Allow non admin users to create organizations +;allow_org_create = true + # Set to true to automatically assign new users to the default organization (id 1) -auto_assign_org = true -; Default role new users will be automatically assigned (if disabled above is set to true) -auto_assign_org_role = Viewer +;auto_assign_org = true +# Default role new users will be automatically assigned (if disabled above is set to true) +;auto_assign_org_role = Viewer + +#################################### Anonymous Auth ########################## [auth.anonymous] -; enable anonymous access -enabled = false -; specify organization name that should be used for unauthenticated users -org_name = Main org. -; specify role for unauthenticated users -org_role = Viewer +# enable anonymous access +;enabled = false +# specify organization name that should be used for unauthenticated users +;org_name = Main Org. + +# specify role for unauthenticated users +;org_role = Viewer + +#################################### Github Auth ########################## +[auth.github] +;enabled = false +;client_id = some_id +;client_secret = some_secret +;scopes = user:email +;auth_url = https://github.com/login/oauth/authorize +;token_url = https://github.com/login/oauth/access_token +;api_url = https://api.github.com/user +# Uncomment bellow to only allow specific email domains +; allowed_domains = mycompany.com othercompany.com + +#################################### Google Auth ########################## +[auth.google] +;enabled = false +;client_id = some_client_id +;client_secret = some_client_secret +;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email +;auth_url = https://accounts.google.com/o/oauth2/auth +;token_url = https://accounts.google.com/o/oauth2/token +;api_url = https://www.googleapis.com/oauth2/v1/userinfo +# Uncomment bellow to only allow specific email domains +; allowed_domains = mycompany.com othercompany.com + +#################################### Logging ########################## [log] -level = Trace -mode = console, file -root_path = /var/log/grafana +# Either "console", "file", default is "console" +# Use comma to separate multiple modes, e.g. "console, file" +;mode = console, file +# Buffer length of channel, keep it as it is if you don't know what it is. +;buffer_len = 10000 + +# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" +;level = Info + +# For "console" mode only +[log.console] +;level = + +# For "file" mode only +[log.file] +;level = +# This enables automated log rotate(switch of following options), default is true +;log_rotate = true + +# Max line number of single file, default is 1000000 +;max_lines = 1000000 + +# Max size shift of single file, default is 28 means 1 << 28, 256MB +;max_lines_shift = 28 + +# Segment log daily, default is true +;daily_rotate = true + +# Expired days of log file(delete after max days), default is 7 +;max_days = 7 + +#################################### AMPQ Event Publisher ########################## +[event_publisher] +;enabled = false +;rabbitmq_url = amqp://localhost/ +;exchange = grafana_events diff --git a/docker/debtest/Dockerfile b/docker/debtest/Dockerfile index 0d32380194c..ee752c98e7d 100644 --- a/docker/debtest/Dockerfile +++ b/docker/debtest/Dockerfile @@ -1,4 +1,6 @@ -FROM debian +FROM debian:jessie + +RUN apt-get update && apt-get install -y vim ADD *.deb /tmp/ diff --git a/docker/rpmtest/Dockerfile b/docker/rpmtest/Dockerfile index e13e4abffb0..3c18cfa1797 100644 --- a/docker/rpmtest/Dockerfile +++ b/docker/rpmtest/Dockerfile @@ -1,6 +1,6 @@ -FROM centos:6.6 +FROM centos:latest RUN yum install -y initscripts -ADD * /tmp/ +ADD *.rpm /tmp/ diff --git a/docs/Makefile b/docs/Makefile index fcb1708f916..d44bc545e2c 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -44,7 +44,7 @@ docs-test: docs-build $(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh docs-build: - git fetch https://github.com/grafana/grafana.git docs-1.x && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files + git fetch https://github.com/grafana/grafana.git docs-2.0 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files echo "$(GIT_BRANCH)" > GIT_BRANCH echo "$(GITCOMMIT)" > GITCOMMIT docker build -t "$(DOCKER_DOCS_IMAGE)" . diff --git a/docs/VERSION b/docs/VERSION index 227cea21564..edb49bc725f 100644 --- a/docs/VERSION +++ b/docs/VERSION @@ -1 +1 @@ -2.0.0 +2.0.0-beta diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 1e963b1b540..2ff90577f07 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -27,6 +27,12 @@ pages: # Introduction: - ['index.md', 'About', 'Grafana'] - ['installation/index.md', 'Installation', 'Installation'] +- ['installation/debian.md', 'Installation', 'Installing on Debian / Ubuntu'] +- ['installation/rpm.md', 'Installation', 'Installing on RPM-based Linux'] +- ['installation/mac.md', 'Installation', 'Installing on Mac OS X'] +- ['installation/windows.md', 'Installation', 'Installing on Windows'] +- ['installation/docker.md', 'Installation', 'Installing on Docker'] + - ['installation/configuration.md', 'Installation', 'Configuration'] - ['installation/provisioning.md', 'Installation', 'Provisioning'] - ['installation/performance.md', 'Installation', 'Performance tips'] @@ -34,12 +40,13 @@ pages: - ['installation/migrating_to2.md', 'Installation', 'Migrating from v1.x to v2.x'] - ['guides/gettingstarted.md', 'User Guides', 'Getting started'] -- ['guides/changes_in_v2.md', 'User Guides', 'Changes and New Features in v2.0'] +- ['guides/whats-new-in-v2.md', 'User Guides', "What's New in Grafana v2.0"] - ['guides/screencasts.md', 'User Guides', 'Screencasts'] - ['reference/graph.md', 'Reference', 'Graph Panel'] - ['reference/singlestat.md', 'Reference', 'Singlestat Panel'] - ['reference/dashlist.md', 'Reference', 'Dashlist Panel'] +- ['reference/sharing.md', 'Reference', 'Sharing'] - ['reference/annotations.md', 'Reference', 'Annotations'] - ['reference/timerange.md', 'Reference', 'Time range controls'] - ['reference/search.md', 'Reference', 'Dashboard Search'] @@ -47,7 +54,7 @@ pages: - ['reference/scripting.md', 'Reference', 'Scripted dashboards'] - ['reference/playlist.md', 'Reference', 'Playlist'] - ['reference/export_import.md', 'Reference', 'Import & Export'] -- ['reference/admin.md', 'Reference', 'Grafana Admin'] +- ['reference/admin.md', 'Reference', 'Administration'] - ['reference/http_api.md', 'Reference', 'HTTP API'] - ['datasources/graphite.md', 'Data Sources', 'Graphite'] diff --git a/docs/sources/datasources/graphite.md b/docs/sources/datasources/graphite.md index 2c091c16331..acf4a78aaa1 100644 --- a/docs/sources/datasources/graphite.md +++ b/docs/sources/datasources/graphite.md @@ -10,9 +10,21 @@ Grafana has an advanced graphite query editor that lets you quickly navigate the Change function paramaters and much more. The editor cannot handle all types of queries yet. To switch to a regular text box click the pen icon to the right. -## Installing Graphite - ## Adding the data source to Grafana +Open the side menu by clicking the the Grafana icon in the top header. In the side menu under the `Dashboards` link you +should find a link named `Data Sources`. If this link is missing in the side menu it means that your current +user does not have the `Admin` role for the current organization. + +![](/img/v2/add_datasource_graphite.png) + +Now click the `Add new` link in the top header. + +Name | Description +------------ | ------------- +Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. +Default | Default data source means that it will be pre-selected for new panels. +Url | The http protocol, ip and port of you graphite-web or graphite-api install. +Access | Proxy = access via Grafana backend, Direct = access directory from browser. ## Metric editor @@ -39,3 +51,9 @@ Some functions like aliasByNode support an optional second argument. To add this ![](/img/animated_gifs/func_editor_optional_params.gif) ## Point consolidation + +All graphite metrics are consolidated so that graphite doesn't return more data points than there are pixels in the graph. By default +this consolidation is done using `avg` function. You can how graphite consolidates metrics by adding the Graphite consolidateBy function. + +> *Notice* This means that legend summary values (max, min, total) cannot be all correct at the same time. They are calculated +> client side by Grafana. And depending on your consolidation function only one or two can be correct at the same time. diff --git a/docs/sources/datasources/influxdb.md b/docs/sources/datasources/influxdb.md index 22b28f8729a..04fbd520a7e 100644 --- a/docs/sources/datasources/influxdb.md +++ b/docs/sources/datasources/influxdb.md @@ -7,7 +7,36 @@ page_keywords: grafana, influxdb, metrics, query, documentation # InfluxDB -## InfluxDB query editor +There are currently two separate datasources for InfluxDB in Grafana: InfluxDB 0.8.x and InfluxDB 0.9.x. The API and capabilities of InfluxDB 0.9.x are completely different from InfluxDB 0.8.x. InfluxDB 0.9.x data source support is provided on an experimental basis. + +## Adding the data source to Grafana +Open the side menu by clicking the the Grafana icon in the top header. In the side menu under the `Dashboards` link you +should find a link named `Data Sources`. If this link is missing in the side menu it means that your current +user does not have the `Admin` role for the current organization. + +![](/img/v2/add_datasource_influxdb.png) + +Now click the `Add new` link in the top header. + +Name | Description +------------ | ------------- +Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. +Default | Default data source means that it will be pre-selected for new panels. +Url | The http protocol, ip and port of you influxdb api (influxdb api port is by default 8086) +Access | Proxy = access via Grafana backend, Direct = access directory from browser. +Database | Name of your influxdb database +User | Name of your database user +Password | Database user's password + +> *Note* When using Proxy access mode the InfluxDB database, user and password will be hidden from the browser/frontend. When +> using direct access mode all users will be able to see the database user & password. + +## InfluxDB 0.9.x query editor + +This editor & data source is not compatible with InfluxDB 0.8.x, please use the right data source for you InfluxDB version. +The InfluxDB 0.9.x editor is currently under development and is not yet fully usable. + +## InfluxDB 0.8.x query editor ![](/img/v1/influxdb_editor.png) @@ -19,7 +48,11 @@ select [[func]]([[column]]) from [[series]] where [[timeFilter]] group by time([ To write the complete query yourself click the cog wheel icon to the right and select ``Raw query mode``. -## InfluxDB Filters & Templated queries +## InfluxDB 0.9 Filters & Templates queries + +The InfluxDB 0.9 data source does not currently support filters or templates. + +## InfluxDB 0.8 Filters & Templated queries ![](/img/animated_gifs/influxdb_templated_query.gif) diff --git a/docs/sources/datasources/opentsdb.md b/docs/sources/datasources/opentsdb.md index 5ccd60dd034..09b9f647a5f 100644 --- a/docs/sources/datasources/opentsdb.md +++ b/docs/sources/datasources/opentsdb.md @@ -6,106 +6,28 @@ page_keywords: grafana, opentsdb, documentation # OpenTSDB Guide -Here you will find some configuration tips for how to setup Grafana and OpenTSDB. +## Adding the data source to Grafana +Open the side menu by clicking the the Grafana icon in the top header. In the side menu under the `Dashboards` link you +should find a link named `Data Sources`. If this link is missing in the side menu it means that your current +user does not have the `Admin` role for the current organization. -## OpenTSDB configuration +![](/img/v2/add_datasource_opentsdb.png) -For OpenTSDB to work in Grafana you will either be needing to run the latest OpenTSDB -version built from the `next` branch that includes built in support for remote -api usage (CORS). This was merged into the next branch with [this issue](https://github.com/OpenTSDB/opentsdb/pull/333). +Now click the `Add new` link in the top header. -If you upgrade you have to set the OpenTSDB setting `tsd.http.request.cors_domains` to your -grafana webserver domain name. +Name | Description +------------ | ------------- +Name | The data source name, important that this is the same as in Grafana v1.x if you plan to import old dashboards. +Default | Default data source means that it will be pre-selected for new panels. +Url | The http protocol, ip and port of you opentsdb server (default port is usually 4242) +Access | Proxy = access via Grafana backend, Direct = access directory from browser. -If you do not want to upgrade OpenTSDB you need to setup an nginx proxy that will add the necessary CORS -HTTP headers. - -Example nginx config: - -Replace: - - - **OPENTSDB_HOST** (2 instances) - Hostname or IP address of the OpenTSDB server - - **OPENTSDB_PORT** (1 instance) - Port number of the OpenTSDB server - - **GRAFANA_DOMAIN** (1 instance) - Domain/Hostname of the Grafana server - -```nginx -upstream opentsdb { - server OPENTSDB_HOST:OPENTSDB_PORT fail_timeout=0; -} - -server { - listen *:4243; - - location / { - # Regex to whitelist systems - if ($http_origin ~* (https?://([a-z0-9._-]*\.)?GRAFANA_DOMAIN(:[0-9]+)?)) { - set $cors "true"; - } - - # OPTIONS indicates a CORS pre-flight request - if ($request_method = 'OPTIONS') { - set $cors "${cors}-options"; - } - - # If it's OPTIONS, then it's a CORS preflight request so respond immediately with no response body - if ($cors = "true-options") { - add_header 'Access-Control-Allow-Origin' "$http_origin"; - add_header 'Access-Control-Allow-Credentials' 'true'; - add_header 'Access-Control-Max-Age' 1728000; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; - add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since'; - add_header 'Content-Length' 0; - add_header 'Content-Type' 'text/plain charset=UTF-8'; - return 204; - } - - # Proxy the request - proxy_set_header X-Host OPENTSDB_HOST; - proxy_set_header X-Forwarded-For $Proxy_add_x_forwarded_for; - proxy_set_header Authorization ""; - proxy_pass http://opentsdb; - proxy_redirect default; - proxy_buffer_size 16k; - proxy_buffers 8 32k; - proxy_busy_buffers_size 64k; - proxy_temp_file_write_size 64k; - proxy_read_timeout 120; - # Strip any OpenTSDB-generated CORS headers that overlap with our own - proxy_hide_header 'Access-Control-Allow-Origin'; - proxy_hide_header 'Access-Control-Allow-Credentials'; - proxy_hide_header 'Access-Control-Allow-Headers'; - - # if it's a GET or POST, set the standard CORS responses header - if ($cors = "true") { - # Add our own CORS headers - add_header 'Access-Control-Allow-Origin' "$http_origin"; - add_header 'Access-Control-Allow-Credentials' 'true'; - add_header 'Access-Control-Allow-Headers' '*'; - } - } -} -``` - -## Grafana config -In config.js specify your opentsdb datasource: - -```javascript -datasources: { - 'OpenTSDB-TEST': { - default: true, - type: 'opentsdb', - url: "http://my_opentsdb_server:4242" - } - }, -``` - -## Create a graph +## Query editor Open a graph in edit mode by click the title. -![](/img/opentsdb/editor_v1.png) +![](/img/v2/opentsdb_query_editor.png) -For details on opentsdb metric queries -checkout the offical [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) +For details on opentsdb metric queries checkout the offical [OpenTSDB documentation](http://opentsdb.net/docs/build/html/index.html) diff --git a/docs/sources/guides/changes_in_v2.md b/docs/sources/guides/changes_in_v2.md deleted file mode 100644 index c9d08f046bd..00000000000 --- a/docs/sources/guides/changes_in_v2.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -page_title: Changes and new features in Grafana v2.0 -page_description: Changes and new features in Grafana v2.0 -page_keywords: grafana, changes, features, documentation ---- - -# Changes and new features in v2.0 - -This is a guide that descriptes some of changes and new features that can be found in Grafana v2.0. - - -## New dashboard top header - - - -1. Side menu toggle -2. Dashboard search (also includes access to New dashboard, Import & Playlist) -3. Dashboard title -4. Star/unstar current dashboard -5. Share current dashboard (Make sure the dashboard is saved before) -6. Save current dashboard -7. Settings dropdown - - Dashboard settings - - Annotations - - Templating - - Export (exports current dashboard to json file) - - View JSON (view current dashboard json model) - - Save As... (Copy & Save current dashboard under a new name) - - Delete dashboard - -> **Note** In Grafana v2.0 when you change the title of a dashboard and then save it it will no -> longer create a new dashboard. It will just change the name for the current dashboard. -> To change name and create a new dashboard use the `Save As...` menu option - -## Panel time overrides & timeshift - -In Grafana v2.x you can now override the relative time range for individual panels. You can also add a -time shift to individual panels. This allows you to show metrics from different time periods or days -at the same time. - -![](/img/v2/panel_time_override.jpg) - -You control these overrides in panel editor mode and the new tab `Time Range`. - -![](/img/v2/time_range_tab.jpg) - -Currently you can only override the dashboard time with relative time ranges, not absolute time ranges. When -you zoom or change the dashboard time to a custom absolute time range the panel overrides will be disabled. The -panel relative time override is only active when the dashboard time is also relative. The panel timeshift override -however is always active, even when the dashboard time is absolute. - -The `Hide time override info` option allows you to hide the the override info text that is by default shown in the -upper right of a panel when overriden time range options. - -## New search view & starring dashboards - -![](/img/v2/dashboard_search.jpg) - -The dashboard search view has received a big UI update and polish. You can now see and filter by which dashboard -you have personally starred. - -## Dashlist panel - -![](/img/v2/dashlist_starred.png) - -There is one new panel in Grafana v2.0 and that is the `dashlist` panel that allows you to show your personal -starred dashboards as well as do custom searches based on search strings or tags. - -## Data Source proxy & admin views - -Data sources in Grafana v2.0 are NOT defined in a config file but added through the UI. The backend can also -handle proxying data source metric requests which meens that it is a lot easier to get started using Grafana with -Graphite or OpenTSDB without having to spend time with nginx CORS (Cross origin resource sharing) work arounds. - -> **Note** For InfluxDB users: The data source proxy feature will -> hide database user & password details from the frontend / browser. - -## Relative time now delay - -A problem that has plagued many is the fact that graphs tend to dip to zero by the end because metrics for -the last interval has yet to be received by the time series database. You can now work around this by adding -a now delay in `Dashboard Settings` > `Time Picker` tab. - -![](/img/v2/timepicker_now_delay.jpg) - -## Overwrite protection - -Grafana v2.0 will protect you and your other Grafana users from accidentally overwriting each others changes -to the same dashboard. The same protection also applies if you try to create a new dashboard with a -name that already exists. - -![](/img/v2/overwrite_protection.jpg) - -## User preferences - -If you open side menu (by clicking on the Grafana icon in the top header) you can access your profile page. -Here you can update your user details, UI Theme and change password. - -## PNG rendering - -In the panel share dialog you now have access to a link that will render the panel to a PNG image. -The panel is rendered on the backend via phantomjs (headless browser). This requires that your metric -data source is accessible from your Grafana server host machine. - -![](/img/v2/share_dialog_image_highlight.jpg) - -## User & Organization permissions - -All dashboards and data sources are linked to an organization (not to a user). Users are linked to -Organizations via a role. That role can be: - -- `Viewer`: Can only view dashboards, not save / create them. -- `Editor`: Can view, update and create dashboards. -- `Admin`: Everything an Editor can plus edit and add data sources and organization users. - -> **Note** A `Viewer` can still view all metrics exposed through a data source, not only -> the metrics used in already existing dashboards. That is because there are not -> per series permissions in Graphite, InfluxDB or OpenTSDB. - -There are currently no permissions on individual dashboards. diff --git a/docs/sources/guides/gettingstarted.md b/docs/sources/guides/gettingstarted.md index df936138483..b0037668f56 100644 --- a/docs/sources/guides/gettingstarted.md +++ b/docs/sources/guides/gettingstarted.md @@ -5,38 +5,64 @@ page_keywords: grafana, guide, documentation --- # Getting started -This guide will help you get started and acquainted with the Grafana user interface. +This guide will help you get started and acquainted with the Grafana user interface. It assumes you have a working +Grafana 2.0 instance, and have added at least one Grafana data source. -## Interface overview - +## Beginner guides +Watch the 10min [beginners guide to building dashboards](https://www.youtube.com/watch?v=sKNZMtoSHN4&index=7&list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2) +to get a quick intro to the dashboard & panel editing UI. -## New dashboard -![](/img/animated_gifs/new_dashboard.gif) +## Top header + -## Edit graphs -Click on a panel's title and then ``Edit`` to open a panel in edit mode. -![](/img/v1/edit_graph_ui_guide.png) +The image above shows you the top header for a dashboard. -## Editing Rows -To the right of each row you have two colored rectangles, hover over these to get access to quick row controls. -![](/img/animated_gifs/row_edit_menu.gif) +1. Side menubar toggle: This toggles the side menu, allowing you to focus on the data presented in the dashboard. The side menu provides access to features unrelated to a Dashboard such as Users, Organizations, and Data Sources. +2. Dashboard dropdown: This dropdown shows you which Dashboard you are currently viewing, and allows you to easily switch to a new Dashboard. From here you can also create a new Dashboard, Import existing Dashboards, and manage Dashboard playlists. +3. Star Dashboard: Star (or unstar) the current Dashboar. Starred Dashboards will show up on your own Home Dashboard by default, and are a convenient way to mark Dashboards that you're interested in. +4. Share Dashboard: Share the current dashboard by creating a link or create a static Snapshot of it. Make sure the Dashboard is saved before sharing. +5. Save dashboard: The current Dashboard will be saved with the current Dashboard name. +6. Settings: Manage Dashboard settings and features such as Templating and Annotations. + +## Dashboard +Dashboards are at the core of what Grafana is all about. Dashboards are composed of individual Panels arranged on a number of Rows. +By adjusting the display properties of Panels and Rows, you can customize the perfect Dashboard for your exact needs. +Each panel can interact with data from any configured Grafana Data Source (currently InfluxDB, Graphite, OpenTSDB, and KairosDB). +This allows you to create a single dashboard that unifies the data across your organization. Panels use the time range specificed +in the main Time Picker in the upper right, but they can also have relative time overrides. + + + +1. Zoom out time range +2. Time picker dropdown. Here you can access relative time range options, auto refresh options and set custom absolute time ranges. +3. Manual refresh button. Will cause all panels to refresh (fetch new data). +4. Row controls menu. Via this menu you can add panels to the row, set row height and more. +5. Dashboard panel. You edit panels by clicking the panel title. +6. Graph legend. You can change series colors, y-axis and series visibility directly from the legend. + +## Drag-and-Drop panels + +You can Drag-and-Drop Panels within and between Rows. Click and hold the Panel title, and drag it to its new location. -## Drag drop panels -Click and drag the panel title to start a drag and drop action. ![](/img/animated_gifs/drag_drop.gif) ## Tips and shortcuts * Click the graph title and in the dropdown menu quickly change span or duplicate the panel. +* Click the Save icon in the menu to save the dashboard with a new name +* Click the Save icon in the menu and then advanced to export the dashboard to json file, or set it as your default dashboard. +* Click the colored icon in the legend to select series color +* Click series name in the legend to hide series +* Ctrl/Shift/Meta + Click legend name to hide other series + +## Grafana loves the keyboard + * Ctrl+S Saves the current dashboard * Ctrl+F Opens the dashboard finder / search * Ctrl+H Hides all controls (good for tv displays) * Hit Escape to exit graph when in fullscreen or edit mode -* Click the colored icon in the legend to select series color -* Click series name in the legend to hide series -* Ctrl/Shift/Meta + Click legend name to hide other series -* Click the Save icon in the menu to save the dashboard with a new name -* Click the Save icon in the menu and then advanced to export the dashboard to json file, or set it as your default dashboard. + + diff --git a/docs/sources/guides/screencasts.md b/docs/sources/guides/screencasts.md index 07f9801e9f8..6c09e5b26af 100644 --- a/docs/sources/guides/screencasts.md +++ b/docs/sources/guides/screencasts.md @@ -1,16 +1,65 @@ page_title: Screencasts page_description: Grafana screencasts page_keywords: grafana, screencasts, documentation, guides +no_toc: true # Screencasts -## Grafana Screencasts - Episode 2 - Templated Graphite Queries - +
+
+

Episode 1 - Building Graphite Queries

+
+ +
+
+
+

Episode 2 - Templated Graphite Queries

+
+ +
+
+
+ -
-## Grafana Screencasts - Episode 1 - Building Graphite Queries - - -
+
+
+

Episode 3 - Whats New In Grafana 2.0

+
+ +
+
+
+

Episode 4 - Installation & Configuration on Ubuntu / Debian

+
+ +
+
+
+ +
+
+

Episode 5 - Installation & Configuration on Redhat / Centos

+
+ +
+
+
+

Episode 6 - Adding data sources, users & organizations

+
+ +
+
+
+ +
+
+

Episode 7 - Beginners guide to building dashboards

+
+ +
+
+
+
+
diff --git a/docs/sources/guides/whats-new-in-v2.md b/docs/sources/guides/whats-new-in-v2.md new file mode 100644 index 00000000000..a388aef6160 --- /dev/null +++ b/docs/sources/guides/whats-new-in-v2.md @@ -0,0 +1,176 @@ +--- +page_title: What's New in Grafana v2.0 +page_description: What's new in Grafana v2.0 +page_keywords: grafana, new, changes, features, documentation +--- + +# What's New in Grafana v2.0 + +Grafana 2.0 represents months of work by the Grafana team and the community. We are pleased to be able to +release the Grafana 2.0 beta. This is a guide that describes some of changes and new features that can +be found in Grafana V2.0. + +If you are interested in how to migrate from Grafana V1.x to V2.0, please read our [Migration Guide](../installation/migrating_to2.md) + +## New backend server + +Grafana now ships with its own required backend server. Also completely open-source, it's written in Go and has a full HTTP API. + +In addition to new features, the backend server makes it much easier to set up and enjoy Grafana. Grafana 2.0 now ships as cross platform binaries with no dependencies. Authentication is built in, and Grafana is now capable of proxying connections to Data Sources. There are no longer any CORS (Cross Origin Resource Sharing) issues requiring messy workarounds. Elasticsearch is no longer required just to store dashboards. + +## User & Organization permissions + +All Dashboards and Data Sources are linked to an Organization (not to a User). Users are linked to +Organizations via a role. That role can be: + +- `Viewer`: Can only view dashboards, not save / create them. +- `Editor`: Can view, update and create dashboards. +- `Admin`: Everything an Editor can plus edit and add data sources and organization users. + +> **Note** A `Viewer` can still view all metrics exposed through a data source, not only +> the metrics used in already existing dashboards. That is because there are not +> per series permissions in Graphite, InfluxDB or OpenTSDB. + +There are currently no permissions on individual dashboards. + +Read more about Grafanas new user model on the [Admin section](../reference/admin/) + +## Dashboard Snapshot sharing + +A Dashboard Snapshot is an easy way to create and share a URL for a stripped down, point-in-time version of any Dashboard. +You can give this URL to anyone or everyone, and they can view the Snapshot even if they're not a User of your Grafana instance. + +You can set an expiration time for any Snapshots you create. When you create a Snapshot, we strip sensitive data, like +panel metric queries, annotation and template queries and panel links. The data points displayed on +screen for that specific time period in your Dashboard is saved in the JSON of the Snapshot itself. + +Sharing a Snapshot is similar to sharing a link to a screenshot of your dashboard, only way better (they'll look great at any screen resolution, you can hover over series, +even zoom in). Also they are fast to load as they aren't actually connected to any live Data Sources in any way. + +They're a great way to communicate about a particular incident with specific people who aren't Users of your Grafana instance. You can also use them to show off your dashboards over the Internet. + +![](/img/v2/dashboard_snapshot_dialog.png) + +### Publish snapshots + +You can publish snapshots locally or to [snapshot.raintank.io](http://snapshot.raintank.io). snapshot.raintank is a free service provided by [raintank](http://raintank.io) for hosting external Grafana snapshots. + +Either way, anyone with the link (and access to your Grafana instance for local snapshots) can view it. + +## Panel time overrides & timeshift + +In Grafana v2.x you can now override the relative time range for individual panels, causing them to be different than what is selected in the Dashboard time picker in the upper right. You can also add a time shift to individual panels. This allows you to show metrics from different time periods or days at the same time. + +![](/img/v2/panel_time_override.jpg) + +You control these overrides in panel editor mode and the new tab `Time Range`. + +![](/img/v2/time_range_tab.jpg) + +When you zoom or change the Dashboard time to a custom absolute time range, all panel overrides will be disabled. The panel relative time override is only active when the dashboard time is also relative. The panel timeshift override however is always active, even when the dashboard time is absolute. + +The `Hide time override info` option allows you to hide the the override info text that is by default shown in the +upper right of a panel when overridden time range options. + +Currently you can only override the dashboard time with relative time ranges, not absolute time ranges. + +## Panel iframe embedding + +You can embed a single panel on another web page or your own application using the panel share dialog. + +Below you should see an iframe with a graph panel (taken from a Dashboard snapshot at [snapshot.raintank.io](http://snapshot.raintank.io). + +Try hovering or zooming on the panel below! + + + +This feature makes it easy to include interactive visualizations from your Grafana instance anywhere you want. + +## New dashboard top header + +The top header has gotten a major streamlining in Grafana V2.0. + + + +1. `Side menubar toggle` Toggle the side menubar on or off. This allows you to focus on the data presented on the Dashboard. The side menubar provides access to features unrelated to a Dashboard such as Users, Organizations, and Data Sources. +2. `Dashboard dropdown` The main dropdown shows you which Dashboard you are currently viewing, and allows you to easily switch to a new Dashboard. From here you can also create a new Dashboard, Import existing Dashboards, and manage the Playlist. +3. `Star Dashboard`: Star (or un-star) the current Dashboard. Starred Dashboards will show up on your own Home Dashboard by default, and are a convenient way to mark Dashboards that you're interested in. +4. `Share Dashboard`: Share the current dashboard by creating a link or create a static Snapshot of it. Make sure the Dashboard is saved before sharing. +5. `Save dashboard`: Save the current Dashboard with the current name. +6. `Settings`: Manage Dashboard settings and features such as Templating, Annotations and the name. + +> **Note** In Grafana v2.0 when you change the title of a dashboard and then save it it will no +> longer create a new Dashboard. It will just change the name for the current Dashboard. +> To change name and create a new Dashboard use the `Save As...` menu option + +### New Side menubar + +The new side menubar provides access to features such as User Preferences, Organizations, and Data Sources. + +If you have multiple Organizations, you can easily switch between them here. + +The side menubar will become more useful as we build out additional functionality in Grafana 2.x + +You can easily collapse or re-open the side menubar at any time by clicking the Grafana icon in the top left. We never want to get in the way of the data. + +## New search view & starring dashboards + +![](/img/v2/dashboard_search.jpg) + +The dashboard search view has gotten a big overhaul. You can now see and filter by which dashboard you have personally starred. + +## Logarithmic scale + +The Graph panel now supports 3 logarithmic scales, `log base 10`, `log base 32`, `log base 1024`. Logarithmic y-axis scales are very useful when rendering many series of different order of magnitude on the same scale (eg. +latency, network traffic, and storage) + +![](/img/v2/graph_logbase10_ms.png) + +## Dashlist panel + +![](/img/v2/dashlist_starred.png) + +The dashlist is a new panel in Grafana v2.0. It allows you to show your personal starred dashboards, as well as do custom searches based on search strings or tags. + +dashlist is used on the new Grafana Home screen. It is included as a reference Panel and is useful to provide basic linking between Dashboards. + +## Data Source proxy & admin views + +Data sources in Grafana v2.0 are no longer defined in a config file. Instead, they are added through the UI or the HTTP API. + +The backend can now proxy data from Data Sources, which means that it is a lot easier to get started using Grafana with Graphite or OpenTSDB without having to spend time with CORS (Cross origin resource sharing) work-arounds. + +In addition, connections to Data Sources can be better controlled and secured, and authentication information no longer needs to be exposed to the browser. + +## Dashboard "now delay" + +A commonly reported problem has been graphs dipping to zero at the the end, because metric data for the last interval has yet to be written to the Data Source. These graphs then "self correct" once the data comes in, but can look deceiving or alarming at times. + +You can avoid this problem by adding a `now delay` in `Dashboard Settings` > `Time Picker` tab. This new feature will cause Grafana to ignore the most recent data up to the set delay. +![](/img/v2/timepicker_now_delay.jpg) + +The delay that may be necessary depends on how much latency you have in your collection pipeline. + +## Dashboard overwrite protection + +Grafana v2.0 protects Users from accidentally overwriting each others Dashboard changes. Similar protections are in place if you try to create a new Dashboard with the same name as an existing one. + +![](/img/v2/overwrite_protection.jpg) + +These protections are only the first step; we will be building out additional capabilities around dashboard versioning and management in future versions of Grafana. + +## User preferences + +If you open side menu (by clicking on the Grafana icon in the top header) you can access your Profile Page. + +Here you can update your user details, UI Theme, and change your password. + +## Server-side Panel rendering + +Grafana now supports server-side PNG rendering. From the Panel share dialog you now have access to a link that will render a particular Panel to a PNG image. + +> **Note** This requires that your Data Source is accessible from your Grafana instance. + +![](/img/v2/share_dialog_image_highlight.jpg) + + diff --git a/docs/sources/index.md b/docs/sources/index.md index ca463496947..24ba087575e 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -4,35 +4,35 @@ page_keywords: grafana, introduction, documentation, about # About Grafana -Grafana is a general purpose dashboard and graph composer. It's focused on providing -rich ways to visualize time series metrics, mainly though graphs but supports other ways to visualize data through -a pluggable panel architecture. It currently has rich support for for [Graphite](http://graphite.readthedocs.org/en/latest/), -[InfluxDB](http://influxdb.org) and [OpenTSDB](http://opentsdb.net). But supports other data sources via plugins. +Grafana is a leading open source applications for visualizing large-scale measurement data. -It is most commonly used for infrastructure monitoring, application monitoring and metric analytics. But Grafana -has an open architecture and design that enabled is to be used in other domains as well, like home automation, -weather and industrial sensors analytics. +It provides a powerful and elegant way to create, share, and explore data and dashboards from your disparate metric databases, either with your team or the world. -## Support +Grafana is most commonly used for Internet infrastructure and application analytics, but many use it in other domains including industrial sensors, home automation, weather, and process control. -If you have any trouble with Grafana, either the install or some feature you do not understand or you suspect isn't working -correctly there are a number of sources where you can get help. +Grafana features pluggable panels and data sources allowing easy extensibility. There is currently rich support for [Graphite](http://graphite.readthedocs.org/en/latest/), [InfluxDB](http://influxdb.org) and [OpenTSDB](http://opentsdb.net). There is also experimental support for KairosDB, and SQL is on the roadmap. Grafana has a variety of panels, including a fully featured graph panel with rich visualization options. + +Version 2.0 was released in April 2015: Grafana now ships with its own backend server that brings [many changes and features](../guides/whats-new-in-v2/). + +## Community Resources, Feedback, and Support + +Thousands of organizations large and small rely on Grafana, and we have a vibrant and active community that constantly inspires us. + +Please don't hesitate to [open a new issue on Github](https://github.com/grafana/grafana/issues) with your suggestions, ideas, and bug reports. + +Most of the new features and improvements that go into Grafana come from our users. We greatly value your feedback and suggestions; we consider them paramount to making the product better! + +If you have any trouble with Grafana, whether you can't get it set up or you just want clarification on a feature, there are a number of ways to get help: - [Troubleshooting guide](../troubleshooting) -- \#grafana IRC channel on freenode -- Search closed and open [issues on github](https://github.com/grafana/grafana/issues). +- \#grafana IRC channel on the freenode network (chat.freenode.net) +- Search closed and open [issues on GitHub](https://github.com/grafana/grafana/issues) - [Mailing list](https://groups.io/org/groupsio/grafana) -Do not hesitate to open a new issue with a question, bug report or an idea for improvement. +## Commercial Support -User feedback and involvement is paramount to making a product better so please take the time and create an issue. - -## Paid support -If you wish to get paid support please [contact us](mailto:contact@grafana.org). +[raintank](http://www.raintank.io), the company behind Grafana, will be launching a SaaS Grafana-based platform later this year that will also include commercial support for all your existing Grafana installations. Please sign up for [early access at raintank](http://www.raintank.io) for more information. ## License -Grafana is licensed under Apache 2.0. See [LICENSE](https://github.com/grafana/grafana/blob/master/LICENSE.mdhttps://github.com/grafana/grafana/blob/master/LICENSE.md) -for full license text. - - +By utilizing this software, you agree to the terms of the included license. Grafana is licensed under the Apache 2.0 agreement. See [LICENSE](https://github.com/grafana/grafana/blob/master/LICENSE.md) for the full license terms. diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 048301a7714..a79a8494d73 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -36,13 +36,37 @@ Then you can override that using: export GF_SECURITY_ADMIN_USER=true
+## [paths] + +### data +Path to where grafana can store the sqlite3 database (if used), file based sessions (if used), and other data. +This path is usually specified via command line in the init.d script or the systemd service file. + +### logs +Path to where grafana can store logs. This path is usually specified via command line in the init.d script or the systemd service file. +It can be overriden in the config file or in the default environment variable file. + ## [server] ### http_addr The ip address to bind to, if empty will bind to all interfaces ### http_port -The port to bind to, defaults to `3000` +The port to bind to, defaults to `3000`. To use port 80 you need to either give the grafana binary permission for example: + +``` +$ sudo setcap 'cap_net_bind_service=+ep' /opt/grafana/current/grafana +``` + +Or redirect port 80 to the grafana port using: +``` +$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000 +``` + +Another way is put nginx or apache infront of Grafana and have them proxy requests to Grafana. + +### protocol +`http` or `https` ### domain This setting is only used in as a part of the root_url setting (see below). Important if you @@ -59,6 +83,14 @@ google or github oauth authentication (for the callback url to be correct). The path to the directory where the frontend files (html & js & css). Default to `public` which is why the Grafana binary needs to be executed with working directory set to the installation path. +### cert_file +Path to cert file (if protocol is https) + +### cert_key +Path to cert key file (if protocol is https) + +
+
## [database] @@ -149,10 +181,14 @@ Client ID and a Client Secret. Specify these in the grafana config file. Example scopes = user:email auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token + allow_sign_up = false Restart the grafana backend. You should now see a github login button on the login page. You can now login or signup with your github accounts. +You may allow users to sign-up via github auth by setting allow_sign_up to true. When this option is +set to true, any user successfully authenticating via github auth will be automatically signed up. + ## [auth.google] You need to create a google project. You can do this in the [Google Developer Console](https://console.developers.google.com/project). When you create the project you will need to specify a callback URL. Specify this as callback: @@ -170,21 +206,26 @@ Client ID and a Client Secret. Specify these in the grafana config file. Example scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email auth_url = https://accounts.google.com/o/oauth2/auth token_url = https://accounts.google.com/o/oauth2/token + allowed_domains = mycompany.com + allow_sign_up = false Restart the grafana backend. You should now see a google login button on the login page. You can -now login or signup with your google accounts. +now login or signup with your google accounts. `allowed_domains` option is optional. + +You may allow users to sign-up via google auth by setting allow_sign_up to true. When this option is +set to true, any user successfully authenticating via google auth will be automatically signed up.
## [session] ### provider -Valid values are "memory", "file", "mysql", 'postgres'. Default is "memory". +Valid values are "memory", "file", "mysql", 'postgres'. Default is "file". ### provider_config This option should be configured differently depending on what type of session provider you have configured. - **file:** session file path, e.g. `data/sessions` -- **mysql:** go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` +- **mysql:** go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1)/database_name` if you use mysql or postgres as session store you need to create the session table manually. Mysql Example: @@ -205,4 +246,16 @@ Set to true if you host Grafana behind HTTPs only. Defaults to `false`. ### session_life_time How long sessions lasts in seconds. Defaults to `86400` (24 hours). +## [analytics] + +### reporting_enabled +When enabled Grafana will send anonymous usage statistics to stats.grafana.org. +No ip addresses are being tracked, only simple counters to track running instances, +versions, dashboard & error counts. It is very helpful to us, please leave this +enabled. Counters are sent every 24 hours. Default value is `true`. + +### google_analytics_ua_id +If you want to track Grafana usage via Google analytics specify *your* Univeral Analytics ID +here. By defualt this feature is disabled. + diff --git a/docs/sources/installation/debian.md b/docs/sources/installation/debian.md new file mode 100644 index 00000000000..4bba51ec530 --- /dev/null +++ b/docs/sources/installation/debian.md @@ -0,0 +1,110 @@ +--- +page_title: Installing on Debian / Ubuntu +page_description: Grafana Installation guide for Debian / Ubuntu. +page_keywords: grafana, installation, debian, ubuntu, guide +--- + +# Installing on Debian / Ubuntu + +## Download + +Description | Download +------------ | ------------- +.deb for Debian-based Linux | [grafana_2.0.2_amd64.deb](https://grafanarel.s3.amazonaws.com/builds/grafana_2.0.2_amd64.deb) + +## Install + + $ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.0.2_amd64.deb + $ sudo apt-get install -y adduser libfontconfig + $ sudo dpkg -i grafana_2.0.1_amd64.deb + +## APT Repository +Add the following line to your `/etc/apt/sources.list` + + deb https://packagecloud.io/grafana/stable/debian/ wheezy main + +Use the above line even if you are on Ubuntu or another debian version. There is also testing +repository if you want beta or release candidates. + + deb https://packagecloud.io/grafana/testing/debian/ wheezy main + +Then add the [Package Cloud](https://packagecloud.io/grafana) key (signs repo metadata). + + $ curl https://packagecloud.io/gpg.key | sudo apt-key add - + +Update apt and install Grafana + + $ sudo apt-get update + $ sudo apt-get install grafana + +On some older versions of Ubuntu and Debian you may need to install `apt-transport-https`, +needed to fetch packages over HTTPS. + + $ sudo apt-get install -y apt-transport-https + +## Package details + +- Installs binary to `/usr/sbin/grafana-server` +- Init.d script to `/etc/init.d/grafana-server` +- Default file (environment vars) to `/etc/default/grafana-server` +- Configuration file to `/etc/grafana/grafana.ini` +- Systemd service (if systemd is available) name `grafana-server.service` +- The default configuration specifies log file at `/var/log/grafana/grafana.log` +- The default configuration specifies sqlite3 db at `/var/lib/grafana/grafana.db` + +## Start the server (init.d service) + +- Start grafana by `sudo service grafana-server start` +- This will start the grafana-server process as the `grafana` user (created during package install) +- Default http port is `3000`, and default user is admin/admin + +To configure Grafana server to start at boot time: + + $ sudo update-rc.d grafana-server defaults 95 10 + +## Start the server (via systemd) + $ systemctl daemon-reload + $ systemctl start grafana-server + $ systemctl status grafana-server + +Enable the systemd service (so grafana starts at boot) + + sudo systemctl enable grafana-server.service + +## Environment file + +The systemd service file and init.d script both use the file located at `/etc/default/grafana-server` for +environment variables used when starting the backend. Here you can override log directory, data directory and other +variables. + +### Logging + +By default grafana will log to `/var/log/grafana` + +### Database + +The default configuration specifies a sqlite3 database located at `/var/lib/grafana/grafana.db`. Please backup +this database before upgrades. You can also use mysql or postgres as the Grafana database. + +## Configuration + +The configuration file is located at `/etc/grafana/grafana.ini`. Go the [Configuration](configuration) page for details +on all those options. + +### Adding data sources + +- [Graphite](../datasources/graphite.md) +- [InfluxDB](../datasources/influxdb.md) +- [OpenTSDB](../datasources/opentsdb.md) + +## Installing from binary tar file + +Start by [downloading](http://grafana.org/download/builds) the latest `.tar.gz` file and extract it. +This will extract into a folder named after the version you downloaded. This folder contains all files required to run grafana. +There are no init scripts or install scripts in this package. + +To configure grafana add a config file named `custom.ini` to the `conf` folder and override any of the settings defined in +`conf/defaults.ini`. Start grafana by excecuting `./grafana web`. The grafana binary needs the working directory +to be the root install dir (where the binary is and the public folder is located). + + diff --git a/docs/sources/installation/docker.md b/docs/sources/installation/docker.md new file mode 100644 index 00000000000..5ec9e4f697e --- /dev/null +++ b/docs/sources/installation/docker.md @@ -0,0 +1,34 @@ +--- +page_title: Installing using Docker +page_description: Grafana Installation guide using Docker container +page_keywords: grafana, installation, docker, container, guide +--- + +# Installing using Docker + +## Install from offical docker image + +Grafana has an offical docker container. + + $ docker run -i -p 3000:3000 grafana/grafana + +All grafana configuration settings can be defined using ENVIRONMENT variables, this is especially useful when using the +above container. + +## Docker volumes & ENV config + +The docker container exposes two volumes, the sqlite3 database in the folder `/var/lib/grafana` and +configuration files is in `/etc/grafana/` folder. You can map these volumes to host folders when you start the container: + + $ docker run -d -p 3000:3000 \ + -v /var/lib/grafana:/var/lib/grafana \ + -e "GF_SECURITY_ADMIN_PASSWORD=secret \ + grafana/grafana:develop + +In the above example I map the data folder and set a config option via an `ENV` variable. + +## Configuration + +The backend web server has a number of configuration options. Go the [Configuration](configuration) page for details +on all those options. + diff --git a/docs/sources/installation/index.md b/docs/sources/installation/index.md index 3f4ebf12118..4956fa81809 100644 --- a/docs/sources/installation/index.md +++ b/docs/sources/installation/index.md @@ -5,88 +5,28 @@ page_keywords: grafana, installation, documentation --- # Installation + Grafana is easily installed via a Debian/Ubuntu package (.deb), via Redhat/Centos package (.rpm) or manually via a tar that contains all required files and binaries. If you can't find a package or binary for your platform you might be able to build one your self, read [build from source](../project/building_from_source) instructions for more information. -## Ubuntu & Debian -Start by [downloading](http://grafana.org/download/builds) the latest `.deb` package. - -To install the package: - - $ sudo dpkg -i grafana_latest_amd64.deb - -## Redhat & Centos -Start by [downloading](http://grafana.org/download/builds) the latest `.rpm` package. - - $ sudo rpm -Uvh grafana-latest-1.x86_64.rpm - -On Redhat/RHEL 5.10 you need to add the grafana user before executing the above. -Execute this to add a grafana user: - - sudo useradd -r grafana - -### Package details -The `.deb` and the `rpm` package install will do the following - -- Install binaries and frontend files under `/opt/grafana/versions/` -- Symlink dir `/opt/grafana/current` to `/opt/grafana/versions/` -- Symlink `/etc/init.d/grafana` to `/opt/grafana/current/scripts/init.sh` -- Add config file to `/etc/grafana/grafana.ini` , this is where you specify your config settings -- Default configuration is in `/opt/grafana/current/conf/defaults.ini`, do not modify that file -- The default configuration specifies log file at `/var/log/grafana/grafana.log` -- The default configuration specifies sqlite3 db at `/opt/grafana/data/grafana.db` - -### Start the backend & web server - -- Start grafana by `sudo service grafana start` -- This will start the grafana process as the `grafana` user (created during package install) -- Default http port is `3000`, and default user is admin/admin - -## Manual install from tar file -Start by [downloading](http://grafana.org/download/builds) the latest `.tar.gz` file and extract it. -This will extract into a folder named after the version you downloaded. This folder contains all files required to run grafana. -There are no init scripts or install scripts in this package. - -To configure grafana add a config file named `custom.ini` to the `conf` folder and override any of the settings defined in -`conf/defaults.ini`. Start grafana by excecuting `./grafana web`. The grafana binary needs the working directory -to be the root install dir (where the binary is and the public folder is located). - -## Dependencies -There are no dependencies with the default configuration. You can switch from a sqlite3 database to mysql or postgres but -that is optional. For small to medium setups sqlite3 should suffice. - -## Install using provisioning -If you prefer to install grafana via Puppet, Ansible, Docker or Chef. [This page](provisioning) has compiled a -list of repositories for different provisioning systems - -## Install from offical docker image - -Grafana has an offical docker container. - - $ docker run -i -p 3000:3000 grafana/grafana:develop - -All grafana configuration settings can be defined using ENVIRONMENT variables, this is especially useful when using the -above container. - -### Docker volumes & ENV config - -The docker container exposes two volumes, the sqlite3 database in the folder `/opt/grafana/data` and -configuration files in the `/opt/grafana/conf` folder. You can map these volumes to host folders when you start the container: - - $ docker run -d -p 3000:3000 \ - -v /var/grafana/data:/opt/grafana/data \ - -e "GF_SECURITY_ADMIN_PASSWORD=secret \ - grafana/grafana:develop - -In the above example I map the data folder and set a config option via an `ENV` variable. +- [Installing on Debian / Ubuntu](debian.md) +- [Installing on RPM-based Linux (CentOS, Fedora, OpenSuse, RedHat)](rpm.md) +- [Installing on Mac OS X](mac.md) +- [Installing on Windows](windows.md) +- [Installing on Docker](docker.md) +- [Installing using Provisioning (Chef, Puppet, Salt, Ansible, etc)](provisioning.md) +- [Nightly Builds](http://grafana.org/download/builds.html) ## Configuration The backend web server has a number of configuration options. Go the [Configuration](configuration) page for details on all those options. - - +## Adding data sources + +- [Graphite](../datasources/graphite.md) +- [InfluxDB](../datasources/influxdb.md) +- [OpenTSDB](../datasources/opentsdb.md) diff --git a/docs/sources/installation/mac.md b/docs/sources/installation/mac.md new file mode 100644 index 00000000000..a5703b4ed7e --- /dev/null +++ b/docs/sources/installation/mac.md @@ -0,0 +1,12 @@ +--- +page_title: Installing on Mac OS X +page_description: Grafana Installation guide for Mac OS X +page_keywords: grafana, installation, mac, osx, guide +--- + +# Installing on Mac + +There are currently no binary build for Mac. But read the [build from source](../project/building_from_source) +page for instructions on how to build it yourself. + + diff --git a/docs/sources/installation/migrating_to2.md b/docs/sources/installation/migrating_to2.md index f2ac8f8bc64..b3d4dab62ce 100644 --- a/docs/sources/installation/migrating_to2.md +++ b/docs/sources/installation/migrating_to2.md @@ -6,42 +6,56 @@ page_keywords: grafana, installation, migration, documentation # Migrating from v1.x to v2.x -Grafana 2.x is pretty different from v1.x in that Grafana 2.x has its own backend and its own -database to store dashboards and users in. +Grafana 2.0 represents a major update to Grafana. It brings new capabilities, many of which are enabled by its new backend server and integrated database. + +The new backend lays a solid foundation that we hope to build on over the coming months. For the 2.0 release, it enables authentication as well as server-side sharing and rendering. + +We've attempted to provide a smooth migration path for V1.9 users to migrate to Grafana 2.0. ## Adding Data sources -Data sources in Grafana v2.0 are no longer configured via the `config.js` file. That config file is no more. -You add data sources via UI or via the [HTTP API](../reference/http_api.md). Go the `Data Sources` view via the side menu. -The side menu can be toggled via the Grafana icon in the top header (to the right). +The config.js file has been deprecated. Data sources are now managed via the UI or [HTTP API](../reference/http_api.md). Manage your organizations data sources by clicking on the `Data Sources` menu on the side menu (which can be toggled via the Grafana icon in the upper left of your browser). -## Importing dashboards +From here, you can add any Graphite, InfluxDB, elasticsearch, and OpenTSDB datasources that you were using with Grafana 1.x. Grafana 2.0 can be configured to communicate with your datasource using a backend mode which can eliminate many CORS-related issues, as well as provide more secure authentication to your datasources. -### From Elasticsearch -Start by going to the `Data Sources` view and add your elasticsearch datasource. Specify the elasticsearch -index name where your Grafana v1.x dashboards are stored, default is `grafana-dash`. +> *Note* When you add your data sources please name them exacly as you named them in config.js in Grafana 1.x. That name is referenced by panels +> , annotation and template queries. That way when you import your old dashboard they will work without any changes. + +## Importing your existing dashboards + +Grafana 2.0 now has integrated dashboard storage engine that can be configured to use an internal sqlite database, MySQL, or Postgres. This eliminates the need to use Elasticsearch for dashboard storage for Graphite users. Grafana 2.0 does not support storing dashboards in InfluxDB. + +You can seamlessly import your existing dashboards. + +### dashboards from Elasticsearch + +Start by going to the `Data Sources` view (via the side menu), and make sure your elasticsearch datasource is added. Specify the elasticsearch index name where your existing Grafana v1.x dashboards are stored (default is `grafana-dash`). ![](/img/v2/datasource_edit_elastic.jpg) +### dashboards from InfluxDB -### From InfluxDB - -Start by going to the `Data Sources` view and add your influxdb datasource. Specify the database -name where your Grafana v1.x dashboards are stored, default is `grafana`. - +Start by going to the `Data Sources` view (via the side menu), and make sure your InfluxDB datasource is added. Specify the database name where your Grafana v1.x dashboards are stored, default is `grafana`. ### Go to Import dashboards view -Go to the `Dashboards` view and click on the dashboards search dropdown. At the bottom of the search dropdown -you find the `Import` button. +Go to the `Dashboards` view and click on the dashboards search dropdown. Click the `Import` button at the bottom of the search dropdown. ![](/img/v2/dashboard_import.jpg) - ### Import view -In the Import view you find the section `Migrate dashboards`. Pick the datasource you added (Elasticsearch or InfluxDB) +In the Import view you find the section `Migrate dashboards`. Pick the datasource you added (from elasticsearch or InfluxDB), and click the `Import` button. ![](/img/v2/migrate_dashboards.jpg) +Your dashboards should be automatically imported into the Grafana 2.0 backend. + +Dashboards will no longer be stored in your previous elasticsearch or InfluxDB databases. + +### Invite your team + +Explain users and orgs. + +### Enjoy the new features diff --git a/docs/sources/installation/performance.md b/docs/sources/installation/performance.md index 535cf72a228..ce41e7a0548 100644 --- a/docs/sources/installation/performance.md +++ b/docs/sources/installation/performance.md @@ -11,6 +11,6 @@ page_keywords: grafana, performance, documentation Graphite 0.9.13 adds a much needed feature to the json rendering API that is very important for Grafana. If you are experiance slow load & rendering times for large time ranges then it is most likely caused by running Graphite 0.9.12 or lower. The latest version of Graphite adds a maxDataPoints parameter to the json render API, without this feature Graphite can return hundreds of thousands of data points per graph, which -can hang your browser. Be sue to upgrade to [0.9.13](http://graphite.readthedocs.org/en/latest/releases/0_9_13.html). +can hang your browser. Be sure to upgrade to [0.9.13](http://graphite.readthedocs.org/en/latest/releases/0_9_13.html). diff --git a/docs/sources/installation/rpm.md b/docs/sources/installation/rpm.md new file mode 100644 index 00000000000..9b7dc0ed9ed --- /dev/null +++ b/docs/sources/installation/rpm.md @@ -0,0 +1,103 @@ +--- +page_title: Installing on RPM-based Linux +page_description: Grafana Installation guide for Centos, Fedora, Redhat. +page_keywords: grafana, installation, centos, fedora, opensuse, redhat, guide +--- + +# Installing on RPM-based Linux (CentOS, Fedora, OpenSuse, RedHat) + +## Download + +Description | Download +------------ | ------------- +.RPM for Fedora / RHEL / CentOS Linux | [grafana-2.0.2-1.x86_64.rpm](https://grafanarel.s3.amazonaws.com/builds/grafana-2.0.2-1.x86_64.rpm) + +## Install +You can install using yum + + $ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-2.0.2-1.x86_64.rpm + +Or manually using `rpm` + + $ sudo yum install initscripts fontconfig + $ sudo rpm -Uvh grafana-2.0.1-1.x86_64.rpm + +## YUM Repository + +Add the following to a new file at `/etc/yum.repos.d/grafana.repo` + + [grafana] + name=grafana + baseurl=https://packagecloud.io/grafana/stable/el/6/$basearch + repo_gpgcheck=1 + enabled=1 + gpgcheck=1 + gpgkey=https://packagecloud.io/gpg.key https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana + sslverify=1 + sslcacert=/etc/pki/tls/certs/ca-bundle.crt + +There is also testing repository if you want beta or release candidates. + + baseurl=https://packagecloud.io/grafana/testing/el/6/$basearch + +Install Grafana + + $ sudo yum install grafana + +### RPM GPG Key +The rpms are signed, you can verify the signature with this [public GPG key](https://grafanarel.s3.amazonaws.com/RPM-GPG-KEY-grafana). + +## Package details + +- Installs binary to `/usr/sbin/grafana-server` +- Init.d script to `/etc/init.d/grafana-server` +- Default file (environment vars) to `/etc/sysconfig/grafana-server` +- Configuration file to `/etc/grafana/grafana.ini` +- Systemd service (if systemd is available) name `grafana-server.service` +- The default configuration specifies log file at `/var/log/grafana/grafana.log` +- The default configuration specifies sqlite3 db at `/var/lib/grafana/grafana.db` + +## Start the server (init.d service) + +- Start grafana by `sudo service grafana-server start` +- This will start the grafana-server process as the `grafana` user (created during package install) +- Default http port is `3000`, and default user is admin/admin +- To configure grafana server to start at boot time: `sudo /sbin/chkconfig --add grafana-server` + +## Start the server (via systemd) + + $ systemctl daemon-reload + $ systemctl start grafana-server + $ systemctl status grafana-server + +### Enable the systemd service (so grafana starts at boot) + + sudo systemctl enable grafana-server.service + +## Environment file + +The systemd service file and init.d script both use the file located at `/etc/sysconfig/grafana-server` for +environment variables used when starting the backend. Here you can override log directory, data directory and other +variables. + +### Logging + +By default grafana will log to `/var/log/grafana` + +### Database + +The default configuration specifies a sqlite3 database located at `/var/lib/grafana/grafana.db`. Please backup +this database before upgrades. You can also use mysql or postgres as the Grafana database. + +## Configuration + +The configuration file is located at `/etc/grafana/grafana.ini`. Go the [Configuration](configuration) page for details +on all those options. + +### Adding data sources + +- [Graphite](../datasources/graphite.md) +- [InfluxDB](../datasources/influxdb.md) +- [OpenTSDB](../datasources/opentsdb.md) + + diff --git a/docs/sources/installation/windows.md b/docs/sources/installation/windows.md new file mode 100644 index 00000000000..5bce40b3233 --- /dev/null +++ b/docs/sources/installation/windows.md @@ -0,0 +1,33 @@ +--- +page_title: Installing on Windows +page_description: Grafana Installation guide for Windows +page_keywords: grafana, installation, windows guide +--- + +# Installing on Windows + +## Download + +Description | Download +------------ | ------------- +Zip package for Windows | [grafana.2.0.2.windows-x64.zip](https://grafanarel.s3.amazonaws.com/winbuilds/dist/grafana-2.0.2.windows-x64.zip) + +## Configure +The zip file contains a folder with the current grafana version. Extract this folder to anywhere you want Grafana to run from. +Go into the `conf` directory and copy `sample.ini` to `custom.ini`. You should edit `custom.ini`, never `defaults.ini`. + +The default grafana port is `3000`, this port requires extra permissions on windows. Edit `custom.ini` and uncomment the `http_port` +config and change it to something like `8080` or similar. That port should not require extra windows privileges. + +Start grafana by executing `grafana-server.exe`, preferbly from the command line. If you want to run Grafana as +windows service, download [NSSM](https://nssm.cc/). It is very easy add grafana as windows service using that tool. + +Read more about the [configuration options](configuration.md). + +## Building on Windows + +The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on windows you need +to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download). + +Copy conf/sample.ini to a file named conf/custom.ini and change the web server port to something like 8080. The default +Grafana port(3000) requires special privileges on Windows. diff --git a/docs/sources/project/building_from_source.md b/docs/sources/project/building_from_source.md index 94e7dd087d7..8466dbd2736 100644 --- a/docs/sources/project/building_from_source.md +++ b/docs/sources/project/building_from_source.md @@ -14,24 +14,24 @@ dev environment. - Go 1.4 - NodeJS - ## Get Code ``` go get github.com/grafana/grafana ``` -The above will give an error saying there is no go code. That is because the new backend parts are in the develop branch. - ## Building the backend ``` cd $GOPATH/src/github.com/grafana/grafana -git checkout -t origin/develop go run build.go setup (only needed once to install godep) godep restore (will pull down all golang lib dependecies in your current GOPATH) go build . ``` +# Building on Windows +The Grafana backend includes Sqlite3 which requires GCC to compile. So in order to compile Grafana on windows you need +to install GCC. We recommend [TDM-GCC](http://tdm-gcc.tdragon.net/download). + ## Building frontend assets To build less to css for the frontend you will need a recent version of of node (v0.12.0), @@ -52,19 +52,23 @@ bra run ## Running ``` -./bin/grafana web +./grafana-server ``` Open grafana in your browser (default http://localhost:3000) and login with admin user (default user/pass = admin/admin). +## Creating optimized release packages +``` +go run build.go build package +``` + ## Dev config Create a custom.ini in the conf directory to override default configuration options. You only need to add the options you want to override. Config files are applied in the order of: 1. grafana.ini -2. dev.ini (if found) -3. custom.ini +2. custom.ini ## Create a pull requests diff --git a/docs/sources/reference/admin.md b/docs/sources/reference/admin.md index cc0548f0ac6..854147b4240 100644 --- a/docs/sources/reference/admin.md +++ b/docs/sources/reference/admin.md @@ -1,10 +1,36 @@ ---- -page_title: Grafana Admin -page_description: Grafana Admin guide +page_title: Administration +page_description: Grafana Administration page_keywords: grafana, admin, administration, documentation --- -# Grafana Admin +# Administration -This documentation page has yet to be written. +Grafana has two levels of administrators: +* Organizational administrators: These admins can manage users within specific organizations in a particular Grafana installation +* Grafana administrators: These super admins can manage users across all organizations in a Grafana installation. They can also change and access system-wide settings. + +## Organizational Administrators + +As an Organizational administrator, you can add `Data Sources`, add Users to your Organization and +modify Organization details and options. + +> *Note*: If Grafana is configured with `users.allow_org_create = true`, any User of any Organization will be able to +> start their own Organization and become the administrator of that Organization. + + +## Grafana Administrators + + +As a Grafana Administrator, you have complete access to any Organization or User in that instance of Grafana. +When performing actions as a Grafana admin, the sidebar will change it's apperance as below to indicate you are performing global server administration. + +From the Grafana Server Admin page, you can access the System Info page which summarizes all of the backend configuration settings of the Grafana server. + +## Why would I have multiple Organizations? + +In many cases, a Grafana installation will only have one Organization. There's no need to create multiple Organizations +if you want all your users to have access to the same set of dashboards and data. In a multitenant deployment, +Organizations can be used to provide a full Grafana experience to different sets of users from a single Grafana instance, +at the convenience of the Grafana Administrator. diff --git a/docs/sources/reference/annotations.md b/docs/sources/reference/annotations.md index 37bf84cb891..41d7bf411a3 100644 --- a/docs/sources/reference/annotations.md +++ b/docs/sources/reference/annotations.md @@ -10,38 +10,31 @@ page_keywords: grafana, annotations, guide, documentation Annotations provide a way to mark points on the graph with rich events. When you hover over an annotation you can get title, tags, and text information for the event. -To enable annotations open dashboard settings and the controls tab. -Under feature toggles you will find the checkbox for annotations. +To add an annotation query click dashboard settings icon in top menu and select `Annotations` from the +dropdown. This will open the `Annotations` edit view. Click the `Add` tab to add a new annotation query. -When enabled they will appear in the sub menu controls area. -![](/img/v1/annotations_submenu1.png) +### Graphite annotations -Click the cog wheel to open the dialog where you can add & edit annotations. -![](/img/v1/annotations_dialog1.png) +Graphite supports two ways to query annotations. -## Datasources -Grafana supports many data sources for annotation. +- A regular metric query, use the `Graphite target expression` text input for this +- Graphite events query, use the `Graphite event tags` text input, especify an tag or wildcard (leave empty should also work) -- Graphite metrics -- Graphite events -- InfluxDB query -- Elasticsearch query +## Elasticsearch annoations +![](/img/v2/annotations_es.png) + +Grafana can query any Elasticsearch index for annotation events. The index name can be the name of an alias or an index wildcard pattern. +You can leave the search query blank or specify a lucene query. + +If your elasticsearch document has a timestamp field other than `@timestamp` you will need to specify that. As well +as the name for the fields that should be used for the annotation title, tags and text. Tags and text are optional. + +> **Note** The annotation timestamp field in elasticsearch need to be in UTC format. ## InfluxDB Annotations -![](/img/influxdb/influxdb_annotation.png) +![](/img/v2/annotations_influxdb.png) For InfluxDB you need to enter a query like in the above screenshot. You need to have the ```where $timeFilter``` part. If you only select one column you will not need to enter anything in the column mapping fields. If you have multiple columns you need to specify which column should be treated as title, tags and text column. -## Elasticsearch Annotations -![](/img/v1/elasticsearch_annotations_edit.png) - -You can use the same data source as you specified in config.js for storing grafana dashboards or you can specify another one. -The annotation definition contains an index name that will override the index name specified in config.js. The index name can -be the name of an alias or an index wildcard pattern. You can leave the search query blank or specify a lucene query. - -If your elasticsearch document has a timestamp field other than ```@timestamp``` you will need to specify that. As well -as the name for the fields that should be used for the annotation title, tags and text. Tags and text are optional. - -**The annotation timestamp field in elasticsearch need to be in UTC format** diff --git a/docs/sources/reference/export_import.md b/docs/sources/reference/export_import.md index bfcdd7bd0d1..8ae39cb0cf2 100644 --- a/docs/sources/reference/export_import.md +++ b/docs/sources/reference/export_import.md @@ -6,45 +6,5 @@ page_keywords: grafana, export, import, documentation # Export and Import -## Export (Json file) - -Load the dashboard you wish to export (e.g. via search) - -Click on the "Save" icon (floppy disk) - -![](/img/v1/export_import_save_menu.png) - -Click on "Export schema" - -![](/img/v1/export_schema_link.png) - -Save file on local disk - -![](/img/v1/export_save_file.png) - -## Import - -Click the "Search" icon (open folder) - -![](/img/v1/export_import_search_menu.png) - -Click "Import" button - -![](/img/v1/export_import_button.png) - -Click "Browse" button and browse to previously exported dashboard on local disk (which should be a valid Json file) - -![](/img/v1/export_import_browse_button.png) - -Select exported dashboard - -![](/img/v1/export_import_popup.png) - - -Click "Save" icon to show save menu - -![](/img/v1/export_import_save_menu.png) - -Click new save icon (floppy disk) to Save newly imported dashboard - -![](/img/v1/export_import_save_dashboard.png) +You find the import view in the bottom of the search dropdown. From this view you +can import local json files or migrate dashboards stored in Elasticsearch or InfluxDB. diff --git a/docs/sources/reference/http_api.md b/docs/sources/reference/http_api.md index b7ef92e0ba8..f02741cb37b 100644 --- a/docs/sources/reference/http_api.md +++ b/docs/sources/reference/http_api.md @@ -6,5 +6,147 @@ page_keywords: grafana, admin, http, api, documentation # HTTP API Reference -This documentation page has yet to be written. +The Grafana backend exposes an HTTP API, the same API is used by the frontend to do everything from saving +dashboards, creating users and updating data sources. + +## Authorization + +Currently you can authenticate via an `API Token` or via a `Session cookie` (acquired using regular login or oauth). + +### Create API Token + +Open the sidemenu and click the organization dropdown and select the `API Keys` option. + +![](/img/v2/orgdropdown_api_keys.png) + +You use the token in all requests in the `Authorization` header, like this: + +**Example**: + + GET http://your.grafana.com/api/dashboards/db/mydash HTTP/1.1 + Accept: application/json + Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +The `Authorization` header value should be `Bearer `. + +## Dashboards + +### Create / Update dashboard + +`POST /api/dashboards/db` + +Creates a new dashboard or updates an existing dashboard. + +**Example Request for new dashboard**: + + POST /api/dashboards/db HTTP/1.1 + Accept: application/json + Content-Type: application/json + Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + + { + "dashboard": { + "id": null, + "title": "Production Overview", + "tags": [ "templated" ], + "timezone": "browser", + "rows": [ + { + } + ] + "schemaVersion": 6, + "version": 0 + }, + "overwrite": false + } + +JSON Body schema: + +- **dashboard** – The complete dashboard model, id = null to create a new dashboard +- **overwrite** – Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title. + +**Example Response**: + + HTTP/1.1 200 OK + Content-Type: application/json; charset=UTF-8 + Content-Length: 78 + + { + "slug": "production-overview", + "status": "success", + "version": 1 + } + +Status Codes: + +- **200** – Created +- **400** – Errors (invalid json, missing or invalid fields, etc) +- **401** – Unauthorized +- **412** – Precondition failed + +The **412** status code is used when a newer dashboard already exists (newer, its version is greater than the verison that was sent). The +same status code is also used if another dashboar exists with the same title. The response body will look like this: + + HTTP/1.1 412 Precondition Failed + Content-Type: application/json; charset=UTF-8 + Content-Length: 97 + + { + "message": "The dashboard has been changed by someone else", + "status": "version-mismatch" + } + +In in case of title already exists the `status` property will be `name-exists`. + +### Get dashboard + +`GET /api/dashboards/db/:slug` + +Will return the dashboard given the dashboard slug. Slug is the url friendly version of the dashboard title. + +**Example Request**: + + GET /api/dashboards/db/production-overview HTTP/1.1 + Accept: application/json + Content-Type: application/json + Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk + +**Example Response**: + + HTTP/1.1 200 + Content-Type: application/json + + { + "meta": { + "isStarred": false, + "slug": "production-overview" + }, + "dashboard": { + "id": null, + "title": "Production Overview", + "tags": [ "templated" ], + "timezone": "browser", + "rows": [ + { + } + ] + "schemaVersion": 6, + "version": 0 + }, + } + +### Delete dashboard + +`DELETE /api/dashboards/db/:slug` + +The above will delete the dashboard with the specified slug. The slug is the url friendly (unique) version of the dashboard title. + +## Data sources + +### Create data source + +## Organizations + +## Users + diff --git a/docs/sources/reference/playlist.md b/docs/sources/reference/playlist.md index 0f81bfe48fc..29c7717939c 100644 --- a/docs/sources/reference/playlist.md +++ b/docs/sources/reference/playlist.md @@ -4,30 +4,31 @@ page_description: Playlist guide for Grafana page_keywords: grafana, playlist, documentation --- -# Playlist Guide +## About the Playlist -In Grafana v1.5 a playlist feature was added. You can use this feature by first marking a couple - of dashboards as favorites. This is accomplished in the Save menu. Click on the save icon and then _Mark As Favorite_. +The Playlist is a special type of Dashboard that rotates through a particular list of two or more Dashboards. They can be a great way to build situational awareness or just show off your metrics to your team or visitors. Since Dashboards in Grafana automatically scale to any resolution they're perfect for big screens! +## Configuring the Playlist -## Step 1 - Mark as favorire -![](/img/v1/mark_as_favorite.png) +The Playlist can be accessed from the main Dashboard picker. Click the 'Playlist' button at the bottom of the picker to access the Playlist functionality. -Then open the playlist modal. You will find this button in the open dashboard / search popup (CTRL+F). +![](/img/v2/dashboard_search.jpg) -## Step 2 - Open playlist view -![](/img/v1/playlist_button.png) +Since the Playlist is basically a list of Dashboards, ensure that all the Dashboards you want to appear in your Playlist are added here. You can search Dashboards by name (or use a regular expression). -This opens the playlist view. +You can search Dashboards by name (or use a regular expression), and add them to your Playlist. By default, your starred dashboards will appear as candidates for the Playlist. -## Step 3 - Select dashbords and interval +Be sure to click the right arrow appearing next to the Dashboard name to add it to the Playlist. -![](/img/v1/playlist_modal.png) +You can configure a time interval for Grafana to wait on a particular Dashboard before advancing to the next one on the Playlist. -In this view you can select the dashboards you want to include in the playlist, and even remove dashboards from you favorites list. Set the time span between dashboard change, for example 1m, 10m, 1h for 1 minute, 10 minute or 1 hour interval. +## Starting and controlling the Playlist -To start the dashboard click the Start button. +To start the Playlist, click the green "Start" button -When a dashboard playlist is running, most menu buttons and dashboard controls are hidden. This is in order to present as clear a view of the dashboard as possible for big tv displays. Click the stop playlist link in the top menu to the right to stop the playlist. +Playlists can also be manually controlled utilizing the Playlist controls at the top of screen when in Playlist mode. + +Click the stop button to stop the Playlist, and exit to the current Dashboard. +Click the next button to advance to the next Dashboard in the Playlist. +Click the back button to rewind to the previous Dashboard in the Playlist. -![](playlist_playing_hiden_menu.png) diff --git a/docs/sources/reference/scripting.md b/docs/sources/reference/scripting.md index 6e8c25c0196..45158eeadcd 100644 --- a/docs/sources/reference/scripting.md +++ b/docs/sources/reference/scripting.md @@ -8,8 +8,9 @@ page_keywords: grafana, scripted, guide, documentation If you have lots of metric names that change (new servers etc) in a defined pattern it is irritating to constantly have to create new dashboards. -With scripted dashboards you can dynamically create your dashboards using javascript. In the folder grafana install folder _app/dashboards/_ there is a file named _scripted.js_. This file contains an example of a scripted dashboard. You can access it by using the url: -> http://grafana_url/#/dashboard/script/scripted.js?rows=3&name=myName +With scripted dashboards you can dynamically create your dashboards using javascript. In the folder grafana install folder +under `public/dashboards/` there is a file named `scripted.js`. This file contains an example of a scripted dashboard. You can access it by using the url: +`http://grafana_url/dashboard/script/scripted.js?rows=3&name=myName` If you open scripted.js you can see how it reads url paramters from ARGS variable and then adds rows and panels. @@ -58,4 +59,4 @@ return dashboard; ## More examples -You can find more examples in `app/dashboards/` directory of your grafana installation. +You can find more examples in `public/dashboards/` directory of your grafana installation. diff --git a/docs/sources/reference/sharing.md b/docs/sources/reference/sharing.md new file mode 100644 index 00000000000..16ddcd8f01b --- /dev/null +++ b/docs/sources/reference/sharing.md @@ -0,0 +1,45 @@ +---- +page_title: Sharing +page_description: Sharing +page_keywords: grafana, sharing, guide, documentation +--- + +# Sharing features +Grafana provides a number of ways to share a dashboard or a specfic panel to other users within your +organization. It also provides ways to publish interactive snapshots that can be accessed by external partners. + +## Share dashboard +Share a dashboard via the share icon in the top nav. This opens the share dialog where you +can get a link to the current dashboard with the current selected time range and template variables. If you have +made changes to the dashboard, make sure those are saved before sending the link. + +### Dashboard snapshot + +A dashboard snapshot is an instant way to share an interactive dashboard publicly. When created, we strip sensitive data like queries +(metric, template and annotation) and panel links, leaving only the visible metric data and series names embedded into your dashboard. Dashboard +snapshots can be accessed by anyone who has the link and can reach the URL. + +![](/img/v2/dashboard_snapshot_dialog.png) + +### Publish snapshots +You can publish snapshots to you local instance or to [snapshot.raintank.io](http://snapshot.raintank.io). The later is a free service +that is provided by [Raintank](http://raintank.io) that allows you to publish dashboard snapshots to an external grafana instance. +The same rules still apply, anyone with the link can view it. You can set an expiration time if you want the snapshot to be removed +after a certain time period. + +## Share Panel +Click a panel title to open the panel menu, then click share in the panel menu to open the Share Panel dialog. Here you +have access to a link that will take you to exactly this panel with the current time range and selected template variables. +You also get a link to service side rendered PNG of the panel. Useful if you want to shara image of the panel. + +### Embed Panel +You can embed a panel using an iframe on another web site. This tab will show you the html that you need to use. + +Example: + +```html + +``` + +Below there should be an interactive Grafana graph embedded in an iframe: + diff --git a/docs/sources/reference/singlestat.md b/docs/sources/reference/singlestat.md index 54085c907bb..8ca68dd82b6 100644 --- a/docs/sources/reference/singlestat.md +++ b/docs/sources/reference/singlestat.md @@ -6,4 +6,10 @@ page_keywords: grafana, singlestat, panel, documentation # Singlestat Panel +![](/img/v1/singlestat_panel2.png) +The singlestat panel allows you to show the one main summery stat of a single series (like max, min, avg, sum). It also +provides thresholds to color that singlestat metric or the panel background. + +## Options +- TODO diff --git a/docs/sources/reference/timerange.md b/docs/sources/reference/timerange.md index 9c2fa634fae..47c9186e119 100644 --- a/docs/sources/reference/timerange.md +++ b/docs/sources/reference/timerange.md @@ -4,23 +4,30 @@ page_description: Time range user guide page_keywords: grafana, time range, guide, documentation --- -# Time range controls +Grafana provides numerous ways to manage the time ranges of the data being visualized, both at the Dashboard-level and the Panel-level. + +# Dashboard time picker ![](/img/v1/time_range_controls.png) -In the menu you have the time range dropdown (between to Zoom out and refresh links). -In this dropdown you have relative time options, auto refresh options and custom time options. +In the top right, you have the master Dashboard time picker (it's inbetween the 'Zoom out' and the 'Refresh' links). +From this dropdown you can: -## Custom time +1. Specify an exact time range (eg. "October 13 12:01 to October 14 12:05) +2. Choose a relative time (eg. "Last 15 minutes","Last 1 week") +3. Configure auto-refresh options -To customize the relative and auto refresh options open the dashboard settings and click the Timepicker tab. -In this tab you can specify the relative and auto refresh intervals. The Timepicker tab settings are saved -on per dashboard basis. +All of this applies to all Panels in the Dashboard (except those with Panel Time Overrides enabled) + +## Customize relative time and auto auto-refresh options + +It's possible to customize the options displayed for relative time and the auto-refresh options. + +From Dashboard setttings, click the Timepicker tab. From here you can specify the relative and auto refresh intervals. The Timepicker tab settings are saved on a per Dashboard basis. Entries are comma seperated and accept a number followed by one of the following units: s (seconds), m (minutes), h (hours), d (days), w (weeks), M (months), y (years). ![](/img/v1/timepicker_editor.png) -## Time picker options +## Panel time override -In dashboard settings, `Time Picker` tab you can add or remove the relative time intervals and refresh intervals -that should be available in the time picker dropdown. +In Grafana 2.0, it's now possible for individual Panels to override the Dashboard time picker. Please check out the [whats new in 2.0 guide](../guides/whats-new-in-v2/) for further information diff --git a/latest.json b/latest.json index 90189fabeca..a6613c11634 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,3 @@ { - "version": "1.9.1", - "url": "http://grafanarel.s3.amazonaws.com/grafana-1.9.1.tar.gz" + "version": "2.0.1", } diff --git a/main.go b/main.go index f23a544e1e9..ffe7e3d7cd3 100644 --- a/main.go +++ b/main.go @@ -1,22 +1,33 @@ package main import ( + "flag" + "io/ioutil" "os" "os/signal" + "path/filepath" "runtime" "strconv" + "time" "github.com/grafana/grafana/pkg/cmd" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/metrics" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/services/eventpublisher" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" - - "github.com/codegangsta/cli" + "github.com/grafana/grafana/pkg/social" ) var version = "master" var commit = "NA" var buildstamp string +var configFile = flag.String("config", "", "path to config file") +var homePath = flag.String("homepath", "", "path to grafana install/home path, defaults to working directory") +var pidFile = flag.String("pidfile", "", "path to pid file") + func init() { runtime.GOMAXPROCS(runtime.NumCPU()) } @@ -35,33 +46,53 @@ func main() { os.Exit(0) }() - app := cli.NewApp() - app.Name = "Grafana Backend" - app.Usage = "grafana web" - app.Version = version - app.Commands = []cli.Command{ - cmd.ListOrgs, - cmd.CreateOrg, - cmd.DeleteOrg, - cmd.ExportDashboard, - cmd.ImportDashboard, - cmd.ListDataSources, - cmd.CreateDataSource, - cmd.DescribeDataSource, - cmd.DeleteDataSource, - cmd.Web} - app.Flags = append(app.Flags, []cli.Flag{ - cli.StringFlag{ - Name: "config", - Usage: "path to grafana.ini config file", - }, - cli.StringFlag{ - Name: "pidfile", - Usage: "path to pidfile", - }, - }...) + flag.Parse() - app.Run(os.Args) + initRuntime() + writePIDFile() + + social.NewOAuthService() + eventpublisher.Init() + plugins.Init() + + if setting.ReportingEnabled { + go metrics.StartUsageReportLoop() + } + + cmd.StartServer() log.Close() } + +func initRuntime() { + setting.NewConfigContext(&setting.CommandLineArgs{ + Config: *configFile, + HomePath: *homePath, + Args: flag.Args(), + }) + + log.Info("Starting Grafana") + log.Info("Version: %v, Commit: %v, Build date: %v", setting.BuildVersion, setting.BuildCommit, time.Unix(setting.BuildStamp, 0)) + setting.LogConfigurationInfo() + + sqlstore.NewEngine() + sqlstore.EnsureAdminUser() +} + +func writePIDFile() { + if *pidFile == "" { + return + } + + // Ensure the required directory structure exists. + err := os.MkdirAll(filepath.Dir(*pidFile), 0700) + if err != nil { + log.Fatal(3, "Failed to verify pid directory", err) + } + + // Retrieve the PID and write it. + pid := strconv.Itoa(os.Getpid()) + if err := ioutil.WriteFile(*pidFile, []byte(pid), 0644); err != nil { + log.Fatal(3, "Failed to write pidfile", err) + } +} diff --git a/package.json b/package.json index dfaa81c7eb9..8785632eaee 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "2.0.0-prebeta2", + "version": "2.1.0-pre1", "repository": { "type": "git", "url": "http://github.com/torkelo/grafana.git" @@ -62,7 +62,7 @@ }, "license": "Apache License", "dependencies": { - "grunt-jscs": "^0.8.1", + "grunt-jscs": "~1.5.x", "karma-sinon": "^1.0.3", "lodash": "^2.4.1", "sinon": "^1.10.3" diff --git a/packaging/deb/control/postinst b/packaging/deb/control/postinst new file mode 100755 index 00000000000..edb163ba7fb --- /dev/null +++ b/packaging/deb/control/postinst @@ -0,0 +1,71 @@ +#!/bin/sh + +set -e + +[ -f /etc/default/grafana-server ] && . /etc/default/grafana-server + +startGrafana() { + if [ -x /bin/systemctl ] ; then + /bin/systemctl daemon-reload + /bin/systemctl start grafana.service + elif [ -x "/etc/init.d/grafana" ]; then + if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then + invoke-rc.d grafana start || true + else + /etc/init.d/grafana start || true + fi + fi +} + +case "$1" in + configure) + [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" + [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" + if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then + addgroup --system "$GRAFANA_GROUP" --quiet + fi + if ! id $GRAFANA_USER > /dev/null 2>&1 ; then + adduser --system --home /usr/share/grafana --no-create-home \ + --ingroup "$GRAFANA_GROUP" --disabled-password --shell /bin/false \ + "$GRAFANA_USER" + fi + + if [ -x /opt/grafana/ ]; then + echo "### Upgrading Notice ### " + echo "-- New grafana install home is /usr/share/grafana" + echo "-- Please move sqlite3 database to /var/lib/grafana/" + echo "-- Notice: service name && binary changed to grafana-server" + fi + + # Set user permissions on /var/log/grafana, /var/lib/grafana + mkdir -p /var/log/grafana /var/lib/grafana + chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana + chmod 755 /var/log/grafana /var/lib/grafana + + # configuration files should not be modifiable by grafana user, as this can be a security issue + chown -Rh root:root /etc/grafana/* + chmod 755 /etc/grafana + find /etc/grafana -type f -exec chmod 644 {} ';' + find /etc/grafana -type d -exec chmod 755 {} ';' + + # if $2 is set, this is an upgrade + if ( [ -n $2 ] && [ "$RESTART_ON_UPGRADE" = "true" ] ) ; then + startGrafana + # this is a fresh installation + elif [ -z $2 ] ; then + if [ -x /bin/systemctl ] ; then + echo "### NOT starting on installation, please execute the following statements to configure grafana to start automatically using systemd" + echo " sudo /bin/systemctl daemon-reload" + echo " sudo /bin/systemctl enable grafana-server.service" + echo "### You can start grafana-server by executing" + echo " sudo /bin/systemctl start grafana-server.service" + + elif [ -x /usr/sbin/update-rc.d ] ; then + echo "### NOT starting grafana-server by default on bootup, please execute" + echo " sudo update-rc.d grafana-server defaults 95 10" + echo "### In order to start grafana-server, execute" + echo " sudo service grafana-server start" + fi + fi + ;; +esac diff --git a/packaging/deb/default/grafana-server b/packaging/deb/default/grafana-server new file mode 100644 index 00000000000..14d2545f6ce --- /dev/null +++ b/packaging/deb/default/grafana-server @@ -0,0 +1,17 @@ +GRAFANA_USER=grafana + +GRAFANA_GROUP=grafana + +GRAFANA_HOME=/usr/share/grafana + +LOG_DIR=/var/log/grafana + +DATA_DIR=/var/lib/grafana + +MAX_OPEN_FILES=10000 + +CONF_DIR=/etc/grafana + +CONF_FILE=/etc/grafana/grafana.ini + +RESTART_ON_UPGRADE=false diff --git a/packaging/deb/init.d/grafana-server b/packaging/deb/init.d/grafana-server new file mode 100755 index 00000000000..6daebdb4331 --- /dev/null +++ b/packaging/deb/init.d/grafana-server @@ -0,0 +1,144 @@ +#! /usr/bin/env bash + +# chkconfig: 2345 80 05 +# description: Grafana web server & backend +# processname: grafana +# config: /etc/grafana/grafana.ini +# pidfile: /var/run/grafana.pid + +### BEGIN INIT INFO +# Provides: grafana +# Required-Start: $all +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start grafana at boot time +### END INIT INFO + +# tested on +# 1. New lsb that define start-stop-daemon +# 3. Centos with initscripts package installed + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +NAME=grafana-server +DESC="Grafana Server" +DEFAULT=/etc/default/$NAME + +GRAFANA_USER=grafana +GRAFANA_GROUP=grafana +GRAFANA_HOME=/usr/share/grafana +CONF_DIR=/etc/grafana +WORK_DIR=$GRAFANA_HOME +DATA_DIR=/var/lib/grafana +LOG_DIR=/var/log/grafana +CONF_FILE=$CONF_DIR/grafana.ini +MAX_OPEN_FILES=10000 +PID_FILE=/var/run/$NAME.pid +DAEMON=/usr/sbin/$NAME + +if [ `id -u` -ne 0 ]; then + echo "You need root privileges to run this script" + exit 1 +fi + +. /lib/lsb/init-functions + +if [ -r /etc/default/rcS ]; then + . /etc/default/rcS +fi + +# overwrite settings from default file +if [ -f "$DEFAULT" ]; then + . "$DEFAULT" +fi + +DAEMON_OPTS="--pidfile=${PID_FILE} --config=${CONF_FILE} cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR}" + +# Check DAEMON exists +test -x $DAEMON || exit 0 + +case "$1" in + start) + + log_daemon_msg "Starting $DESC" + + pid=`pidofproc -p $PID_FILE grafana` + if [ -n "$pid" ] ; then + log_begin_msg "Already running." + log_end_msg 0 + exit 0 + fi + + # Prepare environment + mkdir -p "$LOG_DIR" "$DATA_DIR" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$LOG_DIR" "$DATA_DIR" + touch "$PID_FILE" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$PID_FILE" + + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + + # Start Daemon + start-stop-daemon --start -b --chdir "$WORK_DIR" --user "$GRAFANA_USER" -c "$GRAFANA_USER" --pidfile "$PID_FILE" --exec $DAEMON -- $DAEMON_OPTS + return=$? + if [ $return -eq 0 ] + then + sleep 1 + + # check if pid file has been written two + if ! [[ -s $PID_FILE ]]; then + log_end_msg 1 + exit 1 + fi + + i=0 + timeout=10 + # Wait for the process to be properly started before exiting + until { cat "$PID_FILE" | xargs kill -0; } >/dev/null 2>&1 + do + sleep 1 + i=$(($i + 1)) + if [ $i -gt $timeout ]; then + log_end_msg 1 + exit 1 + fi + done + fi + log_end_msg $return + ;; + stop) + log_daemon_msg "Stopping $DESC" + + if [ -f "$PID_FILE" ]; then + start-stop-daemon --stop --pidfile "$PID_FILE" \ + --user "$GRAFANA_USER" \ + --retry=TERM/20/KILL/5 >/dev/null + if [ $? -eq 1 ]; then + log_progress_msg "$DESC is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + log_failure_msg "Failed to stop $DESC (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + else + log_progress_msg "(not running)" + fi + log_end_msg 0 + ;; + status) + status_of_proc -p $PID_FILE grafana grafana && exit 0 || exit $? + ;; + restart|force-reload) + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; + *) + log_success_msg "Usage: $0 {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/packaging/deb/systemd/grafana-server.service b/packaging/deb/systemd/grafana-server.service new file mode 100644 index 00000000000..f99c96fcb13 --- /dev/null +++ b/packaging/deb/systemd/grafana-server.service @@ -0,0 +1,21 @@ +[Unit] +Description=Starts and stops a single grafana instance on this system +Documentation=http://docs.grafana.org +Wants=network-online.target +After=network-online.target + +[Service] +EnvironmentFile=/etc/default/grafana-server +User=grafana +Group=grafana +Type=simple +WorkingDirectory=/usr/share/grafana +ExecStart=/usr/sbin/grafana-server \ + --config=${CONF_FILE} \ + cfg:default.paths.logs=${LOG_DIR} \ + cfg:default.paths.data=${DATA_DIR} \ +LimitNOFILE=10000 +TimeoutStopSec=20 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/rpm/control/postinst b/packaging/rpm/control/postinst new file mode 100644 index 00000000000..9e5e9accf79 --- /dev/null +++ b/packaging/rpm/control/postinst @@ -0,0 +1,75 @@ +#!/bin/sh + +set -e + +[ -f /etc/sysconfig/grafana-server ] && . /etc/sysconfig/grafana-server + +startGrafana() { + if [ -x /bin/systemctl ] ; then + /bin/systemctl start grafana-server.service + elif [ -x /etc/init.d/grafana-server ] ; then + /etc/init.d/grafana-server start + elif [ -x /etc/rc.d/init.d/grafana-server ] ; then + /etc/rc.d/init.d/grafana-server start + fi +} + +stopGrafana() { + if [ -x /bin/systemctl ] ; then + /bin/systemctl stop grafana-server.service > /dev/null 2>&1 || : + elif [ -x /etc/init.d/grafana-service ] ; then + /etc/init.d/grafana-service stop + elif [ -x /etc/rc.d/init.d/grafana-service ] ; then + /etc/rc.d/init.d/grafana-service stop + fi +} + +# Initial installation: $1 == 1 +# Upgrade: $1 == 2, and configured to restart on upgrade +if [ $1 -eq 1 ] ; then + [ -z "$GRAFANA_USER" ] && GRAFANA_USER="grafana" + [ -z "$GRAFANA_GROUP" ] && GRAFANA_GROUP="grafana" + if ! getent group "$GRAFANA_GROUP" > /dev/null 2>&1 ; then + groupadd -r "$GRAFANA_GROUP" + fi + if ! getent passwd "$GRAFANA_USER" > /dev/null 2>&1 ; then + useradd -r -g grafana -d /usr/share/grafana -s /sbin/nologin \ + -c "grafana user" grafana + fi + + # Set user permissions on /var/log/grafana, /var/lib/grafana + mkdir -p /var/log/grafana /var/lib/grafana + chown -R $GRAFANA_USER:$GRAFANA_GROUP /var/log/grafana /var/lib/grafana + chmod 755 /var/log/grafana /var/lib/grafana + + # configuration files should not be modifiable by grafana user, as this can be a security issue + chown -Rh root:root /etc/grafana/* + chmod 755 /etc/grafana + find /etc/grafana -type f -exec chmod 644 {} ';' + find /etc/grafana -type d -exec chmod 755 {} ';' + + if [ -x /bin/systemctl ] ; then + echo "### NOT starting on installation, please execute the following statements to configure grafana to start automatically using systemd" + echo " sudo /bin/systemctl daemon-reload" + echo " sudo /bin/systemctl enable grafana-server.service" + echo "### You can start grafana-server by executing" + echo " sudo /bin/systemctl start grafana-server.service" + elif [ -x /sbin/chkconfig ] ; then + echo "### NOT starting grafana-server by default on bootup, please execute" + echo " sudo /sbin/chkconfig --add grafana-server" + echo "### In order to start grafana-server, execute" + echo " sudo service grafana-server start" + fi +elif [ $1 -ge 2 ] ; then + if [ -x /opt/grafana/ ]; then + echo "### Upgrading Notice ### " + echo "-- New grafana install home is /usr/share/grafana" + echo "-- Please move sqlite3 database to /var/lib/grafana/" + echo "-- Notice: service name && binary changed to grafana-server" + fi + + if [ "$RESTART_ON_UPGRADE" == "true" ]; then + stopGrafana + startGrafana + fi +fi diff --git a/packaging/rpm/init.d/grafana-server b/packaging/rpm/init.d/grafana-server new file mode 100755 index 00000000000..a30fffb6e00 --- /dev/null +++ b/packaging/rpm/init.d/grafana-server @@ -0,0 +1,146 @@ +#! /usr/bin/env bash + +# chkconfig: 2345 80 05 +# description: Grafana web server & backend +# processname: grafana +# config: /etc/grafana/grafana.ini +# pidfile: /var/run/grafana.pid + +### BEGIN INIT INFO +# Provides: grafana +# Required-Start: $all +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start grafana at boot time +### END INIT INFO + +# tested on +# 1. New lsb that define start-stop-daemon +# 3. Centos with initscripts package installed + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +NAME=grafana-server +DESC="Grafana Server" + +GRAFANA_USER=grafana +GRAFANA_GROUP=grafana +GRAFANA_HOME=/usr/share/grafana +CONF_DIR=/etc/grafana +WORK_DIR=$GRAFANA_HOME +DATA_DIR=/var/lib/grafana +LOG_DIR=/var/log/grafana +CONF_FILE=$CONF_DIR/grafana.ini +MAX_OPEN_FILES=10000 +PID_FILE=/var/run/$NAME.pid +DAEMON=/usr/sbin/$NAME + +# +# init.d / servicectl compatibility (openSUSE) +# +if [ -f /etc/rc.status ]; then + . /etc/rc.status + rc_reset +fi + +# +# Source function library. +# +if [ -f /etc/rc.d/init.d/functions ]; then + . /etc/rc.d/init.d/functions +fi + +# overwrite settings from default file +[ -e /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME + +DAEMON_OPTS="--pidfile=${PID_FILE} --config=${CONF_FILE} cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR}" + +# Check DAEMON exists +test -x $DAEMON || exit 0 + +function isRunning() { + status -p $PID_FILE $NAME > /dev/null 2>&1 +} + +case "$1" in + start) + echo -n $"Starting $DESC: .... " + + isRunning + if [ $? -eq 0 ]; then + echo "Already running." + exit 2 + fi + + # Prepare environment + mkdir -p "$LOG_DIR" "$DATA_DIR" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$LOG_DIR" "$DATA_DIR" + touch "$PID_FILE" && chown "$GRAFANA_USER":"$GRAFANA_GROUP" "$PID_FILE" + + if [ -n "$MAX_OPEN_FILES" ]; then + ulimit -n $MAX_OPEN_FILES + fi + + # Start Daemon + cd $GRAFANA_HOME + su -s /bin/sh -c "nohup ${DAEMON} ${DAEMON_OPTS} >> /dev/null 3>&1 &" $GRAFANA_USER + return=$? + if [ $return -eq 0 ] + then + sleep 1 + # check if pid file has been written two + if ! [[ -s $PID_FILE ]]; then + echo "FAILED" + exit 3 + fi + i=0 + timeout=10 + # Wait for the process to be properly started before exiting + until { cat "$PID_FILE" | xargs kill -0; } >/dev/null 2>&1 + do + sleep 1 + i=$(($i + 1)) + if [ $i -gt $timeout ]; then + echo "FAILED" + exit 4 + fi + done + fi + + echo "OK" + exit $return + ;; + stop) + echo -n "Stopping $DESC ..." + + if [ -f "$PID_FILE" ]; then + killproc -p $PID_FILE -d 20 $NAME + if [ $? -eq 1 ]; then + echo -n "$DESC is not running but pid file exists, cleaning up" + elif [ $? -eq 3 ]; then + PID="`cat $PID_FILE`" + echo -n "Failed to stop $DESC (pid $PID)" + exit 1 + fi + rm -f "$PID_FILE" + echo "OK" + exit 0 + else + echo -n "(not running)" + fi + exit 0 + ;; + status) + status -p $PID_FILE $NAME + ;; + restart|force-reload) + if [ -f "$PID_FILE" ]; then + $0 stop + sleep 1 + fi + $0 start + ;; + *) + echo -n "Usage: $0 {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac diff --git a/packaging/rpm/sysconfig/grafana-server b/packaging/rpm/sysconfig/grafana-server new file mode 100644 index 00000000000..14d2545f6ce --- /dev/null +++ b/packaging/rpm/sysconfig/grafana-server @@ -0,0 +1,17 @@ +GRAFANA_USER=grafana + +GRAFANA_GROUP=grafana + +GRAFANA_HOME=/usr/share/grafana + +LOG_DIR=/var/log/grafana + +DATA_DIR=/var/lib/grafana + +MAX_OPEN_FILES=10000 + +CONF_DIR=/etc/grafana + +CONF_FILE=/etc/grafana/grafana.ini + +RESTART_ON_UPGRADE=false diff --git a/packaging/rpm/systemd/grafana-server.service b/packaging/rpm/systemd/grafana-server.service new file mode 100644 index 00000000000..56a958032b7 --- /dev/null +++ b/packaging/rpm/systemd/grafana-server.service @@ -0,0 +1,21 @@ +[Unit] +Description=Starts and stops a single grafana instance on this system +Documentation=http://docs.grafana.org +Wants=network-online.target +After=network-online.target + +[Service] +EnvironmentFile=/etc/sysconfig/grafana-server +User=grafana +Group=grafana +Type=simple +WorkingDirectory=/usr/share/grafana +ExecStart=/usr/sbin/grafana-server \ + --config=${CONF_FILE} \ + cfg:default.paths.logs=${LOG_DIR} \ + cfg:default.paths.data=${DATA_DIR} \ +LimitNOFILE=10000 +TimeoutStopSec=20 + +[Install] +WantedBy=multi-user.target diff --git a/pkg/api/admin_users.go b/pkg/api/admin_users.go index d3fa111d333..f7e8fca2b5e 100644 --- a/pkg/api/admin_users.go +++ b/pkg/api/admin_users.go @@ -3,6 +3,7 @@ package api import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/util" @@ -64,6 +65,8 @@ func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) { return } + metrics.M_Api_Admin_User_Create.Inc(1) + c.JsonOK("User created") } diff --git a/pkg/api/api.go b/pkg/api/api.go index 8f069c69940..6482beb075c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -41,6 +41,16 @@ func Register(r *macaron.Macaron) { r.Get("/signup", Index) r.Post("/api/user/signup", bind(m.CreateUserCommand{}), SignUp) + // dashboard snapshots + r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) + r.Get("/dashboard/snapshot/*", Index) + + r.Get("/api/snapshots/:key", GetDashboardSnapshot) + r.Get("/api/snapshots-delete/:key", DeleteDashboardSnapshot) + + // api renew session based on remember cookie + r.Get("/api/login/ping", LoginApiPing) + // authed api r.Group("/api", func() { // user diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 8cde5a8bc8a..278264f22d7 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -7,6 +7,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -27,6 +28,8 @@ func isDasboardStarredByUser(c *middleware.Context, dashId int64) (bool, error) } func GetDashboard(c *middleware.Context) { + metrics.M_Api_Dashboard_Get.Inc(1) + slug := c.Params(":slug") query := m.GetDashboardQuery{Slug: slug, OrgId: c.OrgId} @@ -88,6 +91,8 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) { return } + metrics.M_Api_Dashboard_Post.Inc(1) + c.JSON(200, util.DynMap{"status": "success", "slug": cmd.Result.Slug, "version": cmd.Result.Version}) } diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go new file mode 100644 index 00000000000..fe628d580b1 --- /dev/null +++ b/pkg/api/dashboard_snapshot.go @@ -0,0 +1,91 @@ +package api + +import ( + "time" + + "github.com/grafana/grafana/pkg/api/dtos" + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" + "github.com/grafana/grafana/pkg/middleware" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" +) + +func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) { + if cmd.External { + // external snapshot ref requires key and delete key + if cmd.Key == "" || cmd.DeleteKey == "" { + c.JsonApiErr(400, "Missing key and delete key for external snapshot", nil) + return + } + + cmd.OrgId = -1 + cmd.UserId = -1 + metrics.M_Api_Dashboard_Snapshot_External.Inc(1) + } else { + cmd.Key = util.GetRandomString(32) + cmd.DeleteKey = util.GetRandomString(32) + cmd.OrgId = c.OrgId + cmd.UserId = c.UserId + metrics.M_Api_Dashboard_Snapshot_Create.Inc(1) + } + + if err := bus.Dispatch(&cmd); err != nil { + c.JsonApiErr(500, "Failed to create snaphost", err) + return + } + + c.JSON(200, util.DynMap{ + "key": cmd.Key, + "deleteKey": cmd.DeleteKey, + "url": setting.ToAbsUrl("dashboard/snapshot/" + cmd.Key), + "deleteUrl": setting.ToAbsUrl("api/snapshots-delete/" + cmd.DeleteKey), + }) +} + +func GetDashboardSnapshot(c *middleware.Context) { + key := c.Params(":key") + + query := &m.GetDashboardSnapshotQuery{Key: key} + + err := bus.Dispatch(query) + if err != nil { + c.JsonApiErr(500, "Failed to get dashboard snapshot", err) + return + } + + snapshot := query.Result + + // expired snapshots should also be removed from db + if snapshot.Expires.Before(time.Now()) { + c.JsonApiErr(404, "Snapshot not found", err) + return + } + + dto := dtos.Dashboard{ + Model: snapshot.Dashboard, + Meta: dtos.DashboardMeta{ + IsSnapshot: true, + Created: snapshot.Created, + Expires: snapshot.Expires, + }, + } + + metrics.M_Api_Dashboard_Snapshot_Get.Inc(1) + + c.Resp.Header().Set("Cache-Control", "public, max-age=3600") + c.JSON(200, dto) +} + +func DeleteDashboardSnapshot(c *middleware.Context) { + key := c.Params(":key") + cmd := &m.DeleteDashboardSnapshotCommand{DeleteKey: key} + + if err := bus.Dispatch(cmd); err != nil { + c.JsonApiErr(500, "Failed to delete dashboard snapshot", err) + return + } + + c.JSON(200, util.DynMap{"message": "Snapshot deleted. It might take an hour before it's cleared from a CDN cache."}) +} diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index c225c6a5bbb..0057f78ff0d 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -4,6 +4,7 @@ import ( "crypto/md5" "fmt" "strings" + "time" m "github.com/grafana/grafana/pkg/models" ) @@ -27,9 +28,12 @@ type CurrentUser struct { } type DashboardMeta struct { - IsStarred bool `json:"isStarred"` - IsHome bool `json:"isHome"` - Slug string `json:"slug"` + IsStarred bool `json:"isStarred,omitempty"` + IsHome bool `json:"isHome,omitempty"` + IsSnapshot bool `json:"isSnapshot,omitempty"` + Slug string `json:"slug"` + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` } type Dashboard struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index e42a2deedf2..3af191a7af7 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -1,11 +1,10 @@ package api import ( - "errors" - "fmt" "strconv" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" @@ -45,7 +44,8 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro meta, exists := plugins.DataSources[ds.Type] if !exists { - return nil, errors.New(fmt.Sprintf("Could not find plugin definition for data source: %v", ds.Type)) + log.Error(3, "Could not find plugin definition for data source: %v", ds.Type) + continue } dsMap["meta"] = meta diff --git a/pkg/api/index.go b/pkg/api/index.go index 3006c54e8ab..d9ecf65b699 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -28,11 +28,20 @@ func setIndexViewData(c *middleware.Context) error { currentUser.Name = currentUser.Login } + themeUrlParam := c.Query("theme") + if themeUrlParam == "light" { + currentUser.LightTheme = true + } + c.Data["User"] = currentUser c.Data["Settings"] = settings c.Data["AppUrl"] = setting.AppUrl c.Data["AppSubUrl"] = setting.AppSubUrl + if setting.GoogleAnalyticsId != "" { + c.Data["GoogleAnalyticsId"] = setting.GoogleAnalyticsId + } + return nil } @@ -47,7 +56,7 @@ func Index(c *middleware.Context) { func NotFound(c *middleware.Context) { if c.IsApiRequest() { - c.JsonApiErr(200, "Not found", nil) + c.JsonApiErr(404, "Not found", nil) return } diff --git a/pkg/api/login.go b/pkg/api/login.go index e7707c53138..0fc5651d5f9 100644 --- a/pkg/api/login.go +++ b/pkg/api/login.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -27,11 +28,25 @@ func LoginView(c *middleware.Context) { settings["githubAuthEnabled"] = setting.OAuthService.GitHub settings["disableUserSignUp"] = !setting.AllowUserSignUp + if !tryLoginUsingRememberCookie(c) { + c.HTML(200, VIEW_INDEX) + return + } + + if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 { + c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/") + c.Redirect(redirectTo) + return + } + + c.Redirect(setting.AppSubUrl + "/") +} + +func tryLoginUsingRememberCookie(c *middleware.Context) bool { // Check auto-login. uname := c.GetCookie(setting.CookieUserName) if len(uname) == 0 { - c.HTML(200, VIEW_INDEX) - return + return false } isSucceed := false @@ -46,36 +61,32 @@ func LoginView(c *middleware.Context) { userQuery := m.GetUserByLoginQuery{LoginOrEmail: uname} if err := bus.Dispatch(&userQuery); err != nil { - if err != m.ErrUserNotFound { - c.Handle(500, "GetUserByLoginQuery", err) - } else { - c.HTML(200, VIEW_INDEX) - } - return + return false } user := userQuery.Result + // validate remember me cookie if val, _ := c.GetSuperSecureCookie( util.EncodeMd5(user.Rands+user.Password), setting.CookieRememberName); val != user.Login { - c.HTML(200, VIEW_INDEX) - return + return false } isSucceed = true loginUserWithUser(user, c) + return true +} - if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 { - c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/") - c.Redirect(redirectTo) +func LoginApiPing(c *middleware.Context) { + if !tryLoginUsingRememberCookie(c) { + c.JsonApiErr(401, "Unauthorized", nil) return } - c.Redirect(setting.AppSubUrl + "/") + c.JsonOK("Logged in") } func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) { - userQuery := m.GetUserByLoginQuery{LoginOrEmail: cmd.User} err := bus.Dispatch(&userQuery) @@ -92,15 +103,6 @@ func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) { return } - // default to true here for now - cmd.Remember = true - - if cmd.Remember { - days := 86400 * setting.LogInRememberDays - c.SetCookie(setting.CookieUserName, user.Login, days, setting.AppSubUrl+"/") - c.SetSuperSecureCookie(util.EncodeMd5(user.Rands+user.Password), setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/") - } - loginUserWithUser(user, c) result := map[string]interface{}{ @@ -112,6 +114,8 @@ func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) { c.SetCookie("redirect_to", "", -1, setting.AppSubUrl+"/") } + metrics.M_Api_Login_Post.Inc(1) + c.JSON(200, result) } @@ -120,12 +124,16 @@ func loginUserWithUser(user *m.User, c *middleware.Context) { log.Error(3, "User login with nil user") } + days := 86400 * setting.LogInRememberDays + c.SetCookie(setting.CookieUserName, user.Login, days, setting.AppSubUrl+"/") + c.SetSuperSecureCookie(util.EncodeMd5(user.Rands+user.Password), setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/") + c.Session.Set(middleware.SESS_KEY_USERID, user.Id) } func Logout(c *middleware.Context) { c.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl+"/") c.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl+"/") - c.Session.Destory(c.Context) + c.Session.Destory(c) c.Redirect(setting.AppSubUrl + "/login") } diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 9ccb8f0b60d..11d62754a18 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -32,7 +33,6 @@ func OAuthLogin(ctx *middleware.Context) { ctx.Redirect(connect.AuthCodeURL("", oauth2.AccessTypeOnline)) return } - log.Info("code: %v", code) // handle call back token, err := connect.Exchange(oauth2.NoContext, code) @@ -49,14 +49,21 @@ func OAuthLogin(ctx *middleware.Context) { return } - log.Info("login.OAuthLogin(social login): %s", userInfo) + log.Trace("login.OAuthLogin(social login): %s", userInfo) + + // validate that the email is allowed to login to grafana + if !connect.IsEmailAllowed(userInfo.Email) { + log.Info("OAuth login attempt with unallowed email, %s", userInfo.Email) + ctx.Redirect(setting.AppSubUrl + "/login?email_not_allowed=1") + return + } userQuery := m.GetUserByLoginQuery{LoginOrEmail: userInfo.Email} err = bus.Dispatch(&userQuery) // create account if missing if err == m.ErrUserNotFound { - if !setting.AllowUserSignUp { + if !connect.IsSignupAllowed() { ctx.Redirect(setting.AppSubUrl + "/login") return } @@ -81,5 +88,7 @@ func OAuthLogin(ctx *middleware.Context) { // login loginUserWithUser(userQuery.Result, ctx) + metrics.M_Api_Login_OAuth.Inc(1) + ctx.Redirect(setting.AppSubUrl + "/") } diff --git a/pkg/api/org.go b/pkg/api/org.go index 8b41b0e3f5f..ac8727c9e49 100644 --- a/pkg/api/org.go +++ b/pkg/api/org.go @@ -2,8 +2,10 @@ package api import ( "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" ) func GetOrg(c *middleware.Context) { @@ -28,13 +30,19 @@ func GetOrg(c *middleware.Context) { } func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) { - cmd.UserId = c.UserId + if !setting.AllowUserOrgCreate && !c.IsGrafanaAdmin { + c.JsonApiErr(401, "Access denied", nil) + return + } + cmd.UserId = c.UserId if err := bus.Dispatch(&cmd); err != nil { c.JsonApiErr(500, "Failed to create organization", err) return } + metrics.M_Api_Org_Create.Inc(1) + c.JsonOK("Organization created") } diff --git a/pkg/api/render.go b/pkg/api/render.go index 0398d455cc8..728128acaab 100644 --- a/pkg/api/render.go +++ b/pkg/api/render.go @@ -13,6 +13,18 @@ import ( func RenderToPng(c *middleware.Context) { queryReader := util.NewUrlQueryReader(c.Req.URL) queryParams := fmt.Sprintf("?%s", c.Req.URL.RawQuery) + sessionId := c.Session.ID() + + // Handle api calls authenticated without session + if sessionId == "" && c.ApiKeyId != 0 { + c.Session.Start(c) + c.Session.Set(middleware.SESS_KEY_APIKEY, c.ApiKeyId) + // release will make sure the new session is persisted before + // we spin up phantomjs + c.Session.Release() + // cleanup session after render is complete + defer func() { c.Session.Destory(c) }() + } renderOpts := &renderer.RenderOpts{ Url: c.Params("*") + queryParams, diff --git a/pkg/api/signup.go b/pkg/api/signup.go index 74f00509b98..63bb34c72ac 100644 --- a/pkg/api/signup.go +++ b/pkg/api/signup.go @@ -2,6 +2,7 @@ package api import ( "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" @@ -26,4 +27,6 @@ func SignUp(c *middleware.Context, cmd m.CreateUserCommand) { loginUserWithUser(&user, c) c.JsonOK("User created and logged in") + + metrics.M_Api_User_SignUp.Inc(1) } diff --git a/pkg/api/static/static.go b/pkg/api/static/static.go new file mode 100644 index 00000000000..43ba6a32b20 --- /dev/null +++ b/pkg/api/static/static.go @@ -0,0 +1,218 @@ +// Copyright 2013 Martini Authors +// Copyright 2014 Unknwon +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package httpstatic + +import ( + "log" + "net/http" + "os" + "path" + "path/filepath" + "strings" + "sync" + + "github.com/Unknwon/macaron" +) + +var Root string + +func init() { + var err error + Root, err = os.Getwd() + if err != nil { + panic("error getting work directory: " + err.Error()) + } +} + +// StaticOptions is a struct for specifying configuration options for the macaron.Static middleware. +type StaticOptions struct { + // Prefix is the optional prefix used to serve the static directory content + Prefix string + // SkipLogging will disable [Static] log messages when a static file is served. + SkipLogging bool + // IndexFile defines which file to serve as index if it exists. + IndexFile string + // Expires defines which user-defined function to use for producing a HTTP Expires Header + // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching + AddHeaders func(ctx *macaron.Context) + // FileSystem is the interface for supporting any implmentation of file system. + FileSystem http.FileSystem +} + +// FIXME: to be deleted. +type staticMap struct { + lock sync.RWMutex + data map[string]*http.Dir +} + +func (sm *staticMap) Set(dir *http.Dir) { + sm.lock.Lock() + defer sm.lock.Unlock() + + sm.data[string(*dir)] = dir +} + +func (sm *staticMap) Get(name string) *http.Dir { + sm.lock.RLock() + defer sm.lock.RUnlock() + + return sm.data[name] +} + +func (sm *staticMap) Delete(name string) { + sm.lock.Lock() + defer sm.lock.Unlock() + + delete(sm.data, name) +} + +var statics = staticMap{sync.RWMutex{}, map[string]*http.Dir{}} + +// staticFileSystem implements http.FileSystem interface. +type staticFileSystem struct { + dir *http.Dir +} + +func newStaticFileSystem(directory string) staticFileSystem { + if !filepath.IsAbs(directory) { + directory = filepath.Join(Root, directory) + } + dir := http.Dir(directory) + statics.Set(&dir) + return staticFileSystem{&dir} +} + +func (fs staticFileSystem) Open(name string) (http.File, error) { + return fs.dir.Open(name) +} + +func prepareStaticOption(dir string, opt StaticOptions) StaticOptions { + // Defaults + if len(opt.IndexFile) == 0 { + opt.IndexFile = "index.html" + } + // Normalize the prefix if provided + if opt.Prefix != "" { + // Ensure we have a leading '/' + if opt.Prefix[0] != '/' { + opt.Prefix = "/" + opt.Prefix + } + // Remove any trailing '/' + opt.Prefix = strings.TrimRight(opt.Prefix, "/") + } + if opt.FileSystem == nil { + opt.FileSystem = newStaticFileSystem(dir) + } + return opt +} + +func prepareStaticOptions(dir string, options []StaticOptions) StaticOptions { + var opt StaticOptions + if len(options) > 0 { + opt = options[0] + } + return prepareStaticOption(dir, opt) +} + +func staticHandler(ctx *macaron.Context, log *log.Logger, opt StaticOptions) bool { + if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" { + return false + } + + file := ctx.Req.URL.Path + // if we have a prefix, filter requests by stripping the prefix + if opt.Prefix != "" { + if !strings.HasPrefix(file, opt.Prefix) { + return false + } + file = file[len(opt.Prefix):] + if file != "" && file[0] != '/' { + return false + } + } + + f, err := opt.FileSystem.Open(file) + if err != nil { + return false + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return true // File exists but fail to open. + } + + // Try to serve index file + if fi.IsDir() { + // Redirect if missing trailing slash. + if !strings.HasSuffix(ctx.Req.URL.Path, "/") { + http.Redirect(ctx.Resp, ctx.Req.Request, ctx.Req.URL.Path+"/", http.StatusFound) + return true + } + + file = path.Join(file, opt.IndexFile) + f, err = opt.FileSystem.Open(file) + if err != nil { + return false // Discard error. + } + defer f.Close() + + fi, err = f.Stat() + if err != nil || fi.IsDir() { + return true + } + } + + if !opt.SkipLogging { + log.Println("[Static] Serving " + file) + } + + // Add an Expires header to the static content + if opt.AddHeaders != nil { + opt.AddHeaders(ctx) + } + + http.ServeContent(ctx.Resp, ctx.Req.Request, file, fi.ModTime(), f) + return true +} + +// Static returns a middleware handler that serves static files in the given directory. +func Static(directory string, staticOpt ...StaticOptions) macaron.Handler { + opt := prepareStaticOptions(directory, staticOpt) + + return func(ctx *macaron.Context, log *log.Logger) { + staticHandler(ctx, log, opt) + } +} + +// Statics registers multiple static middleware handlers all at once. +func Statics(opt StaticOptions, dirs ...string) macaron.Handler { + if len(dirs) == 0 { + panic("no static directory is given") + } + opts := make([]StaticOptions, len(dirs)) + for i := range dirs { + opts[i] = prepareStaticOption(dirs[i], opt) + } + + return func(ctx *macaron.Context, log *log.Logger) { + for i := range opts { + if staticHandler(ctx, log, opts[i]) { + return + } + } + } +} diff --git a/pkg/cmd/common.go b/pkg/cmd/common.go deleted file mode 100644 index 3caa8350700..00000000000 --- a/pkg/cmd/common.go +++ /dev/null @@ -1,21 +0,0 @@ -package cmd - -import ( - "time" - - "github.com/codegangsta/cli" - "github.com/grafana/grafana/pkg/log" - "github.com/grafana/grafana/pkg/services/sqlstore" - "github.com/grafana/grafana/pkg/setting" -) - -func initRuntime(c *cli.Context) { - setting.NewConfigContext(c.GlobalString("config")) - - log.Info("Starting Grafana") - log.Info("Version: %v, Commit: %v, Build date: %v", setting.BuildVersion, setting.BuildCommit, time.Unix(setting.BuildStamp, 0)) - setting.LogLoadedConfigFiles() - - sqlstore.NewEngine() - sqlstore.EnsureAdminUser() -} diff --git a/pkg/cmd/dashboard.go b/pkg/cmd/dashboard.go deleted file mode 100644 index 10ceedd668d..00000000000 --- a/pkg/cmd/dashboard.go +++ /dev/null @@ -1,207 +0,0 @@ -package cmd - -import ( - "encoding/json" - "io" - "os" - "path/filepath" - "strings" - - "github.com/codegangsta/cli" - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/log" - m "github.com/grafana/grafana/pkg/models" -) - -var ( - ImportDashboard = cli.Command{ - Name: "dashboards:import", - Usage: "imports dashboards in JSON from a directory", - Description: "Starts Grafana import process", - Action: runImport, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "dir", - Usage: "path to folder containing json dashboards", - }, - }, - } - - ExportDashboard = cli.Command{ - Name: "dashboards:export", - Usage: "exports dashboards in JSON from a directory", - Description: "Starts Grafana export process", - Action: runExport, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "dir", - Usage: "path to folder containing json dashboards", - }, - }, - } -) - -func runImport(c *cli.Context) { - dir := c.String("dir") - if len(dir) == 0 { - log.ConsoleFatalf("Missing command flag --dir") - } - - file, err := os.Stat(dir) - if os.IsNotExist(err) { - log.ConsoleFatalf("Directory does not exist: %v", dir) - } - - if !file.IsDir() { - log.ConsoleFatalf("%v is not a directory", dir) - } - - if !c.Args().Present() { - log.ConsoleFatal("Organization name arg is required") - } - - orgName := c.Args().First() - - initRuntime(c) - - orgQuery := m.GetOrgByNameQuery{Name: orgName} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find account", err) - } - - orgId := orgQuery.Result.Id - - visitor := func(path string, f os.FileInfo, err error) error { - if err != nil { - return err - } - if f.IsDir() { - return nil - } - if strings.HasSuffix(f.Name(), ".json") { - if err := importDashboard(path, orgId); err != nil { - log.ConsoleFatalf("Failed to import dashboard file: %v, err: %v", path, err) - } - } - return nil - } - - if err := filepath.Walk(dir, visitor); err != nil { - log.ConsoleFatalf("Failed to scan dir for json files: %v", err) - } -} - -func importDashboard(path string, orgId int64) error { - log.ConsoleInfof("Importing %v", path) - - reader, err := os.Open(path) - if err != nil { - return err - } - - defer reader.Close() - - dash := m.NewDashboard("temp") - jsonParser := json.NewDecoder(reader) - - if err := jsonParser.Decode(&dash.Data); err != nil { - return err - } - dash.Data["id"] = nil - - cmd := m.SaveDashboardCommand{ - OrgId: orgId, - Dashboard: dash.Data, - } - - if err := bus.Dispatch(&cmd); err != nil { - return err - } - - return nil -} - -func runExport(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Account name arg is required") - } - - name := c.Args().First() - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - - dir := c.String("dir") - dash := c.Args().Get(1) - - query := m.SearchDashboardsQuery{OrgId: orgId, Title: dash} - err := bus.Dispatch(&query) - if err != nil { - log.ConsoleFatalf("Failed to find dashboards: %s", err) - return - } - - if dir == "" && len(query.Result) > 1 { - log.ConsoleFatalf("Dashboard title '%s' returned too many results. "+ - "Use --dir or a more specific title", dash) - return - } - - for _, v := range query.Result { - f := os.Stdout - if dir != "" { - dest := filepath.Join(dir, v.Slug+".json") - f, err = os.Create(dest) - if err != nil { - log.ConsoleFatalf("Unable to create file: %s", err) - } - log.ConsoleInfof("Exporting '%s' dashboard to %s", v.Title, dest) - } - - exportDashboard(f, orgId, v.Slug) - - if dir != "" { - if err := f.Sync(); err != nil { - log.ConsoleFatalf("Unable to sync file: %s", err) - } - - if err := f.Close(); err != nil { - log.ConsoleFatalf("Unable to close file: %s", err) - } - } - } - if dir != "" { - log.ConsoleInfof("Exported %d dashboards to %s", len(query.Result), dir) - } -} - -func exportDashboard(w io.Writer, orgId int64, slug string) { - query := m.GetDashboardQuery{Slug: slug, OrgId: orgId} - err := bus.Dispatch(&query) - if err != nil { - log.ConsoleFatalf("Failed to find dashboard: %s", err) - return - } - - out, err := json.MarshalIndent(query.Result.Data, "", " ") - if err != nil { - log.ConsoleFatalf("Failed to marshal dashboard: %s", err) - return - } - - n, err := w.Write(out) - if err != nil { - log.ConsoleFatalf("Failed to write dashboard: %s", err) - return - } - - if n != len(out) { - log.ConsoleFatalf("Failed to write dashboard: wrote %d expected %d", n, len(out)) - return - } -} diff --git a/pkg/cmd/datasource.go b/pkg/cmd/datasource.go deleted file mode 100644 index 9656180acd9..00000000000 --- a/pkg/cmd/datasource.go +++ /dev/null @@ -1,230 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/log" - m "github.com/grafana/grafana/pkg/models" -) - -var ( - ListDataSources = cli.Command{ - Name: "datasources", - Usage: "list datasources", - Description: "Lists the datasources in the system", - Action: listDatasources, - } - CreateDataSource = cli.Command{ - Name: "datasources:create", - Usage: "creates a new datasource", - Description: "Creates a new datasource", - Action: createDataSource, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "type", - Value: "graphite", - Usage: fmt.Sprintf("Datasource type [%s,%s,%s,%s]", - m.DS_GRAPHITE, m.DS_INFLUXDB, m.DS_ES, m.DS_OPENTSDB), - }, - cli.StringFlag{ - Name: "access", - Value: "proxy", - Usage: "Datasource access [proxy,direct]", - }, - cli.BoolFlag{ - Name: "default", - Usage: "Make this the default datasource", - }, - cli.StringFlag{ - Name: "db", - Usage: "InfluxDB DB", - }, - cli.StringFlag{ - Name: "user", - Usage: "InfluxDB username", - }, - cli.StringFlag{ - Name: "password", - Usage: "InfluxDB password", - }, - }, - } - DescribeDataSource = cli.Command{ - Name: "datasources:info", - Usage: "describe the details of a datasource", - Description: "Describes the details of a datasource", - Action: describeDataSource, - } - DeleteDataSource = cli.Command{ - Name: "datasources:delete", - Usage: "Deletes a datasource", - Description: "Deletes a datasource", - Action: deleteDataSource, - } -) - -func createDataSource(c *cli.Context) { - initRuntime(c) - - if len(c.Args()) != 3 { - log.ConsoleFatal("Missing required arguments") - } - - name := c.Args().First() - ds := c.Args()[1] - url := c.Args()[2] - dsType := c.String("type") - dsAccess := c.String("access") - dsDefault := c.Bool("default") - - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - - query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} - if err := bus.Dispatch(&query); err != nil { - if err != m.ErrDataSourceNotFound { - log.ConsoleFatalf("Failed to query for existing datasource: %s", err) - } - } - - if query.Result.Id > 0 { - log.ConsoleFatalf("DataSource %s already exists", ds) - } - - cmd := m.AddDataSourceCommand{ - OrgId: orgId, - Name: ds, - Url: url, - Type: dsType, - Access: m.DsAccess(dsAccess), - IsDefault: dsDefault, - } - - switch dsType { - case m.DS_INFLUXDB: - db := c.String("db") - if db == "" { - log.ConsoleFatal("db name is required for influxdb datasources") - } - cmd.Database = db - cmd.User = c.String("user") - cmd.Password = c.String("password") - } - - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to create datasource: %s", err) - } - datasource := cmd.Result - - log.ConsoleInfof("Datasource %s created", datasource.Name) -} - -func listDatasources(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Account name arg is required") - } - - name := c.Args().First() - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - - query := m.GetDataSourcesQuery{OrgId: orgId} - if err := bus.Dispatch(&query); err != nil { - log.ConsoleFatalf("Failed to find datasources: %s", err) - } - - w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0) - - fmt.Fprintf(w, "ID\tNAME\tURL\tTYPE\tACCESS\tDEFAULT\n") - for _, ds := range query.Result { - fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\n", ds.Id, ds.Name, ds.Url, ds.Type, - ds.Access, ds.IsDefault) - } - w.Flush() -} - -func describeDataSource(c *cli.Context) { - initRuntime(c) - - if len(c.Args()) != 2 { - log.ConsoleFatal("Organization and datasource name args are required") - } - - name := c.Args().First() - ds := c.Args()[1] - - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - - query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} - if err := bus.Dispatch(&query); err != nil { - log.ConsoleFatalf("Failed to find datasource: %s", err) - } - datasource := query.Result - - w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0) - fmt.Fprintf(w, "NAME\t%s\n", datasource.Name) - fmt.Fprintf(w, "URL\t%s\n", datasource.Url) - fmt.Fprintf(w, "DEFAULT\t%t\n", datasource.IsDefault) - fmt.Fprintf(w, "ACCESS\t%s\n", datasource.Access) - fmt.Fprintf(w, "TYPE\t%s\n", datasource.Type) - - switch datasource.Type { - case m.DS_INFLUXDB: - fmt.Fprintf(w, "DATABASE\t%s\n", datasource.Database) - fmt.Fprintf(w, "DB USER\t%s\n", datasource.User) - fmt.Fprintf(w, "DB PASSWORD\t%s\n", datasource.Password) - case m.DS_ES: - fmt.Fprintf(w, "INDEX\t%s\n", datasource.Database) - } - w.Flush() -} - -func deleteDataSource(c *cli.Context) { - initRuntime(c) - - if len(c.Args()) != 2 { - log.ConsoleFatal("Account and datasource name args are required") - } - - name := c.Args().First() - ds := c.Args()[1] - - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - - query := m.GetDataSourceByNameQuery{OrgId: orgId, Name: ds} - if err := bus.Dispatch(&query); err != nil { - log.ConsoleFatalf("Failed to find datasource: %s", err) - } - datasource := query.Result - - cmd := m.DeleteDataSourceCommand{OrgId: orgId, Id: datasource.Id} - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to delete datasource: %s", err) - } - - log.ConsoleInfof("DataSource %s deleted", ds) -} diff --git a/pkg/cmd/orgs.go b/pkg/cmd/orgs.go deleted file mode 100644 index bcc2eeec38a..00000000000 --- a/pkg/cmd/orgs.go +++ /dev/null @@ -1,99 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "text/tabwriter" - - "github.com/codegangsta/cli" - - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/log" - m "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/setting" -) - -var ListOrgs = cli.Command{ - Name: "orgs", - Usage: "list organizations", - Description: "Lists the organizations in the system", - Action: listOrgs, -} - -var CreateOrg = cli.Command{ - Name: "orgs:create", - Usage: "Creates a new organization", - Description: "Creates a new organization", - Action: createOrg, -} - -var DeleteOrg = cli.Command{ - Name: "orgs:delete", - Usage: "Delete an existing organization", - Description: "Deletes an existing organization", - Action: deleteOrg, -} - -func listOrgs(c *cli.Context) { - initRuntime(c) - - orgsQuery := m.GetOrgListQuery{} - if err := bus.Dispatch(&orgsQuery); err != nil { - log.ConsoleFatalf("Failed to find organizations: %s", err) - } - - w := tabwriter.NewWriter(os.Stdout, 8, 1, 4, ' ', 0) - - fmt.Fprintf(w, "ID\tNAME\n") - for _, org := range orgsQuery.Result { - fmt.Fprintf(w, "%d\t%s\n", org.Id, org.Name) - } - w.Flush() -} - -func createOrg(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Organization name arg is required") - } - - name := c.Args().First() - - adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser} - - if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound { - log.ConsoleFatalf("Failed to find default admin user: %s", err) - } - - adminUser := adminQuery.Result - - cmd := m.CreateOrgCommand{Name: name, UserId: adminUser.Id} - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to create organization: %s", err) - } - - log.ConsoleInfof("Organization %s created for admin user %s\n", name, adminUser.Email) -} - -func deleteOrg(c *cli.Context) { - initRuntime(c) - - if !c.Args().Present() { - log.ConsoleFatal("Organization name arg is required") - } - - name := c.Args().First() - orgQuery := m.GetOrgByNameQuery{Name: name} - if err := bus.Dispatch(&orgQuery); err != nil { - log.ConsoleFatalf("Failed to find organization: %s", err) - } - - orgId := orgQuery.Result.Id - cmd := m.DeleteOrgCommand{Id: orgId} - if err := bus.Dispatch(&cmd); err != nil { - log.ConsoleFatalf("Failed to delete organization: %s", err) - } - - log.ConsoleInfof("Organization %s deleted", name) -} diff --git a/pkg/cmd/web.go b/pkg/cmd/web.go index 6619e5b1e0e..7b6014aeb7e 100644 --- a/pkg/cmd/web.go +++ b/pkg/cmd/web.go @@ -5,44 +5,27 @@ package cmd import ( "fmt" - "io/ioutil" "net/http" - "os" "path" - "path/filepath" - "strconv" - "time" "github.com/Unknwon/macaron" - "github.com/codegangsta/cli" - "github.com/macaron-contrib/session" - _ "github.com/macaron-contrib/session/mysql" - _ "github.com/macaron-contrib/session/postgres" "github.com/grafana/grafana/pkg/api" + "github.com/grafana/grafana/pkg/api/static" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" - "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/services/eventpublisher" "github.com/grafana/grafana/pkg/setting" - "github.com/grafana/grafana/pkg/social" ) -var Web = cli.Command{ - Name: "web", - Usage: "Starts Grafana backend & web server", - Description: "Starts Grafana backend & web server", - Action: runWeb, -} - func newMacaron() *macaron.Macaron { macaron.Env = setting.Env - m := macaron.New() + m.Use(middleware.Logger()) m.Use(macaron.Recovery()) + if setting.EnableGzip { - m.Use(macaron.Gziper()) + m.Use(middleware.Gziper()) } mapStatic(m, "", "public") @@ -51,8 +34,6 @@ func newMacaron() *macaron.Macaron { mapStatic(m, "img", "img") mapStatic(m, "fonts", "fonts") - m.Use(session.Sessioner(setting.SessionOptions)) - m.Use(macaron.Renderer(macaron.RenderOptions{ Directory: path.Join(setting.StaticRootPath, "views"), IndentJSON: macaron.Env != macaron.PROD, @@ -60,29 +41,33 @@ func newMacaron() *macaron.Macaron { })) m.Use(middleware.GetContextHandler()) + m.Use(middleware.Sessioner(setting.SessionOptions)) + return m } func mapStatic(m *macaron.Macaron, dir string, prefix string) { - m.Use(macaron.Static( + headers := func(c *macaron.Context) { + c.Resp.Header().Set("Cache-Control", "public, max-age=3600") + } + + if setting.Env == setting.DEV { + headers = func(c *macaron.Context) { + c.Resp.Header().Set("Cache-Control", "max-age=0, must-revalidate, no-cache") + } + } + + m.Use(httpstatic.Static( path.Join(setting.StaticRootPath, dir), - macaron.StaticOptions{ + httpstatic.StaticOptions{ SkipLogging: true, Prefix: prefix, - Expires: func() string { - return time.Now().UTC().Format(http.TimeFormat) - }, + AddHeaders: headers, }, )) } -func runWeb(c *cli.Context) { - initRuntime(c) - writePIDFile(c) - - social.NewOAuthService() - eventpublisher.Init() - plugins.Init() +func StartServer() { var err error m := newMacaron() @@ -103,22 +88,3 @@ func runWeb(c *cli.Context) { log.Fatal(4, "Fail to start server: %v", err) } } - -func writePIDFile(c *cli.Context) { - path := c.GlobalString("pidfile") - if path == "" { - return - } - - // Ensure the required directory structure exists. - err := os.MkdirAll(filepath.Dir(path), 0700) - if err != nil { - log.Fatal(3, "Failed to verify pid directory", err) - } - - // Retrieve the PID and write it. - pid := strconv.Itoa(os.Getpid()) - if err := ioutil.WriteFile(path, []byte(pid), 0644); err != nil { - log.Fatal(3, "Failed to write pidfile", err) - } -} diff --git a/pkg/components/renderer/renderer.go b/pkg/components/renderer/renderer.go index 054632395ce..aa9e0c92525 100644 --- a/pkg/components/renderer/renderer.go +++ b/pkg/components/renderer/renderer.go @@ -1,8 +1,6 @@ package renderer import ( - "crypto/md5" - "encoding/hex" "io" "os" "os/exec" @@ -11,6 +9,7 @@ import ( "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" ) type RenderOpts struct { @@ -24,10 +23,10 @@ func RenderToPng(params *RenderOpts) (string, error) { log.Info("PhantomRenderer::renderToPng url %v", params.Url) binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "phantomjs")) scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js")) - pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, getHash(params.Url))) + pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20))) pngPath = pngPath + ".png" - cmd := exec.Command(binPath, scriptPath, "url="+params.Url, "width="+params.Width, + cmd := exec.Command(binPath, "--ignore-ssl-errors=true", scriptPath, "url="+params.Url, "width="+params.Width, "height="+params.Height, "png="+pngPath, "cookiename="+setting.SessionOptions.CookieName, "domain="+setting.Domain, "sessionid="+params.SessionId) stdout, err := cmd.StdoutPipe() @@ -64,9 +63,3 @@ func RenderToPng(params *RenderOpts) (string, error) { return pngPath, nil } - -func getHash(text string) string { - hasher := md5.New() - hasher.Write([]byte(text)) - return hex.EncodeToString(hasher.Sum(nil)) -} diff --git a/pkg/metrics/counter.go b/pkg/metrics/counter.go new file mode 100644 index 00000000000..1a4a88be37b --- /dev/null +++ b/pkg/metrics/counter.go @@ -0,0 +1,72 @@ +package metrics + +import "sync/atomic" + +// Counters hold an int64 value that can be incremented and decremented. +type Counter interface { + Clear() + Count() int64 + Dec(int64) + Inc(int64) + Snapshot() Counter +} + +// NewCounter constructs a new StandardCounter. +func NewCounter() Counter { + return &StandardCounter{0} +} + +// CounterSnapshot is a read-only copy of another Counter. +type CounterSnapshot int64 + +// Clear panics. +func (CounterSnapshot) Clear() { + panic("Clear called on a CounterSnapshot") +} + +// Count returns the count at the time the snapshot was taken. +func (c CounterSnapshot) Count() int64 { return int64(c) } + +// Dec panics. +func (CounterSnapshot) Dec(int64) { + panic("Dec called on a CounterSnapshot") +} + +// Inc panics. +func (CounterSnapshot) Inc(int64) { + panic("Inc called on a CounterSnapshot") +} + +// Snapshot returns the snapshot. +func (c CounterSnapshot) Snapshot() Counter { return c } + +// StandardCounter is the standard implementation of a Counter and uses the +// sync/atomic package to manage a single int64 value. +type StandardCounter struct { + count int64 +} + +// Clear sets the counter to zero. +func (c *StandardCounter) Clear() { + atomic.StoreInt64(&c.count, 0) +} + +// Count returns the current count. +func (c *StandardCounter) Count() int64 { + return atomic.LoadInt64(&c.count) +} + +// Dec decrements the counter by the given amount. +func (c *StandardCounter) Dec(i int64) { + atomic.AddInt64(&c.count, -i) +} + +// Inc increments the counter by the given amount. +func (c *StandardCounter) Inc(i int64) { + atomic.AddInt64(&c.count, i) +} + +// Snapshot returns a read-only copy of the counter. +func (c *StandardCounter) Snapshot() Counter { + return CounterSnapshot(c.Count()) +} diff --git a/pkg/metrics/metric_ref.go b/pkg/metrics/metric_ref.go new file mode 100644 index 00000000000..f9e5d693d4c --- /dev/null +++ b/pkg/metrics/metric_ref.go @@ -0,0 +1,39 @@ +package metrics + +type comboCounterRef struct { + usageCounter Counter + metricCounter Counter +} + +func NewComboCounterRef(name string) Counter { + cr := &comboCounterRef{} + cr.usageCounter = UsageStats.GetOrRegister(name, NewCounter).(Counter) + cr.metricCounter = MetricStats.GetOrRegister(name, NewCounter).(Counter) + return cr +} + +func (c comboCounterRef) Clear() { + c.usageCounter.Clear() + c.metricCounter.Clear() +} + +func (c comboCounterRef) Count() int64 { + panic("Count called on a combocounter ref") +} + +// Dec panics. +func (c comboCounterRef) Dec(i int64) { + c.usageCounter.Dec(i) + c.metricCounter.Dec(i) +} + +// Inc panics. +func (c comboCounterRef) Inc(i int64) { + c.usageCounter.Inc(i) + c.metricCounter.Inc(i) +} + +// Snapshot returns the snapshot. +func (c comboCounterRef) Snapshot() Counter { + panic("snapshot called on a combocounter ref") +} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 00000000000..f6dab8c8043 --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,29 @@ +package metrics + +var UsageStats = NewRegistry() +var MetricStats = NewRegistry() + +var ( + M_Instance_Start = NewComboCounterRef("instance.start") + + M_Page_Status_200 = NewComboCounterRef("page.status.200") + M_Page_Status_500 = NewComboCounterRef("page.status.500") + M_Page_Status_404 = NewComboCounterRef("page.status.404") + + M_Api_Status_500 = NewComboCounterRef("api.status.500") + M_Api_Status_404 = NewComboCounterRef("api.status.404") + + M_Api_User_SignUp = NewComboCounterRef("api.user.signup") + M_Api_Dashboard_Get = NewComboCounterRef("api.dashboard.get") + M_Api_Dashboard_Post = NewComboCounterRef("api.dashboard.post") + M_Api_Admin_User_Create = NewComboCounterRef("api.admin.user_create") + M_Api_Login_Post = NewComboCounterRef("api.login.post") + M_Api_Login_OAuth = NewComboCounterRef("api.login.oauth") + M_Api_Org_Create = NewComboCounterRef("api.org.create") + + M_Api_Dashboard_Snapshot_Create = NewComboCounterRef("api.dashboard_snapshot.create") + M_Api_Dashboard_Snapshot_External = NewComboCounterRef("api.dashboard_snapshot.external") + M_Api_Dashboard_Snapshot_Get = NewComboCounterRef("api.dashboard_snapshot.get") + + M_Models_Dashboard_Insert = NewComboCounterRef("models.dashboard.insert") +) diff --git a/pkg/metrics/registry.go b/pkg/metrics/registry.go new file mode 100644 index 00000000000..9e1618f3691 --- /dev/null +++ b/pkg/metrics/registry.go @@ -0,0 +1,102 @@ +package metrics + +import ( + "fmt" + "reflect" + "sync" +) + +// DuplicateMetric is the error returned by Registry.Register when a metric +// already exists. If you mean to Register that metric you must first +// Unregister the existing metric. +type DuplicateMetric string + +func (err DuplicateMetric) Error() string { + return fmt.Sprintf("duplicate metric: %s", string(err)) +} + +type Registry interface { + // Call the given function for each registered metric. + Each(func(string, interface{})) + + // Get the metric by the given name or nil if none is registered. + Get(string) interface{} + + // Gets an existing metric or registers the given one. + // The interface can be the metric to register if not found in registry, + // or a function returning the metric for lazy instantiation. + GetOrRegister(string, interface{}) interface{} + + // Register the given metric under the given name. + Register(string, interface{}) error +} + +// The standard implementation of a Registry is a mutex-protected map +// of names to metrics. +type StandardRegistry struct { + metrics map[string]interface{} + mutex sync.Mutex +} + +// Create a new registry. +func NewRegistry() Registry { + return &StandardRegistry{metrics: make(map[string]interface{})} +} + +// Call the given function for each registered metric. +func (r *StandardRegistry) Each(f func(string, interface{})) { + for name, i := range r.registered() { + f(name, i) + } +} + +// Get the metric by the given name or nil if none is registered. +func (r *StandardRegistry) Get(name string) interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + return r.metrics[name] +} + +// Gets an existing metric or creates and registers a new one. Threadsafe +// alternative to calling Get and Register on failure. +// The interface can be the metric to register if not found in registry, +// or a function returning the metric for lazy instantiation. +func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + if metric, ok := r.metrics[name]; ok { + return metric + } + if v := reflect.ValueOf(i); v.Kind() == reflect.Func { + i = v.Call(nil)[0].Interface() + } + r.register(name, i) + return i +} + +// Register the given metric under the given name. Returns a DuplicateMetric +// if a metric by the given name is already registered. +func (r *StandardRegistry) Register(name string, i interface{}) error { + r.mutex.Lock() + defer r.mutex.Unlock() + return r.register(name, i) +} + +func (r *StandardRegistry) register(name string, i interface{}) error { + if _, ok := r.metrics[name]; ok { + return DuplicateMetric(name) + } + + r.metrics[name] = i + return nil +} + +func (r *StandardRegistry) registered() map[string]interface{} { + metrics := make(map[string]interface{}, len(r.metrics)) + r.mutex.Lock() + defer r.mutex.Unlock() + for name, i := range r.metrics { + metrics[name] = i + } + return metrics +} diff --git a/pkg/metrics/report_usage.go b/pkg/metrics/report_usage.go new file mode 100644 index 00000000000..c8848fb5371 --- /dev/null +++ b/pkg/metrics/report_usage.go @@ -0,0 +1,64 @@ +package metrics + +import ( + "bytes" + "encoding/json" + "net/http" + "strings" + "time" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" +) + +func StartUsageReportLoop() chan struct{} { + M_Instance_Start.Inc(1) + + ticker := time.NewTicker(time.Hour * 24) + for { + select { + case <-ticker.C: + sendUsageStats() + } + } +} + +func sendUsageStats() { + log.Trace("Sending anonymous usage stats to stats.grafana.org") + + version := strings.Replace(setting.BuildVersion, ".", "_", -1) + + metrics := map[string]interface{}{} + report := map[string]interface{}{ + "version": version, + "metrics": metrics, + } + + statsQuery := m.GetSystemStatsQuery{} + if err := bus.Dispatch(&statsQuery); err != nil { + log.Error(3, "Failed to get system stats", err) + return + } + + UsageStats.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + if metric.Count() > 0 { + metrics[name+".count"] = metric.Count() + metric.Clear() + } + } + }) + + metrics["stats.dashboards.count"] = statsQuery.Result.DashboardCount + metrics["stats.users.count"] = statsQuery.Result.UserCount + metrics["stats.orgs.count"] = statsQuery.Result.OrgCount + + out, _ := json.Marshal(report) + data := bytes.NewBuffer(out) + + client := http.Client{Timeout: time.Duration(5 * time.Second)} + go client.Post("https://stats.grafana.org/grafana-usage-report", "application/json", data) +} diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 53b554942a6..9fb09a5c395 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -28,7 +28,7 @@ func getRequestUserId(c *Context) int64 { func getApiKey(c *Context) string { header := c.Req.Header.Get("Authorization") parts := strings.SplitN(header, " ", 2) - if len(parts) == 2 || parts[0] == "Bearer" { + if len(parts) == 2 && parts[0] == "Bearer" { key := parts[1] return key } @@ -42,6 +42,7 @@ func authDenied(c *Context) { return } + c.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+c.Req.RequestURI), 0, setting.AppSubUrl+"/") c.Redirect(setting.AppSubUrl + "/login") } @@ -63,13 +64,11 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler { func Auth(options *AuthOptions) macaron.Handler { return func(c *Context) { if !c.IsGrafanaAdmin && options.ReqGrafanaAdmin { - c.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+c.Req.RequestURI), 0, setting.AppSubUrl+"/") authDenied(c) return } if !c.IsSignedIn && options.ReqSignedIn && !c.AllowAnonymous { - c.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+c.Req.RequestURI), 0, setting.AppSubUrl+"/") authDenied(c) return } diff --git a/pkg/middleware/middleware.go b/pkg/middleware/middleware.go index a15fd075fca..b93cd517364 100644 --- a/pkg/middleware/middleware.go +++ b/pkg/middleware/middleware.go @@ -5,11 +5,11 @@ import ( "strings" "github.com/Unknwon/macaron" - "github.com/macaron-contrib/session" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/apikeygen" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/metrics" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" ) @@ -18,78 +18,137 @@ type Context struct { *macaron.Context *m.SignedInUser - Session session.Store + Session SessionStore IsSignedIn bool AllowAnonymous bool } func GetContextHandler() macaron.Handler { - return func(c *macaron.Context, sess session.Store) { + return func(c *macaron.Context) { ctx := &Context{ Context: c, - Session: sess, SignedInUser: &m.SignedInUser{}, + Session: GetSession(), IsSignedIn: false, AllowAnonymous: false, } - // try get account id from request - if userId := getRequestUserId(ctx); userId != 0 { - query := m.GetSignedInUserQuery{UserId: userId} - if err := bus.Dispatch(&query); err != nil { - log.Error(3, "Failed to get user by id, %v, %v", userId, err) - } else { - ctx.SignedInUser = query.Result - ctx.IsSignedIn = true - } - } else if keyString := getApiKey(ctx); keyString != "" { - // base64 decode key - decoded, err := apikeygen.Decode(keyString) - if err != nil { - ctx.JsonApiErr(401, "Invalid API key", err) - return - } - // fetch key - keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId} - if err := bus.Dispatch(&keyQuery); err != nil { - ctx.JsonApiErr(401, "Invalid API key", err) - return - } else { - apikey := keyQuery.Result - - // validate api key - if !apikeygen.IsValid(decoded, apikey.Key) { - ctx.JsonApiErr(401, "Invalid API key", err) - return - } - - ctx.IsSignedIn = true - ctx.SignedInUser = &m.SignedInUser{} - - // TODO: fix this - ctx.OrgRole = apikey.Role - ctx.ApiKeyId = apikey.Id - ctx.OrgId = apikey.OrgId - } - } else if setting.AnonymousEnabled { - orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName} - if err := bus.Dispatch(&orgQuery); err != nil { - log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err) - } else { - ctx.IsSignedIn = false - ctx.AllowAnonymous = true - ctx.SignedInUser = &m.SignedInUser{} - ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole) - ctx.OrgId = orgQuery.Result.Id - ctx.OrgName = orgQuery.Result.Name - } + // the order in which these are tested are important + // look for api key in Authorization header first + // then init session and look for userId in session + // then look for api key in session (special case for render calls via api) + // then test if anonymous access is enabled + if initContextWithApiKey(ctx) || + initContextWithUserSessionCookie(ctx) || + initContextWithApiKeyFromSession(ctx) || + initContextWithAnonymousUser(ctx) { } c.Map(ctx) } } +func initContextWithAnonymousUser(ctx *Context) bool { + if !setting.AnonymousEnabled { + return false + } + + orgQuery := m.GetOrgByNameQuery{Name: setting.AnonymousOrgName} + if err := bus.Dispatch(&orgQuery); err != nil { + log.Error(3, "Anonymous access organization error: '%s': %s", setting.AnonymousOrgName, err) + return false + } else { + ctx.IsSignedIn = false + ctx.AllowAnonymous = true + ctx.SignedInUser = &m.SignedInUser{} + ctx.OrgRole = m.RoleType(setting.AnonymousOrgRole) + ctx.OrgId = orgQuery.Result.Id + ctx.OrgName = orgQuery.Result.Name + return true + } +} + +func initContextWithUserSessionCookie(ctx *Context) bool { + // initialize session + if err := ctx.Session.Start(ctx); err != nil { + log.Error(3, "Failed to start session", err) + return false + } + + var userId int64 + if userId = getRequestUserId(ctx); userId == 0 { + return false + } + + query := m.GetSignedInUserQuery{UserId: userId} + if err := bus.Dispatch(&query); err != nil { + return false + } else { + ctx.SignedInUser = query.Result + ctx.IsSignedIn = true + return true + } +} + +func initContextWithApiKey(ctx *Context) bool { + var keyString string + if keyString = getApiKey(ctx); keyString == "" { + return false + } + + // base64 decode key + decoded, err := apikeygen.Decode(keyString) + if err != nil { + ctx.JsonApiErr(401, "Invalid API key", err) + return true + } + // fetch key + keyQuery := m.GetApiKeyByNameQuery{KeyName: decoded.Name, OrgId: decoded.OrgId} + if err := bus.Dispatch(&keyQuery); err != nil { + ctx.JsonApiErr(401, "Invalid API key", err) + return true + } else { + apikey := keyQuery.Result + + // validate api key + if !apikeygen.IsValid(decoded, apikey.Key) { + ctx.JsonApiErr(401, "Invalid API key", err) + return true + } + + ctx.IsSignedIn = true + ctx.SignedInUser = &m.SignedInUser{} + ctx.OrgRole = apikey.Role + ctx.ApiKeyId = apikey.Id + ctx.OrgId = apikey.OrgId + return true + } +} + +// special case for panel render calls with api key +func initContextWithApiKeyFromSession(ctx *Context) bool { + keyId := ctx.Session.Get(SESS_KEY_APIKEY) + if keyId == nil { + return false + } + + keyQuery := m.GetApiKeyByIdQuery{ApiKeyId: keyId.(int64)} + if err := bus.Dispatch(&keyQuery); err != nil { + log.Error(3, "Failed to get api key by id", err) + return false + } else { + apikey := keyQuery.Result + + ctx.IsSignedIn = true + ctx.SignedInUser = &m.SignedInUser{} + ctx.OrgRole = apikey.Role + ctx.ApiKeyId = apikey.Id + ctx.OrgId = apikey.OrgId + return true + } +} + // Handle handles and logs error by given status. func (ctx *Context) Handle(status int, title string, err error) { if err != nil { @@ -99,6 +158,15 @@ func (ctx *Context) Handle(status int, title string, err error) { } } + switch status { + case 200: + metrics.M_Page_Status_200.Inc(1) + case 404: + metrics.M_Page_Status_404.Inc(1) + case 500: + metrics.M_Page_Status_500.Inc(1) + } + ctx.Data["Title"] = title ctx.HTML(status, strconv.Itoa(status)) } @@ -128,7 +196,9 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) { switch status { case 404: resp["message"] = "Not Found" + metrics.M_Api_Status_500.Inc(1) case 500: + metrics.M_Api_Status_404.Inc(1) resp["message"] = "Internal Server Error" } diff --git a/pkg/middleware/session.go b/pkg/middleware/session.go index 1eb1dd5acbf..71f87b343ff 100644 --- a/pkg/middleware/session.go +++ b/pkg/middleware/session.go @@ -1,6 +1,111 @@ package middleware -const ( - SESS_KEY_USERID = "uid" - SESS_KEY_FAVORITES = "favorites" +import ( + "time" + + "github.com/Unknwon/macaron" + "github.com/macaron-contrib/session" + _ "github.com/macaron-contrib/session/mysql" + _ "github.com/macaron-contrib/session/postgres" + _ "github.com/macaron-contrib/session/redis" ) + +const ( + SESS_KEY_USERID = "uid" + SESS_KEY_APIKEY = "apikey_id" // used fror render requests with api keys +) + +var sessionManager *session.Manager +var sessionOptions session.Options + +func startSessionGC() { + sessionManager.GC() + time.AfterFunc(time.Duration(sessionOptions.Gclifetime)*time.Second, startSessionGC) +} + +func Sessioner(options session.Options) macaron.Handler { + var err error + sessionOptions = options + sessionManager, err = session.NewManager(options.Provider, options) + if err != nil { + panic(err) + } + + go startSessionGC() + + return func(ctx *Context) { + ctx.Next() + + if err = ctx.Session.Release(); err != nil { + panic("session(release): " + err.Error()) + } + } +} + +func GetSession() SessionStore { + return &SessionWrapper{manager: sessionManager} +} + +type SessionStore interface { + // Set sets value to given key in session. + Set(interface{}, interface{}) error + // Get gets value by given key in session. + Get(interface{}) interface{} + // ID returns current session ID. + ID() string + // Release releases session resource and save data to provider. + Release() error + // Destory deletes a session. + Destory(*Context) error + // init + Start(*Context) error +} + +type SessionWrapper struct { + session session.RawStore + manager *session.Manager +} + +func (s *SessionWrapper) Start(c *Context) error { + var err error + s.session, err = s.manager.Start(c.Context) + return err +} + +func (s *SessionWrapper) Set(k interface{}, v interface{}) error { + if s.session != nil { + return s.session.Set(k, v) + } + return nil +} + +func (s *SessionWrapper) Get(k interface{}) interface{} { + if s.session != nil { + return s.session.Get(k) + } + return nil +} + +func (s *SessionWrapper) ID() string { + if s.session != nil { + return s.session.ID() + } + return "" +} + +func (s *SessionWrapper) Release() error { + if s.session != nil { + return s.session.Release() + } + return nil +} + +func (s *SessionWrapper) Destory(c *Context) error { + if s.session != nil { + if err := s.manager.Destory(c.Context); err != nil { + return err + } + s.session = nil + } + return nil +} diff --git a/pkg/middleware/util.go b/pkg/middleware/util.go new file mode 100644 index 00000000000..0823e4de4bf --- /dev/null +++ b/pkg/middleware/util.go @@ -0,0 +1,21 @@ +package middleware + +import ( + "strings" + + "github.com/Unknwon/macaron" +) + +func Gziper() macaron.Handler { + macaronGziper := macaron.Gziper() + + return func(ctx *macaron.Context) { + requestPath := ctx.Req.URL.RequestURI() + // ignore datasource proxy requests + if strings.HasPrefix(requestPath, "/api/datasources/proxy") { + return + } + + ctx.Invoke(macaronGziper) + } +} diff --git a/pkg/models/apikey.go b/pkg/models/apikey.go index f550e4433db..a666cb30c61 100644 --- a/pkg/models/apikey.go +++ b/pkg/models/apikey.go @@ -55,6 +55,11 @@ type GetApiKeyByNameQuery struct { Result *ApiKey } +type GetApiKeyByIdQuery struct { + ApiKeyId int64 + Result *ApiKey +} + // ------------------------ // DTO & Projections diff --git a/pkg/models/dashboard_snapshot.go b/pkg/models/dashboard_snapshot.go new file mode 100644 index 00000000000..e8f37e2a236 --- /dev/null +++ b/pkg/models/dashboard_snapshot.go @@ -0,0 +1,49 @@ +package models + +import "time" + +// DashboardSnapshot model +type DashboardSnapshot struct { + Id int64 + Name string + Key string + DeleteKey string + OrgId int64 + UserId int64 + External bool + ExternalUrl string + + Expires time.Time + Created time.Time + Updated time.Time + + Dashboard map[string]interface{} +} + +// ----------------- +// COMMANDS + +type CreateDashboardSnapshotCommand struct { + Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` + Expires int64 `json:"expires"` + + // these are passed when storing an external snapshot ref + External bool `json:"external"` + Key string `json:"key"` + DeleteKey string `json:"deleteKey"` + + OrgId int64 `json:"-"` + UserId int64 `json:"-"` + + Result *DashboardSnapshot +} + +type DeleteDashboardSnapshotCommand struct { + DeleteKey string `json:"-"` +} + +type GetDashboardSnapshotQuery struct { + Key string + + Result *DashboardSnapshot +} diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 7f39dbc74c7..159d4e1a3c6 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -2,9 +2,10 @@ package models import ( "errors" - "regexp" "strings" "time" + + "github.com/dalu/slug" ) // Typed errors @@ -82,9 +83,7 @@ func (dash *Dashboard) GetString(prop string) string { // UpdateSlug updates the slug func (dash *Dashboard) UpdateSlug() { title := strings.ToLower(dash.Data["title"].(string)) - re := regexp.MustCompile("[^\\w ]+") - re2 := regexp.MustCompile("\\s") - dash.Slug = re2.ReplaceAllString(re.ReplaceAllString(title, ""), "-") + dash.Slug = slug.Make(title) } // diff --git a/pkg/models/models.go b/pkg/models/models.go index 189e594576b..c38f0c5a391 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -1,5 +1,7 @@ package models +import "errors" + type OAuthType int const ( @@ -7,3 +9,5 @@ const ( GOOGLE TWITTER ) + +var ErrNotFound = errors.New("Not found") diff --git a/pkg/models/stats.go b/pkg/models/stats.go new file mode 100644 index 00000000000..0d83882e666 --- /dev/null +++ b/pkg/models/stats.go @@ -0,0 +1,11 @@ +package models + +type SystemStats struct { + DashboardCount int + UserCount int + OrgCount int +} + +type GetSystemStatsQuery struct { + Result *SystemStats +} diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go index d6a138ac76e..4d3e2c98836 100644 --- a/pkg/plugins/plugins_test.go +++ b/pkg/plugins/plugins_test.go @@ -10,7 +10,7 @@ import ( func TestPluginScans(t *testing.T) { Convey("When scaning for plugins", t, func() { - path, _ := filepath.Abs("../../src/app/plugins") + path, _ := filepath.Abs("../../public/app/plugins") err := scan(path) So(err, ShouldBeNil) diff --git a/pkg/services/sqlstore/apikey.go b/pkg/services/sqlstore/apikey.go index f4fc88fa211..1cca7b5e40b 100644 --- a/pkg/services/sqlstore/apikey.go +++ b/pkg/services/sqlstore/apikey.go @@ -10,6 +10,7 @@ import ( func init() { bus.AddHandler("sql", GetApiKeys) + bus.AddHandler("sql", GetApiKeyById) bus.AddHandler("sql", GetApiKeyByName) bus.AddHandler("sql", DeleteApiKey) bus.AddHandler("sql", AddApiKey) @@ -49,6 +50,20 @@ func AddApiKey(cmd *m.AddApiKeyCommand) error { }) } +func GetApiKeyById(query *m.GetApiKeyByIdQuery) error { + var apikey m.ApiKey + has, err := x.Id(query.ApiKeyId).Get(&apikey) + + if err != nil { + return err + } else if has == false { + return m.ErrInvalidApiKey + } + + query.Result = &apikey + return nil +} + func GetApiKeyByName(query *m.GetApiKeyByNameQuery) error { var apikey m.ApiKey has, err := x.Where("org_id=? AND name=?", query.OrgId, query.KeyName).Get(&apikey) diff --git a/pkg/services/sqlstore/apikey_test.go b/pkg/services/sqlstore/apikey_test.go index e86a1c0db33..790c8837def 100644 --- a/pkg/services/sqlstore/apikey_test.go +++ b/pkg/services/sqlstore/apikey_test.go @@ -21,8 +21,8 @@ func TestApiKeyDataAccess(t *testing.T) { Convey("Should be able to get key by name", func() { query := m.GetApiKeyByNameQuery{KeyName: "hello", OrgId: 1} err = GetApiKeyByName(&query) - So(err, ShouldBeNil) + So(err, ShouldBeNil) So(query.Result, ShouldNotBeNil) }) diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index bf748b600f4..7dbebd94e4c 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -6,6 +6,7 @@ import ( "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/metrics" m "github.com/grafana/grafana/pkg/models" ) @@ -48,6 +49,7 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error { } if dash.Id == 0 { + metrics.M_Models_Dashboard_Insert.Inc(1) _, err = sess.Insert(dash) } else { dash.Version += 1 @@ -138,7 +140,7 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error { query.Limit = 300 } - sql.WriteString(fmt.Sprintf(" LIMIT %d", query.Limit)) + sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT %d", query.Limit)) var res []DashboardSearchProjection err := x.Sql(sql.String(), params...).Find(&res) diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go new file mode 100644 index 00000000000..0bbb01ed6bd --- /dev/null +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -0,0 +1,65 @@ +package sqlstore + +import ( + "time" + + "github.com/go-xorm/xorm" + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", CreateDashboardSnapshot) + bus.AddHandler("sql", GetDashboardSnapshot) + bus.AddHandler("sql", DeleteDashboardSnapshot) +} + +func CreateDashboardSnapshot(cmd *m.CreateDashboardSnapshotCommand) error { + return inTransaction(func(sess *xorm.Session) error { + + // never + var expires = time.Now().Add(time.Hour * 24 * 365 * 50) + if cmd.Expires > 0 { + expires = time.Now().Add(time.Second * time.Duration(cmd.Expires)) + } + + snapshot := &m.DashboardSnapshot{ + Key: cmd.Key, + DeleteKey: cmd.DeleteKey, + OrgId: cmd.OrgId, + UserId: cmd.UserId, + External: cmd.External, + Dashboard: cmd.Dashboard, + Expires: expires, + Created: time.Now(), + Updated: time.Now(), + } + + _, err := sess.Insert(snapshot) + cmd.Result = snapshot + + return err + }) +} + +func DeleteDashboardSnapshot(cmd *m.DeleteDashboardSnapshotCommand) error { + return inTransaction(func(sess *xorm.Session) error { + var rawSql = "DELETE FROM dashboard_snapshot WHERE delete_key=?" + _, err := sess.Exec(rawSql, cmd.DeleteKey) + return err + }) +} + +func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error { + snapshot := m.DashboardSnapshot{Key: query.Key} + has, err := x.Get(&snapshot) + + if err != nil { + return err + } else if has == false { + return m.ErrNotFound + } + + query.Result = &snapshot + return nil +} diff --git a/pkg/services/sqlstore/dashboard_snapshot_test.go b/pkg/services/sqlstore/dashboard_snapshot_test.go new file mode 100644 index 00000000000..5301f0f1cc9 --- /dev/null +++ b/pkg/services/sqlstore/dashboard_snapshot_test.go @@ -0,0 +1,37 @@ +package sqlstore + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + + m "github.com/grafana/grafana/pkg/models" +) + +func TestDashboardSnapshotDBAccess(t *testing.T) { + + Convey("Testing DashboardSnapshot data access", t, func() { + InitTestDB(t) + + Convey("Given saved snaphot", func() { + cmd := m.CreateDashboardSnapshotCommand{ + Key: "hej", + Dashboard: map[string]interface{}{ + "hello": "mupp", + }, + } + err := CreateDashboardSnapshot(&cmd) + So(err, ShouldBeNil) + + Convey("Should be able to get snaphot by key", func() { + query := m.GetDashboardSnapshotQuery{Key: "hej"} + err = GetDashboardSnapshot(&query) + So(err, ShouldBeNil) + + So(query.Result, ShouldNotBeNil) + So(query.Result.Dashboard["hello"], ShouldEqual, "mupp") + }) + + }) + }) +} diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index 9c3dd6c2902..ebb2ad977b1 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -89,8 +89,8 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { func updateIsDefaultFlag(ds *m.DataSource, sess *xorm.Session) error { // Handle is default flag if ds.IsDefault { - rawSql := "UPDATE data_source SET is_default = 0 WHERE org_id=? AND id <> ?" - if _, err := sess.Exec(rawSql, ds.OrgId, ds.Id); err != nil { + rawSql := "UPDATE data_source SET is_default=? WHERE org_id=? AND id <> ?" + if _, err := sess.Exec(rawSql, false, ds.OrgId, ds.Id); err != nil { return err } } diff --git a/pkg/services/sqlstore/migrations/dashboard_mig.go b/pkg/services/sqlstore/migrations/dashboard_mig.go index 6ded17ff0e7..5d440d85ebc 100644 --- a/pkg/services/sqlstore/migrations/dashboard_mig.go +++ b/pkg/services/sqlstore/migrations/dashboard_mig.go @@ -86,4 +86,10 @@ func addDashboardMigration(mg *Migrator) { })) mg.AddMigration("drop table dashboard_v1", NewDropTableMigration("dashboard_v1")) + + // change column type of dashboard.data + mg.AddMigration("alter dashboard.data to mediumtext v1", new(RawSqlMigration). + Sqlite("SELECT 0 WHERE 0;"). + Postgres("SELECT 0;"). + Mysql("ALTER TABLE dashboard MODIFY data MEDIUMTEXT;")) } diff --git a/pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go b/pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go new file mode 100644 index 00000000000..b08cc451e55 --- /dev/null +++ b/pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go @@ -0,0 +1,57 @@ +package migrations + +import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator" + +func addDashboardSnapshotMigrations(mg *Migrator) { + snapshotV4 := Table{ + Name: "dashboard_snapshot", + Columns: []*Column{ + {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, + {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "key", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "dashboard", Type: DB_Text, Nullable: false}, + {Name: "expires", Type: DB_DateTime, Nullable: false}, + {Name: "created", Type: DB_DateTime, Nullable: false}, + {Name: "updated", Type: DB_DateTime, Nullable: false}, + }, + Indices: []*Index{ + {Cols: []string{"key"}, Type: UniqueIndex}, + }, + } + + // add v4 + mg.AddMigration("create dashboard_snapshot table v4", NewAddTableMigration(snapshotV4)) + mg.AddMigration("drop table dashboard_snapshot_v4 #1", NewDropTableMigration("dashboard_snapshot")) + + snapshotV5 := Table{ + Name: "dashboard_snapshot", + Columns: []*Column{ + {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, + {Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "key", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "delete_key", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "org_id", Type: DB_BigInt, Nullable: false}, + {Name: "user_id", Type: DB_BigInt, Nullable: false}, + {Name: "external", Type: DB_Bool, Nullable: false}, + {Name: "external_url", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "dashboard", Type: DB_Text, Nullable: false}, + {Name: "expires", Type: DB_DateTime, Nullable: false}, + {Name: "created", Type: DB_DateTime, Nullable: false}, + {Name: "updated", Type: DB_DateTime, Nullable: false}, + }, + Indices: []*Index{ + {Cols: []string{"key"}, Type: UniqueIndex}, + {Cols: []string{"delete_key"}, Type: UniqueIndex}, + {Cols: []string{"user_id"}}, + }, + } + + mg.AddMigration("create dashboard_snapshot table v5 #2", NewAddTableMigration(snapshotV5)) + addTableIndicesMigrations(mg, "v5", snapshotV5) + + // change column type of dashboard + mg.AddMigration("alter dashboard_snapshot to mediumtext v2", new(RawSqlMigration). + Sqlite("SELECT 0 WHERE 0;"). + Postgres("SELECT 0;"). + Mysql("ALTER TABLE dashboard_snapshot MODIFY dashboard MEDIUMTEXT;")) +} diff --git a/pkg/services/sqlstore/migrations/datasource_mig.go b/pkg/services/sqlstore/migrations/datasource_mig.go index 924e1a16189..4f046b1f8e9 100644 --- a/pkg/services/sqlstore/migrations/datasource_mig.go +++ b/pkg/services/sqlstore/migrations/datasource_mig.go @@ -95,5 +95,5 @@ func addDataSourceMigration(mg *Migrator) { "updated": "updated", })) - mg.AddMigration("Drop old table data_source_v1", NewDropTableMigration("data_source_old")) + mg.AddMigration("Drop old table data_source_v1 #2", NewDropTableMigration("data_source_v1")) } diff --git a/pkg/services/sqlstore/migrations/migrations.go b/pkg/services/sqlstore/migrations/migrations.go index c48167d97e5..329c6187c9d 100644 --- a/pkg/services/sqlstore/migrations/migrations.go +++ b/pkg/services/sqlstore/migrations/migrations.go @@ -15,6 +15,7 @@ func AddMigrations(mg *Migrator) { addDashboardMigration(mg) addDataSourceMigration(mg) addApiKeyMigrations(mg) + addDashboardSnapshotMigrations(mg) } func addMigrationLogMigrations(mg *Migrator) { diff --git a/pkg/services/sqlstore/migrator/migrations.go b/pkg/services/sqlstore/migrator/migrations.go index e596ef6c171..a65c7ec7e81 100644 --- a/pkg/services/sqlstore/migrator/migrations.go +++ b/pkg/services/sqlstore/migrator/migrations.go @@ -25,8 +25,9 @@ func (m *MigrationBase) GetCondition() MigrationCondition { type RawSqlMigration struct { MigrationBase - sqlite string - mysql string + sqlite string + mysql string + postgres string } func (m *RawSqlMigration) Sql(dialect Dialect) string { @@ -35,6 +36,8 @@ func (m *RawSqlMigration) Sql(dialect Dialect) string { return m.mysql case SQLITE: return m.sqlite + case POSTGRES: + return m.postgres } panic("db type not supported") @@ -50,6 +53,11 @@ func (m *RawSqlMigration) Mysql(sql string) *RawSqlMigration { return m } +func (m *RawSqlMigration) Postgres(sql string) *RawSqlMigration { + m.postgres = sql + return m +} + type AddColumnMigration struct { MigrationBase tableName string diff --git a/pkg/services/sqlstore/sqlstore.go b/pkg/services/sqlstore/sqlstore.go index 218f24f0142..2dd483651c0 100644 --- a/pkg/services/sqlstore/sqlstore.go +++ b/pkg/services/sqlstore/sqlstore.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path" + "path/filepath" "strings" "github.com/grafana/grafana/pkg/bus" @@ -22,7 +23,6 @@ import ( var ( x *xorm.Engine dialect migrator.Dialect - tables []interface{} HasEngine bool @@ -34,24 +34,29 @@ var ( ) func EnsureAdminUser() { - adminQuery := m.GetUserByLoginQuery{LoginOrEmail: setting.AdminUser} + statsQuery := m.GetSystemStatsQuery{} - if err := bus.Dispatch(&adminQuery); err == m.ErrUserNotFound { - cmd := m.CreateUserCommand{} - cmd.Login = setting.AdminUser - cmd.Email = setting.AdminUser + "@localhost" - cmd.Password = setting.AdminPassword - cmd.IsAdmin = true - - if err = bus.Dispatch(&cmd); err != nil { - log.Error(3, "Failed to create default admin user", err) - return - } - - log.Info("Created default admin user: %v", setting.AdminUser) - } else if err != nil { - log.Error(3, "Could not determine if admin user exists: %v", err) + if err := bus.Dispatch(&statsQuery); err != nil { + log.Fatal(3, "Could not determine if admin user exists: %v", err) + return } + + if statsQuery.Result.UserCount > 0 { + return + } + + cmd := m.CreateUserCommand{} + cmd.Login = setting.AdminUser + cmd.Email = setting.AdminUser + "@localhost" + cmd.Password = setting.AdminPassword + cmd.IsAdmin = true + + if err := bus.Dispatch(&cmd); err != nil { + log.Error(3, "Failed to create default admin user", err) + return + } + + log.Info("Created default admin user: %v", setting.AdminUser) } func NewEngine() { @@ -80,12 +85,8 @@ func SetEngine(engine *xorm.Engine, enableLog bool) (err error) { return fmt.Errorf("Sqlstore::Migration failed err: %v\n", err) } - if err := x.Sync2(tables...); err != nil { - return fmt.Errorf("sync database struct error: %v\n", err) - } - if enableLog { - logPath := path.Join(setting.LogRootPath, "xorm.log") + logPath := path.Join(setting.LogsPath, "xorm.log") os.MkdirAll(path.Dir(logPath), os.ModePerm) f, err := os.Create(logPath) @@ -94,11 +95,13 @@ func SetEngine(engine *xorm.Engine, enableLog bool) (err error) { } x.Logger = xorm.NewSimpleLogger(f) - x.ShowSQL = true - x.ShowInfo = true - x.ShowDebug = true - x.ShowErr = true - x.ShowWarn = true + if setting.Env == setting.DEV { + x.ShowSQL = false + x.ShowInfo = false + x.ShowDebug = false + x.ShowErr = true + x.ShowWarn = true + } } return nil @@ -124,8 +127,11 @@ func getEngine() (*xorm.Engine, error) { cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s", DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode) case "sqlite3": + if !filepath.IsAbs(DbCfg.Path) { + DbCfg.Path = filepath.Join(setting.DataPath, DbCfg.Path) + } os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm) - cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc" + cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc&_loc=Local" default: return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type) } diff --git a/pkg/services/sqlstore/sqlstore.goconvey b/pkg/services/sqlstore/sqlstore.goconvey new file mode 100644 index 00000000000..92feb5268a5 --- /dev/null +++ b/pkg/services/sqlstore/sqlstore.goconvey @@ -0,0 +1 @@ +-timeout=10s diff --git a/pkg/services/sqlstore/sqlutil/sqlutil.go b/pkg/services/sqlstore/sqlutil/sqlutil.go index f873a1a2f9f..075b56c1c18 100644 --- a/pkg/services/sqlstore/sqlutil/sqlutil.go +++ b/pkg/services/sqlstore/sqlutil/sqlutil.go @@ -11,7 +11,7 @@ type TestDB struct { ConnStr string } -var TestDB_Sqlite3 = TestDB{DriverName: "sqlite3", ConnStr: ":memory:"} +var TestDB_Sqlite3 = TestDB{DriverName: "sqlite3", ConnStr: ":memory:?_loc=Local"} var TestDB_Mysql = TestDB{DriverName: "mysql", ConnStr: "grafana:password@tcp(localhost:3306)/grafana_tests?charset=utf8"} var TestDB_Postgres = TestDB{DriverName: "postgres", ConnStr: "user=grafanatest password=grafanatest host=localhost port=5432 dbname=grafanatest sslmode=disable"} diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go new file mode 100644 index 00000000000..7995dd43f38 --- /dev/null +++ b/pkg/services/sqlstore/stats.go @@ -0,0 +1,36 @@ +package sqlstore + +import ( + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +func init() { + bus.AddHandler("sql", GetSystemStats) +} + +func GetSystemStats(query *m.GetSystemStatsQuery) error { + var rawSql = `SELECT + ( + SELECT COUNT(*) + FROM ` + dialect.Quote("user") + ` + ) AS user_count, + ( + SELECT COUNT(*) + FROM ` + dialect.Quote("org") + ` + ) AS org_count, + ( + SELECT COUNT(*) + FROM ` + dialect.Quote("dashboard") + ` + ) AS dashboard_count + ` + + var stats m.SystemStats + _, err := x.Sql(rawSql).Get(&stats) + if err != nil { + return err + } + + query.Result = &stats + return err +} diff --git a/pkg/services/sqlstore/xorm.log b/pkg/services/sqlstore/xorm.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index b8d038dbc29..46f98e32efe 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -4,19 +4,22 @@ package setting import ( + "bytes" + "encoding/json" "fmt" "net/url" "os" "path" "path/filepath" + "regexp" "runtime" "strings" - "github.com/Unknwon/com" "github.com/macaron-contrib/session" "gopkg.in/ini.v1" "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/util" ) type Scheme string @@ -44,10 +47,14 @@ var ( BuildCommit string BuildStamp int64 + // Paths + LogsPath string + HomePath string + DataPath string + // Log settings. - LogRootPath string - LogModes []string - LogConfigs []string + LogModes []string + LogConfigs []util.DynMap // Http server options Protocol Scheme @@ -83,53 +90,32 @@ var ( SessionOptions session.Options // Global setting objects. - WorkDir string Cfg *ini.File ConfRootPath string - CustomPath string // Custom directory path. - ProdMode bool - RunUser string IsWindows bool // PhantomJs Rendering ImagesDir string PhantomDir string - configFiles []string + // for logging purposes + configFiles []string + appliedCommandLineProperties []string + appliedEnvOverrides []string + + ReportingEnabled bool + GoogleAnalyticsId string ) +type CommandLineArgs struct { + Config string + HomePath string + Args []string +} + func init() { IsWindows = runtime.GOOS == "windows" log.NewLogger(0, "console", `{"level": 0}`) - WorkDir, _ = filepath.Abs(".") -} - -func findConfigFiles(customConfigFile string) { - ConfRootPath = path.Join(WorkDir, "conf") - configFiles = make([]string, 0) - - configFile := path.Join(ConfRootPath, "defaults.ini") - if com.IsFile(configFile) { - configFiles = append(configFiles, configFile) - } - - configFile = path.Join(ConfRootPath, "dev.ini") - if com.IsFile(configFile) { - configFiles = append(configFiles, configFile) - } - - configFile = path.Join(ConfRootPath, "custom.ini") - if com.IsFile(configFile) { - configFiles = append(configFiles, configFile) - } - - if customConfigFile != "" { - configFiles = append(configFiles, customConfigFile) - } - - if len(configFiles) == 0 { - log.Fatal(3, "Could not find any config file") - } } func parseAppUrlAndSubUrl(section *ini.Section) (string, string) { @@ -152,7 +138,8 @@ func ToAbsUrl(relativeUrl string) string { return AppUrl + relativeUrl } -func loadEnvVariableOverrides() { +func applyEnvVariableOverrides() { + appliedEnvOverrides = make([]string, 0) for _, section := range Cfg.Sections() { for _, key := range section.Keys() { sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1)) @@ -161,32 +148,189 @@ func loadEnvVariableOverrides() { envValue := os.Getenv(envKey) if len(envValue) > 0 { - log.Info("Setting: ENV override found: %s", envKey) key.SetValue(envValue) + appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue)) } } } } -func NewConfigContext(config string) { - findConfigFiles(config) +func applyCommandLineDefaultProperties(props map[string]string) { + appliedCommandLineProperties = make([]string, 0) + for _, section := range Cfg.Sections() { + for _, key := range section.Keys() { + keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name()) + value, exists := props[keyString] + if exists { + key.SetValue(value) + appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value)) + } + } + } +} - var err error +func applyCommandLineProperties(props map[string]string) { + for _, section := range Cfg.Sections() { + for _, key := range section.Keys() { + keyString := fmt.Sprintf("%s.%s", section.Name(), key.Name()) + value, exists := props[keyString] + if exists { + key.SetValue(value) + appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value)) + } + } + } +} - for i, file := range configFiles { - if i == 0 { - Cfg, err = ini.Load(configFiles[i]) - } else { - err = Cfg.Append(configFiles[i]) +func getCommandLineProperties(args []string) map[string]string { + props := make(map[string]string) + + for _, arg := range args { + if !strings.HasPrefix(arg, "cfg:") { + continue } - if err != nil { - log.Fatal(4, "Fail to parse config file: %v, error: %v", file, err) + trimmed := strings.TrimPrefix(arg, "cfg:") + parts := strings.Split(trimmed, "=") + if len(parts) != 2 { + log.Fatal(3, "Invalid command line argument", arg) + return nil + } + + props[parts[0]] = parts[1] + } + return props +} + +func makeAbsolute(path string, root string) string { + if filepath.IsAbs(path) { + return path + } + return filepath.Join(root, path) +} + +func evalEnvVarExpression(value string) string { + regex := regexp.MustCompile(`\${(\w+)}`) + return regex.ReplaceAllStringFunc(value, func(envVar string) string { + envVar = strings.TrimPrefix(envVar, "${") + envVar = strings.TrimSuffix(envVar, "}") + envValue := os.Getenv(envVar) + return envValue + }) +} + +func evalConfigValues() { + for _, section := range Cfg.Sections() { + for _, key := range section.Keys() { + key.SetValue(evalEnvVarExpression(key.Value())) + } + } +} + +func loadSpecifedConfigFile(configFile string) { + if configFile == "" { + configFile = filepath.Join(HomePath, "conf/custom.ini") + // return without error if custom file does not exist + if !pathExists(configFile) { + return } } - loadEnvVariableOverrides() - initLogging() + userConfig, err := ini.Load(configFile) + userConfig.BlockMode = false + if err != nil { + log.Fatal(3, "Failed to parse %v, %v", configFile, err) + } + + for _, section := range userConfig.Sections() { + for _, key := range section.Keys() { + if key.Value() == "" { + continue + } + + defaultSec, err := Cfg.GetSection(section.Name()) + if err != nil { + log.Fatal(3, "Unknown config section %s defined in %s", section.Name(), configFile) + } + defaultKey, err := defaultSec.GetKey(key.Name()) + if err != nil { + log.Fatal(3, "Unknown config key %s defined in section %s, in file", key.Name(), section.Name(), configFile) + } + defaultKey.SetValue(key.Value()) + } + } + + configFiles = append(configFiles, configFile) +} + +func loadConfiguration(args *CommandLineArgs) { + var err error + + // load config defaults + defaultConfigFile := path.Join(HomePath, "conf/defaults.ini") + configFiles = append(configFiles, defaultConfigFile) + + Cfg, err = ini.Load(defaultConfigFile) + Cfg.BlockMode = false + + if err != nil { + log.Fatal(3, "Failed to parse defaults.ini, %v", err) + } + + // command line props + commandLineProps := getCommandLineProperties(args.Args) + + // load default overrides + applyCommandLineDefaultProperties(commandLineProps) + + // load specified config file + loadSpecifedConfigFile(args.Config) + + // apply environment overrides + applyEnvVariableOverrides() + + // apply command line overrides + applyCommandLineProperties(commandLineProps) + + // evaluate config values containing environment variables + evalConfigValues() +} + +func pathExists(path string) bool { + _, err := os.Stat(path) + if err == nil { + return true + } + if os.IsNotExist(err) { + return false + } + return false +} + +func setHomePath(args *CommandLineArgs) { + if args.HomePath != "" { + HomePath = args.HomePath + return + } + + HomePath, _ = filepath.Abs(".") + // check if homepath is correct + if pathExists(filepath.Join(HomePath, "conf/defaults.ini")) { + return + } + + // try down one path + if pathExists(filepath.Join(HomePath, "../conf/defaults.ini")) { + HomePath = filepath.Join(HomePath, "../") + } +} + +func NewConfigContext(args *CommandLineArgs) { + setHomePath(args) + loadConfiguration(args) + + DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath) + initLogging(args) AppName = Cfg.Section("").Key("app_name").MustString("Grafana") Env = Cfg.Section("").Key("app_mode").MustString("development") @@ -205,7 +349,7 @@ func NewConfigContext(config string) { HttpAddr = server.Key("http_addr").MustString("0.0.0.0") HttpPort = server.Key("http_port").MustString("3000") - StaticRootPath = server.Key("static_root_path").MustString(path.Join(WorkDir, "webapp")) + StaticRootPath = makeAbsolute(server.Key("static_root_path").String(), HomePath) RouterLogging = server.Key("router_logging").MustBool(false) EnableGzip = server.Key("enable_gzip").MustBool(false) @@ -230,8 +374,12 @@ func NewConfigContext(config string) { AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String() // PhantomJS rendering - ImagesDir = "data/png" - PhantomDir = "vendor/phantomjs" + ImagesDir = filepath.Join(DataPath, "png") + PhantomDir = filepath.Join(HomePath, "vendor/phantomjs") + + analytics := Cfg.Section("analytics") + ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true) + GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String() readSessionConfig() } @@ -239,33 +387,40 @@ func NewConfigContext(config string) { func readSessionConfig() { sec := Cfg.Section("session") SessionOptions = session.Options{} - SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql"}) + SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql", "postgres"}) SessionOptions.ProviderConfig = strings.Trim(sec.Key("provider_config").String(), "\" ") SessionOptions.CookieName = sec.Key("cookie_name").MustString("grafana_sess") SessionOptions.CookiePath = AppSubUrl SessionOptions.Secure = sec.Key("cookie_secure").MustBool() SessionOptions.Gclifetime = Cfg.Section("session").Key("gc_interval_time").MustInt64(86400) SessionOptions.Maxlifetime = Cfg.Section("session").Key("session_life_time").MustInt64(86400) + SessionOptions.IDLength = 16 if SessionOptions.Provider == "file" { + SessionOptions.ProviderConfig = makeAbsolute(SessionOptions.ProviderConfig, DataPath) os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm) } + + if SessionOptions.CookiePath == "" { + SessionOptions.CookiePath = "/" + } } -var logLevels = map[string]string{ - "Trace": "0", - "Debug": "1", - "Info": "2", - "Warn": "3", - "Error": "4", - "Critical": "5", +var logLevels = map[string]int{ + "Trace": 0, + "Debug": 1, + "Info": 2, + "Warn": 3, + "Error": 4, + "Critical": 5, } -func initLogging() { +func initLogging(args *CommandLineArgs) { // Get and check log mode. LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), ",") - LogRootPath = Cfg.Section("log").Key("root_path").MustString(path.Join(WorkDir, "/data/log")) - LogConfigs = make([]string, len(LogModes)) + LogsPath = makeAbsolute(Cfg.Section("paths").Key("logs").String(), HomePath) + + LogConfigs = make([]util.DynMap, len(LogModes)) for i, mode := range LogModes { mode = strings.TrimSpace(mode) sec, err := Cfg.GetSection("log." + mode) @@ -284,43 +439,76 @@ func initLogging() { // Generate log configuration. switch mode { case "console": - LogConfigs[i] = fmt.Sprintf(`{"level":%s}`, level) + LogConfigs[i] = util.DynMap{"level": level} case "file": - logPath := sec.Key("file_name").MustString(path.Join(LogRootPath, "grafana.log")) - os.MkdirAll(path.Dir(logPath), os.ModePerm) - LogConfigs[i] = fmt.Sprintf( - `{"level":%s,"filename":"%s","rotate":%v,"maxlines":%d,"maxsize":%d,"daily":%v,"maxdays":%d}`, level, - logPath, - sec.Key("log_rotate").MustBool(true), - sec.Key("max_lines").MustInt(1000000), - 1< 0 { + text.WriteString("Command lines overrides:\n") + for i, prop := range appliedCommandLineProperties { + text.WriteString(fmt.Sprintf(" [%d]: %s\n", i, prop)) + } + } + + if len(appliedEnvOverrides) > 0 { + text.WriteString("\tEnvironment variables used:\n") + for i, prop := range appliedCommandLineProperties { + text.WriteString(fmt.Sprintf(" [%d]: %s\n", i, prop)) + } + } + + text.WriteString("Paths:\n") + text.WriteString(fmt.Sprintf(" home: %s\n", HomePath)) + text.WriteString(fmt.Sprintf(" data: %s\n", DataPath)) + text.WriteString(fmt.Sprintf(" logs: %s\n", LogsPath)) + + log.Info(text.String()) } diff --git a/pkg/setting/setting_oauth.go b/pkg/setting/setting_oauth.go index 43a022079b7..db2f0fb3802 100644 --- a/pkg/setting/setting_oauth.go +++ b/pkg/setting/setting_oauth.go @@ -5,6 +5,9 @@ type OAuthInfo struct { Scopes []string AuthUrl, TokenUrl string Enabled bool + AllowedDomains []string + ApiUrl string + AllowSignup bool } type OAuther struct { diff --git a/pkg/setting/setting_test.go b/pkg/setting/setting_test.go index 253eb94f123..73ccabd2dbc 100644 --- a/pkg/setting/setting_test.go +++ b/pkg/setting/setting_test.go @@ -10,12 +10,10 @@ import ( func TestLoadingSettings(t *testing.T) { - WorkDir, _ = filepath.Abs("../../") - Convey("Testing loading settings from ini file", t, func() { Convey("Given the default ini files", func() { - NewConfigContext("") + NewConfigContext(&CommandLineArgs{HomePath: "../../"}) So(AppName, ShouldEqual, "Grafana") So(AdminUser, ShouldEqual, "admin") @@ -23,9 +21,71 @@ func TestLoadingSettings(t *testing.T) { Convey("Should be able to override via environment variables", func() { os.Setenv("GF_SECURITY_ADMIN_USER", "superduper") - NewConfigContext("") + NewConfigContext(&CommandLineArgs{HomePath: "../../"}) So(AdminUser, ShouldEqual, "superduper") + So(DataPath, ShouldEqual, filepath.Join(HomePath, "data")) + So(LogsPath, ShouldEqual, filepath.Join(DataPath, "log")) + }) + + Convey("Should get property map from command line args array", func() { + props := getCommandLineProperties([]string{"cfg:test=value", "cfg:map.test=1"}) + + So(len(props), ShouldEqual, 2) + So(props["test"], ShouldEqual, "value") + So(props["map.test"], ShouldEqual, "1") + }) + + Convey("Should be able to override via command line", func() { + NewConfigContext(&CommandLineArgs{ + HomePath: "../../", + Args: []string{"cfg:paths.data=/tmp/data", "cfg:paths.logs=/tmp/logs"}, + }) + + So(DataPath, ShouldEqual, "/tmp/data") + So(LogsPath, ShouldEqual, "/tmp/logs") + }) + + Convey("Should be able to override defaults via command line", func() { + NewConfigContext(&CommandLineArgs{ + HomePath: "../../", + Args: []string{ + "cfg:default.server.domain=test2", + }, + Config: filepath.Join(HomePath, "tests/config-files/override.ini"), + }) + + So(Domain, ShouldEqual, "test2") + }) + + Convey("Defaults can be overriden in specified config file", func() { + NewConfigContext(&CommandLineArgs{ + HomePath: "../../", + Config: filepath.Join(HomePath, "tests/config-files/override.ini"), + Args: []string{"cfg:default.paths.data=/tmp/data"}, + }) + + So(DataPath, ShouldEqual, "/tmp/override") + }) + + Convey("Command line overrides specified config file", func() { + NewConfigContext(&CommandLineArgs{ + HomePath: "../../", + Config: filepath.Join(HomePath, "tests/config-files/override.ini"), + Args: []string{"cfg:paths.data=/tmp/data"}, + }) + + So(DataPath, ShouldEqual, "/tmp/data") + }) + + Convey("Can use environment variables in config values", func() { + os.Setenv("GF_DATA_PATH", "/tmp/env_override") + NewConfigContext(&CommandLineArgs{ + HomePath: "../../", + Args: []string{"cfg:paths.data=${GF_DATA_PATH}"}, + }) + + So(DataPath, ShouldEqual, "/tmp/env_override") }) }) diff --git a/pkg/social/social.go b/pkg/social/social.go index 7e650d11ba0..47c7ea5dc38 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -2,11 +2,13 @@ package social import ( "encoding/json" + "fmt" "strconv" "strings" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/setting" + "golang.org/x/net/context" "golang.org/x/oauth2" ) @@ -22,9 +24,11 @@ type BasicUserInfo struct { type SocialConnector interface { Type() int UserInfo(token *oauth2.Token) (*BasicUserInfo, error) + IsEmailAllowed(email string) bool + IsSignupAllowed() bool AuthCodeURL(state string, opts ...oauth2.AuthCodeOption) string - Exchange(ctx oauth2.Context, code string) (*oauth2.Token, error) + Exchange(ctx context.Context, code string) (*oauth2.Token, error) } var ( @@ -41,12 +45,15 @@ func NewOAuthService() { for _, name := range allOauthes { sec := setting.Cfg.Section("auth." + name) info := &setting.OAuthInfo{ - ClientId: sec.Key("client_id").String(), - ClientSecret: sec.Key("client_secret").String(), - Scopes: sec.Key("scopes").Strings(" "), - AuthUrl: sec.Key("auth_url").String(), - TokenUrl: sec.Key("token_url").String(), - Enabled: sec.Key("enabled").MustBool(), + ClientId: sec.Key("client_id").String(), + ClientSecret: sec.Key("client_secret").String(), + Scopes: sec.Key("scopes").Strings(" "), + AuthUrl: sec.Key("auth_url").String(), + TokenUrl: sec.Key("token_url").String(), + ApiUrl: sec.Key("api_url").String(), + Enabled: sec.Key("enabled").MustBool(), + AllowedDomains: sec.Key("allowed_domains").Strings(" "), + AllowSignup: sec.Key("allow_sign_up").MustBool(), } if !info.Enabled { @@ -68,25 +75,50 @@ func NewOAuthService() { // GitHub. if name == "github" { setting.OAuthService.GitHub = true - SocialMap["github"] = &SocialGithub{Config: &config} + SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup} } // Google. if name == "google" { setting.OAuthService.Google = true - SocialMap["google"] = &SocialGoogle{Config: &config} + SocialMap["google"] = &SocialGoogle{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup} } } } +func isEmailAllowed(email string, allowedDomains []string) bool { + if len(allowedDomains) == 0 { + return true + } + + valid := false + for _, domain := range allowedDomains { + emailSuffix := fmt.Sprintf("@%s", domain) + valid = valid || strings.HasSuffix(email, emailSuffix) + } + + return valid +} + type SocialGithub struct { *oauth2.Config + allowedDomains []string + ApiUrl string + allowSignup bool } func (s *SocialGithub) Type() int { return int(models.GITHUB) } +func (s *SocialGithub) IsEmailAllowed(email string) bool { + return isEmailAllowed(email, s.allowedDomains) +} + +func (s *SocialGithub) IsSignupAllowed() bool { + return s.allowSignup +} + func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { var data struct { Id int `json:"id"` @@ -96,7 +128,7 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { var err error client := s.Client(oauth2.NoContext, token) - r, err := client.Get("https://api.github.com/user") + r, err := client.Get(s.ApiUrl) if err != nil { return nil, err } @@ -123,12 +155,23 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { type SocialGoogle struct { *oauth2.Config + allowedDomains []string + ApiUrl string + allowSignup bool } func (s *SocialGoogle) Type() int { return int(models.GOOGLE) } +func (s *SocialGoogle) IsEmailAllowed(email string) bool { + return isEmailAllowed(email, s.allowedDomains) +} + +func (s *SocialGoogle) IsSignupAllowed() bool { + return s.allowSignup +} + func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { var data struct { Id string `json:"id"` @@ -137,9 +180,8 @@ func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) { } var err error - reqUrl := "https://www.googleapis.com/oauth2/v1/userinfo" client := s.Client(oauth2.NoContext, token) - r, err := client.Get(reqUrl) + r, err := client.Get(s.ApiUrl) if err != nil { return nil, err } diff --git a/src/app/app.js b/public/app/app.js similarity index 100% rename from src/app/app.js rename to public/app/app.js diff --git a/src/app/components/config.js b/public/app/components/config.js similarity index 100% rename from src/app/components/config.js rename to public/app/components/config.js diff --git a/src/app/components/extend-jquery.js b/public/app/components/extend-jquery.js similarity index 100% rename from src/app/components/extend-jquery.js rename to public/app/components/extend-jquery.js diff --git a/src/app/components/kbn.js b/public/app/components/kbn.js similarity index 97% rename from src/app/components/kbn.js rename to public/app/components/kbn.js index 3c774dcb585..fa2b8214873 100644 --- a/src/app/components/kbn.js +++ b/public/app/components/kbn.js @@ -158,11 +158,6 @@ function($, _, moment) { return info.sec * info.count; }; - // This should go away, moment.js can do this - kbn.time_ago = function(string) { - return new Date(new Date().getTime() - (kbn.interval_to_ms(string))); - }; - /* This is a simplified version of elasticsearch's date parser */ kbn.parseDate = function(text) { if(_.isDate(text)) { @@ -385,6 +380,9 @@ function($, _, moment) { kbn.valueFormats.Bps = kbn.formatFuncCreator(1000, [' Bps', ' KBps', ' MBps', ' GBps', ' TBps', ' PBps', ' EBps', ' ZBps', ' YBps']); kbn.valueFormats.short = kbn.formatFuncCreator(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Qaudr', ' Quint', ' Sext', ' Sept']); kbn.valueFormats.joule = kbn.formatFuncCreator(1000, [' J', ' kJ', ' MJ', ' GJ', ' TJ', ' PJ', ' EJ', ' ZJ', ' YJ']); + kbn.valueFormats.amp = kbn.formatFuncCreator(1000, [' A', ' kA', ' MA', ' GA', ' TA', ' PA', ' EA', ' ZA', ' YA']); + kbn.valueFormats.volt = kbn.formatFuncCreator(1000, [' V', ' kV', ' MV', ' GV', ' TV', ' PV', ' EV', ' ZV', ' YV']); + kbn.valueFormats.hertz = kbn.formatFuncCreator(1000, [' Hz', ' kHz', ' MHz', ' GHz', ' THz', ' PHz', ' EHz', ' ZHz', ' YHz']); kbn.valueFormats.watt = kbn.formatFuncCreator(1000, [' W', ' kW', ' MW', ' GW', ' TW', ' PW', ' EW', ' ZW', ' YW']); kbn.valueFormats.kwatt = kbn.formatFuncCreator(1000, [' kW', ' MW', ' GW', ' TW', ' PW', ' EW', ' ZW', ' YW']); kbn.valueFormats.watth = kbn.formatFuncCreator(1000, [' Wh', ' kWh', ' MWh', ' GWh', ' TWh', ' PWh', ' EWh', ' ZWh', ' YWh']); @@ -539,6 +537,7 @@ function($, _, moment) { {text: 'microseconds (µs)', value: 'µs'}, {text: 'milliseconds (ms)', value: 'ms'}, {text: 'seconds (s)', value: 's'}, + {text: 'Hertz (1/s)', value: 'hertz'}, ] }, { @@ -566,6 +565,8 @@ function($, _, moment) { {text: 'kilowatt-hour (kWh)', value: 'kwatth'}, {text: 'joule (J)', value: 'joule'}, {text: 'electron volt (eV)', value: 'ev'}, + {text: 'Ampere (A)', value: 'amp'}, + {text: 'Volt (V)', value: 'volt'}, ] }, { diff --git a/src/app/components/lodash.extended.js b/public/app/components/lodash.extended.js similarity index 100% rename from src/app/components/lodash.extended.js rename to public/app/components/lodash.extended.js diff --git a/src/app/components/panelmeta.js b/public/app/components/panelmeta.js similarity index 100% rename from src/app/components/panelmeta.js rename to public/app/components/panelmeta.js diff --git a/src/app/components/partials.js b/public/app/components/partials.js similarity index 100% rename from src/app/components/partials.js rename to public/app/components/partials.js diff --git a/src/app/components/require.config.js b/public/app/components/require.config.js similarity index 100% rename from src/app/components/require.config.js rename to public/app/components/require.config.js diff --git a/src/app/components/settings.js b/public/app/components/settings.js similarity index 100% rename from src/app/components/settings.js rename to public/app/components/settings.js diff --git a/src/app/components/store.js b/public/app/components/store.js similarity index 100% rename from src/app/components/store.js rename to public/app/components/store.js diff --git a/src/app/components/timeSeries.js b/public/app/components/timeSeries.js similarity index 100% rename from src/app/components/timeSeries.js rename to public/app/components/timeSeries.js diff --git a/src/app/controllers/all.js b/public/app/controllers/all.js similarity index 100% rename from src/app/controllers/all.js rename to public/app/controllers/all.js diff --git a/src/app/controllers/console-ctrl.js b/public/app/controllers/console-ctrl.js similarity index 100% rename from src/app/controllers/console-ctrl.js rename to public/app/controllers/console-ctrl.js diff --git a/src/app/controllers/errorCtrl.js b/public/app/controllers/errorCtrl.js similarity index 100% rename from src/app/controllers/errorCtrl.js rename to public/app/controllers/errorCtrl.js diff --git a/src/app/controllers/grafanaCtrl.js b/public/app/controllers/grafanaCtrl.js similarity index 100% rename from src/app/controllers/grafanaCtrl.js rename to public/app/controllers/grafanaCtrl.js diff --git a/src/app/controllers/inspectCtrl.js b/public/app/controllers/inspectCtrl.js similarity index 100% rename from src/app/controllers/inspectCtrl.js rename to public/app/controllers/inspectCtrl.js diff --git a/src/app/controllers/jsonEditorCtrl.js b/public/app/controllers/jsonEditorCtrl.js similarity index 100% rename from src/app/controllers/jsonEditorCtrl.js rename to public/app/controllers/jsonEditorCtrl.js diff --git a/src/app/controllers/loginCtrl.js b/public/app/controllers/loginCtrl.js similarity index 100% rename from src/app/controllers/loginCtrl.js rename to public/app/controllers/loginCtrl.js diff --git a/src/app/controllers/metricKeys.js b/public/app/controllers/metricKeys.js similarity index 100% rename from src/app/controllers/metricKeys.js rename to public/app/controllers/metricKeys.js diff --git a/src/app/controllers/pulldown.js b/public/app/controllers/pulldown.js similarity index 100% rename from src/app/controllers/pulldown.js rename to public/app/controllers/pulldown.js diff --git a/src/app/controllers/search.js b/public/app/controllers/search.js similarity index 98% rename from src/app/controllers/search.js rename to public/app/controllers/search.js index 1f85952843b..396b3f6a3d8 100644 --- a/src/app/controllers/search.js +++ b/public/app/controllers/search.js @@ -17,6 +17,10 @@ function (angular, _, config) { $scope.query = { query: '', tag: '', starred: false }; $scope.currentSearchId = 0; + if ($scope.dashboardViewState.fullscreen) { + $scope.exitFullscreen(); + } + $timeout(function() { $scope.giveSearchFocus = $scope.giveSearchFocus + 1; $scope.query.query = ''; diff --git a/src/app/controllers/sidemenuCtrl.js b/public/app/controllers/sidemenuCtrl.js similarity index 100% rename from src/app/controllers/sidemenuCtrl.js rename to public/app/controllers/sidemenuCtrl.js diff --git a/src/app/directives/all.js b/public/app/directives/all.js similarity index 100% rename from src/app/directives/all.js rename to public/app/directives/all.js diff --git a/src/app/directives/arrayJoin.js b/public/app/directives/arrayJoin.js similarity index 100% rename from src/app/directives/arrayJoin.js rename to public/app/directives/arrayJoin.js diff --git a/src/app/directives/bodyClass.js b/public/app/directives/bodyClass.js similarity index 84% rename from src/app/directives/bodyClass.js rename to public/app/directives/bodyClass.js index 20339b8bf92..d0274aca6b2 100644 --- a/src/app/directives/bodyClass.js +++ b/public/app/directives/bodyClass.js @@ -1,8 +1,9 @@ define([ 'angular', - 'lodash' + 'lodash', + 'jquery' ], -function (angular, _) { +function (angular, _, $) { 'use strict'; angular @@ -13,6 +14,11 @@ function (angular, _) { var lastHideControlsVal; + // tooltip removal fix + $scope.$on("$routeChangeSuccess", function() { + $("#tooltip").remove(); + }); + $scope.$watch('submenuEnabled', function() { if (!$scope.dashboard) { return; diff --git a/src/app/directives/bootstrap-tagsinput.js b/public/app/directives/bootstrap-tagsinput.js similarity index 100% rename from src/app/directives/bootstrap-tagsinput.js rename to public/app/directives/bootstrap-tagsinput.js diff --git a/src/app/directives/configModal.js b/public/app/directives/configModal.js similarity index 100% rename from src/app/directives/configModal.js rename to public/app/directives/configModal.js diff --git a/src/app/directives/confirmClick.js b/public/app/directives/confirmClick.js similarity index 100% rename from src/app/directives/confirmClick.js rename to public/app/directives/confirmClick.js diff --git a/src/app/directives/dashEditLink.js b/public/app/directives/dashEditLink.js similarity index 100% rename from src/app/directives/dashEditLink.js rename to public/app/directives/dashEditLink.js diff --git a/src/app/directives/dashUpload.js b/public/app/directives/dashUpload.js similarity index 100% rename from src/app/directives/dashUpload.js rename to public/app/directives/dashUpload.js diff --git a/src/app/directives/dropdown.typeahead.js b/public/app/directives/dropdown.typeahead.js similarity index 100% rename from src/app/directives/dropdown.typeahead.js rename to public/app/directives/dropdown.typeahead.js diff --git a/src/app/directives/grafanaSimplePanel.js b/public/app/directives/grafanaSimplePanel.js similarity index 100% rename from src/app/directives/grafanaSimplePanel.js rename to public/app/directives/grafanaSimplePanel.js diff --git a/src/app/directives/grafanaVersionCheck.js b/public/app/directives/grafanaVersionCheck.js similarity index 81% rename from src/app/directives/grafanaVersionCheck.js rename to public/app/directives/grafanaVersionCheck.js index 487265e26af..c98ce269381 100644 --- a/src/app/directives/grafanaVersionCheck.js +++ b/public/app/directives/grafanaVersionCheck.js @@ -6,11 +6,11 @@ function (angular) { angular .module('grafana.directives') - .directive('grafanaVersionCheck', function($http, grafanaVersion) { + .directive('grafanaVersionCheck', function($http, contextSrv) { return { restrict: 'A', link: function(scope, elem) { - if (grafanaVersion[0] === '@') { + if (contextSrv.version === 'master') { return; } @@ -20,7 +20,7 @@ function (angular) { return; } - if (grafanaVersion !== response.data.version) { + if (contextSrv.version !== response.data.version) { elem.append(' ' + ' ' + 'New version available: ' + response.data.version + diff --git a/src/app/directives/graphiteSegment.js b/public/app/directives/graphiteSegment.js similarity index 96% rename from src/app/directives/graphiteSegment.js rename to public/app/directives/graphiteSegment.js index 577bbf5d6b7..c8ad131e6c7 100644 --- a/src/app/directives/graphiteSegment.js +++ b/public/app/directives/graphiteSegment.js @@ -37,12 +37,14 @@ function (angular, app, _, $) { if (selected) { segment.value = selected.value; segment.html = selected.html; + segment.fake = false; segment.expandable = selected.expandable; } else { segment.value = value; segment.html = $sce.trustAsHtml(value); segment.expandable = true; + segment.fake = false; } $scope.segmentValueChanged(segment, $scope.$index); }); @@ -71,7 +73,7 @@ function (angular, app, _, $) { options = _.map($scope.altSegments, function(alt) { return alt.value; }); // add custom values - if (segment.value !== 'select metric' && _.indexOf(options, segment.value) === -1) { + if (!segment.fake && _.indexOf(options, segment.value) === -1) { options.unshift(segment.value); } diff --git a/src/app/directives/ngBlur.js b/public/app/directives/ngBlur.js similarity index 100% rename from src/app/directives/ngBlur.js rename to public/app/directives/ngBlur.js diff --git a/src/app/directives/ngModelOnBlur.js b/public/app/directives/ngModelOnBlur.js similarity index 100% rename from src/app/directives/ngModelOnBlur.js rename to public/app/directives/ngModelOnBlur.js diff --git a/src/app/directives/spectrumPicker.js b/public/app/directives/spectrumPicker.js similarity index 100% rename from src/app/directives/spectrumPicker.js rename to public/app/directives/spectrumPicker.js diff --git a/src/app/directives/templateParamSelector.js b/public/app/directives/templateParamSelector.js similarity index 100% rename from src/app/directives/templateParamSelector.js rename to public/app/directives/templateParamSelector.js diff --git a/src/app/directives/tip.js b/public/app/directives/tip.js similarity index 100% rename from src/app/directives/tip.js rename to public/app/directives/tip.js diff --git a/src/app/directives/topnav.js b/public/app/directives/topnav.js similarity index 96% rename from src/app/directives/topnav.js rename to public/app/directives/topnav.js index c871dd085cb..88b98d55984 100644 --- a/src/app/directives/topnav.js +++ b/public/app/directives/topnav.js @@ -25,7 +25,7 @@ function (angular) { '' + '' + - '' + + '' + '' + '' + diff --git a/src/app/features/admin/adminEditUserCtrl.js b/public/app/features/admin/adminEditUserCtrl.js similarity index 100% rename from src/app/features/admin/adminEditUserCtrl.js rename to public/app/features/admin/adminEditUserCtrl.js diff --git a/src/app/features/admin/adminSettingsCtrl.js b/public/app/features/admin/adminSettingsCtrl.js similarity index 100% rename from src/app/features/admin/adminSettingsCtrl.js rename to public/app/features/admin/adminSettingsCtrl.js diff --git a/src/app/features/admin/adminUsersCtrl.js b/public/app/features/admin/adminUsersCtrl.js similarity index 100% rename from src/app/features/admin/adminUsersCtrl.js rename to public/app/features/admin/adminUsersCtrl.js diff --git a/src/app/features/admin/all.js b/public/app/features/admin/all.js similarity index 100% rename from src/app/features/admin/all.js rename to public/app/features/admin/all.js diff --git a/src/app/features/admin/partials/edit_user.html b/public/app/features/admin/partials/edit_user.html similarity index 100% rename from src/app/features/admin/partials/edit_user.html rename to public/app/features/admin/partials/edit_user.html diff --git a/src/app/features/admin/partials/new_user.html b/public/app/features/admin/partials/new_user.html similarity index 100% rename from src/app/features/admin/partials/new_user.html rename to public/app/features/admin/partials/new_user.html diff --git a/src/app/features/admin/partials/orgs.html b/public/app/features/admin/partials/orgs.html similarity index 100% rename from src/app/features/admin/partials/orgs.html rename to public/app/features/admin/partials/orgs.html diff --git a/src/app/features/admin/partials/settings.html b/public/app/features/admin/partials/settings.html similarity index 100% rename from src/app/features/admin/partials/settings.html rename to public/app/features/admin/partials/settings.html diff --git a/src/app/features/admin/partials/users.html b/public/app/features/admin/partials/users.html similarity index 100% rename from src/app/features/admin/partials/users.html rename to public/app/features/admin/partials/users.html diff --git a/src/app/features/all.js b/public/app/features/all.js similarity index 100% rename from src/app/features/all.js rename to public/app/features/all.js diff --git a/src/app/features/annotations/annotationsSrv.js b/public/app/features/annotations/annotationsSrv.js similarity index 100% rename from src/app/features/annotations/annotationsSrv.js rename to public/app/features/annotations/annotationsSrv.js diff --git a/src/app/features/annotations/editorCtrl.js b/public/app/features/annotations/editorCtrl.js similarity index 100% rename from src/app/features/annotations/editorCtrl.js rename to public/app/features/annotations/editorCtrl.js diff --git a/src/app/features/annotations/partials/editor.html b/public/app/features/annotations/partials/editor.html similarity index 100% rename from src/app/features/annotations/partials/editor.html rename to public/app/features/annotations/partials/editor.html diff --git a/src/app/features/dashboard/all.js b/public/app/features/dashboard/all.js similarity index 83% rename from src/app/features/dashboard/all.js rename to public/app/features/dashboard/all.js index 811f48cc464..aeabe6b354e 100644 --- a/src/app/features/dashboard/all.js +++ b/public/app/features/dashboard/all.js @@ -1,10 +1,12 @@ define([ './dashboardCtrl', './dashboardNavCtrl', + './snapshotTopNavCtrl', './saveDashboardAsCtrl', './playlistCtrl', './rowCtrl', - './sharePanelCtrl', + './shareModalCtrl', + './shareSnapshotCtrl', './submenuCtrl', './dashboardSrv', './keybindings', diff --git a/src/app/features/dashboard/dashboardCtrl.js b/public/app/features/dashboard/dashboardCtrl.js similarity index 83% rename from src/app/features/dashboard/dashboardCtrl.js rename to public/app/features/dashboard/dashboardCtrl.js index c194dcdb7ed..ba86f9f5dba 100644 --- a/src/app/features/dashboard/dashboardCtrl.js +++ b/public/app/features/dashboard/dashboardCtrl.js @@ -18,9 +18,11 @@ function (angular, $, config) { dynamicDashboardSrv, dashboardSrv, dashboardViewStateSrv, + contextSrv, $timeout) { $scope.editor = { index: 0 }; + $scope.topNavPartial = 'app/features/dashboard/partials/dashboardTopNav.html'; $scope.panels = config.panels; var resizeEventTimeout; @@ -37,29 +39,38 @@ function (angular, $, config) { $rootScope.performance.panelsInitialized = 0; $rootScope.performance.panelsRendered = 0; - var dashboard = dashboardSrv.create(data.model); + var dashboard = dashboardSrv.create(data.model, data.meta); // init services timeSrv.init(dashboard); // template values service needs to initialize completely before // the rest of the dashboard can load - templateValuesSrv.init(dashboard).then(function() { + templateValuesSrv.init(dashboard).finally(function() { dynamicDashboardSrv.init(dashboard); - $scope.dashboard = dashboard; + $scope.dashboardMeta = dashboard.meta; $scope.dashboardViewState = dashboardViewStateSrv.create($scope); - $scope.dashboardMeta = data.meta; dashboardKeybindings.shortcuts($scope); + $scope.updateTopNavPartial(); $scope.updateSubmenuVisibility(); $scope.setWindowTitleAndTheme(); $scope.appEvent("dashboard-loaded", $scope.dashboard); + }).catch(function(err) { + console.log('Failed to initialize dashboard template variables, error: ', err); + $scope.appEvent("alert-error", ['Dashboard init failed', 'Template variables could not be initialized: ' + err.message]); }); }; + $scope.updateTopNavPartial = function() { + if ($scope.dashboard.meta.isSnapshot) { + $scope.topNavPartial = 'app/features/dashboard/partials/snapshotTopNav.html'; + } + }; + $scope.updateSubmenuVisibility = function() { $scope.submenuEnabled = $scope.dashboard.hasTemplateVarsOrAnnotations(); }; @@ -135,4 +146,5 @@ function (angular, $, config) { }; }); + }); diff --git a/src/app/features/dashboard/dashboardNavCtrl.js b/public/app/features/dashboard/dashboardNavCtrl.js similarity index 78% rename from src/app/features/dashboard/dashboardNavCtrl.js rename to public/app/features/dashboard/dashboardNavCtrl.js index 5950c504a3a..27ce528f69e 100644 --- a/src/app/features/dashboard/dashboardNavCtrl.js +++ b/public/app/features/dashboard/dashboardNavCtrl.js @@ -1,25 +1,20 @@ define([ 'angular', 'lodash', - 'moment', 'config', 'store', 'filesaver' ], -function (angular, _, moment) { +function (angular, _) { 'use strict'; var module = angular.module('grafana.controllers'); - module.controller('DashboardNavCtrl', function($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, timeSrv) { + module.controller('DashboardNavCtrl', function($scope, $rootScope, alertSrv, $location, playlistSrv, backendSrv, $timeout) { $scope.init = function() { $scope.onAppEvent('save-dashboard', $scope.saveDashboard); $scope.onAppEvent('delete-dashboard', $scope.deleteDashboard); - - $scope.onAppEvent('zoom-out', function() { - $scope.zoom(2); - }); }; $scope.openEditView = function(editview) { @@ -42,7 +37,7 @@ function (angular, _, moment) { $scope.shareDashboard = function() { $scope.appEvent('show-modal', { - src: './app/features/dashboard/partials/shareDashboard.html', + src: './app/features/dashboard/partials/shareModal.html', scope: $scope.$new(), }); }; @@ -57,7 +52,11 @@ function (angular, _, moment) { }; $scope.saveDashboard = function(options) { - var clone = angular.copy($scope.dashboard); + if ($scope.dashboardMeta.canSave === false) { + return; + } + + var clone = $scope.dashboard.getSaveModelClone(); backendSrv.saveDashboard(clone, options).then(function(data) { $scope.dashboard.version = data.version; @@ -123,7 +122,9 @@ function (angular, _, moment) { $scope.saveDashboardAs = function() { var newScope = $rootScope.$new(); - newScope.clone = angular.copy($scope.dashboard); + newScope.clone = $scope.dashboard.getSaveModelClone(); + newScope.clone.editable = true; + newScope.clone.hideControls = false; $scope.appEvent('show-modal', { src: './app/features/dashboard/partials/saveDashboardAs.html', @@ -132,33 +133,26 @@ function (angular, _, moment) { }; $scope.exportDashboard = function() { - var blob = new Blob([angular.toJson($scope.dashboard, true)], { type: "application/json;charset=utf-8" }); + var clone = $scope.dashboard.getSaveModelClone(); + var blob = new Blob([angular.toJson(clone, true)], { type: "application/json;charset=utf-8" }); window.saveAs(blob, $scope.dashboard.title + '-' + new Date().getTime()); }; - $scope.zoom = function(factor) { - var range = timeSrv.timeRange(); + $scope.snapshot = function() { + $scope.dashboard.snapshot = true; + $rootScope.$broadcast('refresh'); - var timespan = (range.to.valueOf() - range.from.valueOf()); - var center = range.to.valueOf() - timespan/2; + $timeout(function() { + $scope.exportDashboard(); + $scope.dashboard.snapshot = false; + $scope.appEvent('dashboard-snapshot-cleanup'); + }, 1000); - var to = (center + (timespan*factor)/2); - var from = (center - (timespan*factor)/2); - - if(to > Date.now() && range.to <= Date.now()) { - var offset = to - Date.now(); - from = from - offset; - to = Date.now(); - } - - timeSrv.setTime({ - from: moment.utc(from).toDate(), - to: moment.utc(to).toDate(), - }); }; $scope.editJson = function() { - $scope.appEvent('show-json-editor', { object: $scope.dashboard }); + var clone = $scope.dashboard.getSaveModelClone(); + $scope.appEvent('show-json-editor', { object: clone }); }; $scope.stopPlaylist = function() { diff --git a/src/app/features/dashboard/dashboardSrv.js b/public/app/features/dashboard/dashboardSrv.js similarity index 80% rename from src/app/features/dashboard/dashboardSrv.js rename to public/app/features/dashboard/dashboardSrv.js index 0eab4f867d9..02d8f51fafe 100644 --- a/src/app/features/dashboard/dashboardSrv.js +++ b/public/app/features/dashboard/dashboardSrv.js @@ -10,10 +10,9 @@ function (angular, $, kbn, _, moment) { var module = angular.module('grafana.services'); - module.factory('dashboardSrv', function() { - - function DashboardModel (data) { + module.factory('dashboardSrv', function(contextSrv) { + function DashboardModel (data, meta) { if (!data) { data = {}; } @@ -37,6 +36,7 @@ function (angular, $, kbn, _, moment) { this.templating = this._ensureListExist(data.templating); this.annotations = this._ensureListExist(data.annotations); this.refresh = data.refresh; + this.snapshot = data.snapshot; this.schemaVersion = data.schemaVersion || 0; this.version = data.version || 0; @@ -45,10 +45,41 @@ function (angular, $, kbn, _, moment) { } this._updateSchema(data); + this._initMeta(meta); } var p = DashboardModel.prototype; + p._initMeta = function(meta) { + meta = meta || {}; + + meta.canShare = meta.canShare === false ? false : true; + meta.canSave = meta.canSave === false ? false : true; + meta.canEdit = meta.canEdit === false ? false : true; + meta.canStar = meta.canStar === false ? false : true; + meta.canDelete = meta.canDelete === false ? false : true; + + if (contextSrv.hasRole('Viewer')) { + meta.canSave = false; + } + + if (!this.editable) { + meta.canEdit = false; + meta.canDelete = false; + meta.canSave = false; + this.hideControls = true; + } + + this.meta = meta; + }; + + // cleans meta data and other non peristent state + p.getSaveModelClone = function() { + var copy = angular.copy(this); + delete copy.meta; + return copy; + }; + p._ensureListExist = function (data) { if (!data) { data = {}; } if (!data.list) { data.list = []; } @@ -67,6 +98,29 @@ function (angular, $, kbn, _, moment) { return max + 1; }; + p.forEachPanel = function(callback) { + var i, j, row; + for (i = 0; i < this.rows.length; i++) { + row = this.rows[i]; + for (j = 0; j < row.panels.length; j++) { + callback(row.panels[j], j, row, i); + } + } + }; + + p.getPanelById = function(id) { + for (var i = 0; i < this.rows.length; i++) { + var row = this.rows[i]; + for (var j = 0; j < row.panels.length; j++) { + var panel = row.panels[j]; + if (panel.id === id) { + return panel; + } + } + } + return null; + }; + p.rowSpan = function(row) { return _.reduce(row.panels, function(p,v) { return p + v.span; @@ -253,8 +307,8 @@ function (angular, $, kbn, _, moment) { }; return { - create: function(dashboard) { - return new DashboardModel(dashboard); + create: function(dashboard, meta) { + return new DashboardModel(dashboard, meta); } }; diff --git a/src/app/features/dashboard/directives/dashSearchView.js b/public/app/features/dashboard/directives/dashSearchView.js similarity index 100% rename from src/app/features/dashboard/directives/dashSearchView.js rename to public/app/features/dashboard/directives/dashSearchView.js diff --git a/src/app/features/dashboard/graphiteImportCtrl.js b/public/app/features/dashboard/graphiteImportCtrl.js similarity index 81% rename from src/app/features/dashboard/graphiteImportCtrl.js rename to public/app/features/dashboard/graphiteImportCtrl.js index 5a9205bd817..be912610e36 100644 --- a/src/app/features/dashboard/graphiteImportCtrl.js +++ b/public/app/features/dashboard/graphiteImportCtrl.js @@ -10,25 +10,27 @@ function (angular, app, _, kbn) { var module = angular.module('grafana.controllers'); module.controller('GraphiteImportCtrl', function($scope, datasourceSrv, dashboardSrv, $location) { + $scope.options = {}; $scope.init = function() { $scope.datasources = []; _.each(datasourceSrv.getAll(), function(ds) { if (ds.type === 'graphite') { - $scope.sourceName = ds.name; + $scope.options.sourceName = ds.name; $scope.datasources.push(ds.name); } }); }; $scope.listAll = function() { - $scope.datasource = datasourceSrv.get($scope.sourceName); - - $scope.datasource.listDashboards('').then(function(results) { - $scope.dashboards = results; - }, function(err) { - var message = err.message || err.statusText || 'Error'; - $scope.appEvent('alert-error', ['Failed to load dashboard list from graphite', message]); + datasourceSrv.get($scope.options.sourceName).then(function(datasource) { + $scope.datasource = datasource; + $scope.datasource.listDashboards('').then(function(results) { + $scope.dashboards = results; + }, function(err) { + var message = err.message || err.statusText || 'Error'; + $scope.appEvent('alert-error', ['Failed to load dashboard list from graphite', message]); + }); }); }; diff --git a/src/app/features/dashboard/importCtrl.js b/public/app/features/dashboard/importCtrl.js similarity index 100% rename from src/app/features/dashboard/importCtrl.js rename to public/app/features/dashboard/importCtrl.js diff --git a/src/app/features/dashboard/keybindings.js b/public/app/features/dashboard/keybindings.js similarity index 100% rename from src/app/features/dashboard/keybindings.js rename to public/app/features/dashboard/keybindings.js diff --git a/src/app/partials/dashboard_topnav.html b/public/app/features/dashboard/partials/dashboardTopNav.html similarity index 67% rename from src/app/partials/dashboard_topnav.html rename to public/app/features/dashboard/partials/dashboardTopNav.html index bf3ba635a97..6f85d6b4e02 100644 --- a/src/app/partials/dashboard_topnav.html +++ b/public/app/features/dashboard/partials/dashboardTopNav.html @@ -18,28 +18,28 @@ -