Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d790cab7a | ||
|
|
af05da4bbe | ||
|
|
e5ea6e0a41 | ||
|
|
c70a6385da | ||
|
|
2ab0cd16d8 | ||
|
|
e760e1096d | ||
|
|
74319714c0 | ||
|
|
3ebeba0619 | ||
|
|
fa094d0cb0 | ||
|
|
61e270bb3d | ||
|
|
5c0ef34196 | ||
|
|
8d9ab0c1da | ||
|
|
17b6bd4400 | ||
|
|
f56ca42b2c | ||
|
|
6eaf2ad46f | ||
|
|
7256639768 | ||
|
|
1ac7207c90 | ||
|
|
9a644f3100 | ||
|
|
45c65ed1b8 | ||
|
|
6aa15b9cc5 | ||
|
|
35a99fd95d | ||
|
|
db2f74a253 | ||
|
|
78c0b016a5 | ||
|
|
4e074b36fb | ||
|
|
b20d76e232 | ||
|
|
681cb29686 | ||
|
|
73b73d89a2 | ||
|
|
06cb2526a4 | ||
|
|
12a95a4604 | ||
|
|
b263d75373 | ||
|
|
495da34e37 | ||
|
|
a2fc16baeb | ||
|
|
98cf27a3a9 | ||
|
|
31eecc2a13 | ||
|
|
08c895aa12 | ||
|
|
be6f455b01 | ||
|
|
5cba22a19a | ||
|
|
0f2f8a47ff | ||
|
|
affc78fb63 | ||
|
|
5781fb6efc | ||
|
|
6be0938566 | ||
|
|
4520271c8f | ||
|
|
656c1e24f4 | ||
|
|
eb9a5103c2 | ||
|
|
f59603c90b |
@@ -0,0 +1,46 @@
|
||||
# Harley's Keyboard - agent notes
|
||||
|
||||
Fork of Julow/Unexpected-Keyboard (Unexpected Keyboard). Android IME, Java, Gradle Kotlin DSL.
|
||||
|
||||
## Build & test (exact)
|
||||
|
||||
```bash
|
||||
./gradlew testDebugUnitTest assembleDebug # full verification: unit tests THEN the APK
|
||||
./gradlew clean assembleDebug # when a generated file may be stale
|
||||
```
|
||||
|
||||
- SDK: `local.properties` -> `sdk.dir=/home/nyra/android-sdk` (present, gitignored, do not commit).
|
||||
- JDK 21, build-tools 34.0.0/36.0.0, AGP 8.8.2, compileSdk 36, minSdk 21.
|
||||
- APK: `build/outputs/apk/debug/Unexpected-Keyboard-debug.apk`, package id `harley.keyboard`.
|
||||
|
||||
## Layout & generated code
|
||||
|
||||
- Layout rows live in `srcs/layouts/*.xml`. A layout edit needs a **full build**, not just compile:
|
||||
`gen_layouts.py` regenerates `res/values/layouts.xml`, `res/raw/*.xml` and
|
||||
`srcs/juloo.keyboard2/ComposeKeyData.java`. Unit tests run before regeneration report
|
||||
stale-generated-state failures that are not real bugs.
|
||||
- Never hand-edit generated files (`res/values/layouts.xml`, `res/raw/*`, `ComposeKeyData.java`,
|
||||
`res/xml/method.xml`).
|
||||
|
||||
## Always do
|
||||
|
||||
- Run `./gradlew testDebugUnitTest assembleDebug` before reporting done.
|
||||
- Keep user-visible text in `res/values/strings.xml`.
|
||||
- Keep all network access off the UI thread.
|
||||
|
||||
## Ask first
|
||||
|
||||
- Adding any dependency (prefer the framework: `org.json`, `HttpURLConnection`).
|
||||
- Changing the package id, signing config or versionCode.
|
||||
|
||||
## Never do
|
||||
|
||||
- Commit or print secrets/tokens; no credentials in code, XML, tests or logs.
|
||||
- Touch `.git` internals, force-push, or edit CI workflows.
|
||||
- Enable cleartext HTTP globally (only a per-host network security config).
|
||||
- Change the space-bar peak-colour behaviour (a separate, deliberate feature).
|
||||
|
||||
## Definition of done
|
||||
|
||||
`./gradlew testDebugUnitTest assembleDebug` exits 0, new behaviour is covered by unit tests,
|
||||
and the tree has no generated-file diffs beyond what a normal build produces.
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<application android:label="@string/app_name" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:hardwareAccelerated="false">
|
||||
<application android:label="@string/app_name" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:hardwareAccelerated="false" android:networkSecurityConfig="@xml/network_security_config">
|
||||
<service android:name="juloo.keyboard2.Keyboard2" android:label="@string/app_name" android:permission="android.permission.BIND_INPUT_METHOD" android:exported="true" android:directBootAware="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.view.InputMethod"/>
|
||||
|
||||
Binary file not shown.
+2
-3
@@ -17,7 +17,7 @@ android {
|
||||
compileSdkVersion = "android-36"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "juloo.keyboard2"
|
||||
applicationId = "harley.keyboard"
|
||||
minSdk = 21
|
||||
targetSdk { version = release(36) }
|
||||
versionCode = 55
|
||||
@@ -82,7 +82,6 @@ android {
|
||||
isMinifyEnabled = false
|
||||
isShrinkResources = false
|
||||
isDebuggable = true
|
||||
applicationIdSuffix = ".debug"
|
||||
resValue("string", "app_name", "@string/app_name_debug")
|
||||
resValue("bool", "debug_logs", "true")
|
||||
signingConfig = signingConfigs["debug"]
|
||||
@@ -194,4 +193,4 @@ tasks.named("preBuild") {
|
||||
// checked in the repository that don't need to be updated during regular
|
||||
// builds.
|
||||
mustRunAfter(genEmojis, genLayoutsList, compileComposeSequences, genMethodXml)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@ latin_kbdtuf_tr: These keys are now added automatically, unexpected: f11_placeho
|
||||
latin_kbdtuf_tr: Missing programming keys, missing: loc esc, loc tab
|
||||
latn_colemak: Some keys contain whitespaces, unexpected: ́
|
||||
latn_dvorak: Missing important key, missing: loc capslock
|
||||
latn_neo2: Layout redefines the bottom row but some important keys are missing, missing: loc switch_clipboard
|
||||
latn_neo2: Layout redefines the bottom row but some important keys are missing, missing: alt, loc switch_clipboard, meta, pgdn, pgup, switch_fn, switch_lights, switch_roku
|
||||
latn_qwertz_cz_multifunctional: Layout includes some ASCII punctuation but not all, missing: `
|
||||
latn_qwertz_sk: Layout includes some ASCII punctuation but not all, missing: `
|
||||
latn_qzerty_it: Layout includes some ASCII punctuation but not all, missing: #, $, \, `, ~
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<item>@string/pref_theme_e_everforestlight</item>
|
||||
<item>@string/pref_theme_e_cobalt</item>
|
||||
<item>@string/pref_theme_e_pine</item>
|
||||
<item>@string/pref_theme_e_orchid</item>
|
||||
<item>@string/pref_theme_e_epaperblack</item>
|
||||
</string-array>
|
||||
<string-array name="pref_theme_values">
|
||||
@@ -64,6 +65,7 @@
|
||||
<item>everforestlight</item>
|
||||
<item>cobalt</item>
|
||||
<item>pine</item>
|
||||
<item>orchid</item>
|
||||
<item>epaperblack</item>
|
||||
</string-array>
|
||||
<string-array name="pref_swipe_dist_entries">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name_release">Unexpected Keyboard</string>
|
||||
<string name="app_name_debug">Unexpected Keyboard (Debug)</string>
|
||||
<string name="app_name_release">Harley\'s Keyboard</string>
|
||||
<string name="app_name_debug">Harley\'s Keyboard</string>
|
||||
<string name="short_description">Lightweight and privacy-conscious virtual keyboard for Android.</string>
|
||||
<string name="store_description">The main feature is that you can type more characters by swiping the keys towards the corners.\n\nThis application was originally designed for programmers using Termux.\nNow perfect for everyday use.\n\nThis application contains no ads and is Open Source.</string>
|
||||
<string name="pref_portrait">In portrait mode</string>
|
||||
@@ -9,6 +9,9 @@
|
||||
<string name="pref_landscape">In landscape mode</string>
|
||||
<string name="pref_landscape_unfolded">In landscape mode unfolded</string>
|
||||
<string name="pref_category_layout">Layout</string>
|
||||
<string name="pref_usage_gauge_enabled">Usage gauge key</string>
|
||||
<string name="pref_usage_gauge_url">Usage gauge URL</string>
|
||||
<string name="pref_usage_gauge_token">Usage gauge bearer token</string>
|
||||
<string name="pref_label_brightness">Adjust label brightness</string>
|
||||
<string name="pref_keyboard_opacity">Adjust keyboard background opacity</string>
|
||||
<string name="pref_key_opacity">Adjust key opacity</string>
|
||||
@@ -82,6 +85,7 @@
|
||||
<string name="pref_theme_e_everforestlight">Everforest Light</string>
|
||||
<string name="pref_theme_e_cobalt">Cobalt</string>
|
||||
<string name="pref_theme_e_pine">Pine</string>
|
||||
<string name="pref_theme_e_orchid">Orchid</string>
|
||||
<string name="pref_theme_e_epaperblack">ePaper Black</string>
|
||||
<string name="pref_swipe_dist_e_very_short">Very short</string>
|
||||
<string name="pref_swipe_dist_e_short">Short</string>
|
||||
|
||||
@@ -15,6 +15,20 @@
|
||||
<attr name="colorLabelPressed" format="color"/> <!-- defaults to colorLabel -->
|
||||
<attr name="colorLabelActivated" format="color"/>
|
||||
<attr name="colorLabelLocked" format="color"/>
|
||||
<!-- The Roku page's four direction keys. They carry FLAG_SECONDARY like every
|
||||
other event key, so without a colour of their own they come out dimmed to
|
||||
secondaryLabelColor. This is the same cyan the keyboard already uses for
|
||||
"the blue" (the space bar's io-stall line, the DeepSeek off-peak indicator),
|
||||
and it is deliberately the same value in every theme. -->
|
||||
<attr name="colorRokuArrow" format="color"/>
|
||||
<!-- The lights page's colour rule: a light that is ON is BLUE, one that is OFF is PINK, and
|
||||
one nobody can see - 'unavailable', 'unknown', or no data at all - is the stale grey.
|
||||
These are the only colours the rule is written with, and colorLightOn takes the very blue
|
||||
the Roku arrows wear instead of repeating its hex, so swapping on and off is a one-line
|
||||
change here and no key holds a colour of its own. -->
|
||||
<attr name="colorLightOn" format="color"/>
|
||||
<attr name="colorLightOff" format="color"/>
|
||||
<attr name="colorLightStale" format="color"/>
|
||||
<attr name="secondaryDimming" format="float"/>
|
||||
<attr name="greyedDimming" format="float"/>
|
||||
<!-- Corner labels -->
|
||||
@@ -61,6 +75,10 @@
|
||||
<item name="clipboard_divider_height">1px</item>
|
||||
<item name="suggestions_background">?attr/colorKeyboard</item>
|
||||
<item name="suggestions_border_radius">?attr/keyBorderRadius</item>
|
||||
<item name="colorRokuArrow">#00E5FF</item>
|
||||
<item name="colorLightOn">?attr/colorRokuArrow</item>
|
||||
<item name="colorLightOff">#FF2D95</item>
|
||||
<item name="colorLightStale">#8a8d93</item>
|
||||
</style>
|
||||
<style name="Dark" parent="BaseTheme">
|
||||
<item name="android:isLightTheme">false</item>
|
||||
@@ -311,6 +329,25 @@
|
||||
<item name="emoji_color">?colorLabel</item>
|
||||
<item name="clipboard_divider_color">?colorKey</item>
|
||||
</style>
|
||||
<style name="Orchid" parent="BaseTheme">
|
||||
<item name="android:isLightTheme">false</item>
|
||||
<item name="colorKeyboard">#100a1c</item>
|
||||
<item name="colorKey">?colorKeyboard</item>
|
||||
<item name="colorKeyActivated">#a855f7</item>
|
||||
<item name="colorLabel">#ff8ad8</item>
|
||||
<item name="colorLabelActivated">?colorKey</item>
|
||||
<item name="colorLabelLocked">#9db8ff</item>
|
||||
<item name="colorSubLabel">#c67dff</item>
|
||||
<item name="keyBorderWidth">0.0dip</item>
|
||||
<item name="keyBorderWidthActivated">0.0dip</item>
|
||||
<item name="keyBorderColorLeft">#241734</item>
|
||||
<item name="keyBorderColorTop">#241734</item>
|
||||
<item name="keyBorderColorRight">#241734</item>
|
||||
<item name="keyBorderColorBottom">#241734</item>
|
||||
<item name="emoji_button_bg">?colorKeyboard</item>
|
||||
<item name="emoji_color">?colorLabel</item>
|
||||
<item name="clipboard_divider_color">?colorKey</item>
|
||||
</style>
|
||||
<style name="ePaperBlack" parent="BaseTheme">
|
||||
<item name="android:isLightTheme">false</item>
|
||||
<item name="colorKeyboard">#000000</item>
|
||||
|
||||
+10
-5
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<row height="0.95">
|
||||
<key width="1.5" role="action" key0="ctrl" key1="loc meta" key3="loc switch_clipboard"/>
|
||||
<key width="1.3" role="action" key0="alt" key1="config"/>
|
||||
<key width="1.0" role="action" key7="pgup" key8="pgdn" key0="loc compose"/>
|
||||
<key width="5.5" role="space_bar" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right"/>
|
||||
<key width="1.4" role="action" key0="fn" key2="meta" key3="loc voice_typing"/>
|
||||
<key width="1.5" role="action" key0="enter" key2="action"/>
|
||||
<!-- The Alt key's four corner slots (1 top-left, 2 top-right, 3 bottom-left, 4 bottom-right):
|
||||
three are taken - the Roku page, the emoji pane and the config - so the free bottom-right one
|
||||
carries the lights page, drawn with the lamp glyph at 0xE025 (res/xml/lights.xml). Swipe
|
||||
up-right on Alt to open it. -->
|
||||
<key width="1.3" role="action" key0="alt" key1="switch_roku" key2="switch_emoji" key3="config" key4="switch_lights"/>
|
||||
<key width="1.0" role="action" key7="pgup" key8="pgdn"/>
|
||||
<key width="4.5" role="space_bar" key0="space" key7="switch_forward" key8="switch_backward" key5="cursor_left" key6="cursor_right"/>
|
||||
<key width="1.0" role="action" key7="up" key8="down"/>
|
||||
<key width="1.4" role="action" key0="switch_numeric" key1="meta" key2="loc change_method" key3="loc voice_typing" key4="switch_fn"/>
|
||||
<key width="1.5" role="action" key0="enter" key4="loc compose" key2="action"/>
|
||||
</row>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<keyboard bottom_row="false">
|
||||
<row>
|
||||
<key key0="f1"/>
|
||||
<key key0="f2"/>
|
||||
<key key0="f3"/>
|
||||
<key key0="f4"/>
|
||||
<key key0="f5"/>
|
||||
<key key0="f6"/>
|
||||
<key key0="f7"/>
|
||||
<key key0="f8"/>
|
||||
<key key0="f9"/>
|
||||
<key key0="f10"/>
|
||||
<key key0="f11"/>
|
||||
<key key0="f12"/>
|
||||
</row>
|
||||
<row height="0.95">
|
||||
<key width="1.5" role="action" key0="shift"/>
|
||||
<key width="1.5" role="action" key0="ctrl"/>
|
||||
<key width="1.3" role="action" key0="alt"/>
|
||||
<key width="1.4" role="action" key0="meta"/>
|
||||
<key width="4.2" role="space_bar" key0="space"/>
|
||||
<key width="1.4" role="action" key0="switch_text"/>
|
||||
<key width="1.5" role="action" key0="enter" key2="action"/>
|
||||
</row>
|
||||
</keyboard>
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The lights page. Not a user-selectable layout: it is opened by the switch_lights key (the lamp
|
||||
glyph at 0xE025, in the free bottom-right corner of the Alt key) and left with lights_close.
|
||||
|
||||
This file is only the page's skeleton: one row of three light slots, and the full-width
|
||||
"Keyboard" key that fires Event.SWITCH_TEXT, exactly like roku_close on the Roku page.
|
||||
[Keyboard2.loadLightsLayout] repeats the slot row once per row of lights the lights-proxy
|
||||
reports and replaces both values of every slot - the label with the light's name as the
|
||||
registry gives it, the top-centre corner with the state glyph (the lit lamp at 0xE025 for a
|
||||
light that is on, the unlit one at 0xE026 for everything else) - so one key per light, three to
|
||||
a row, the last row stretched if it is short. Before the proxy has answered, the three
|
||||
placeholder slots below stand as-is: a dash and the unlit lamp in the stale look, because until
|
||||
somebody names the lights there is nothing honest to show. The "light_N" names below exist only
|
||||
so this file parses; [LightsPage] replaces them.
|
||||
|
||||
Like res/xml/roku.xml it is loaded as a plain layout, NOT through LayoutModifier.modify_layout,
|
||||
so the forced config key and the user's extra keys cannot land on it: the page is its own keys
|
||||
and the way back, and nothing else.
|
||||
|
||||
Row height 1.0 (not the Roku page's 0.8) because these keys carry words, not just glyphs. -->
|
||||
<keyboard bottom_row="false">
|
||||
<row height="1.0">
|
||||
<key width="3.3" role="action" key0="light_1"/>
|
||||
<key width="3.3" role="action" key0="light_2"/>
|
||||
<key width="3.3" role="action" key0="light_3"/>
|
||||
</row>
|
||||
<row height="1.0">
|
||||
<key width="9.9" role="action" key0="lights_close"/>
|
||||
</row>
|
||||
</keyboard>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="false"/>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="false">10.10.10.1</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- The Roku remote page. Not a user-selectable layout: it is opened by the
|
||||
switch_roku key and left with roku_close. Every key above the last row sends
|
||||
an HTTP keypress to the TV (see RokuRemote) instead of typing.
|
||||
|
||||
This page carries nothing but remote keys. It is loaded as a plain layout
|
||||
(Keyboard2.loadRokuLayout), NOT through LayoutModifier.modify_layout, so the
|
||||
forced config key and the configured extra keys do not land on it and do not
|
||||
fill the empty corner slots of these keys. That is why the last row is a
|
||||
full-width "Keyboard" key instead of the config gear: it fires the same
|
||||
Event.SWITCH_TEXT as the switch_text key on other pages and is the way back
|
||||
to the letter keyboard.
|
||||
|
||||
Vol- is also the page's mute: holding it sends mute instead of volume-down
|
||||
(KeyModifier.modify_long_press), so mute is on the page without a slot. -->
|
||||
<keyboard bottom_row="false">
|
||||
<row height="0.8">
|
||||
<key width="3.3" role="action" key0="roku_home"/>
|
||||
<key width="3.3" role="action" key0="roku_back"/>
|
||||
<key width="3.3" role="action" key0="roku_power"/>
|
||||
</row>
|
||||
<row height="0.8">
|
||||
<key shift="3.3" width="3.3" role="action" key0="roku_up"/>
|
||||
</row>
|
||||
<row height="0.8">
|
||||
<key width="3.3" role="action" key0="roku_left"/>
|
||||
<key width="3.3" role="action" key0="roku_select"/>
|
||||
<key width="3.3" role="action" key0="roku_right"/>
|
||||
</row>
|
||||
<row height="0.8">
|
||||
<key shift="3.3" width="3.3" role="action" key0="roku_down"/>
|
||||
</row>
|
||||
<row height="0.8">
|
||||
<key width="3.3" role="action" key0="roku_voldown"/>
|
||||
<key width="3.3" role="action" key0="roku_play"/>
|
||||
<key width="3.3" role="action" key0="roku_volup"/>
|
||||
</row>
|
||||
<row height="1.0">
|
||||
<key width="9.9" role="action" key0="roku_close"/>
|
||||
</row>
|
||||
</keyboard>
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory android:title="@string/pref_category_layout">
|
||||
<CheckBoxPreference android:key="usage_gauge_enabled" android:title="@string/pref_usage_gauge_enabled" android:defaultValue="true"/>
|
||||
<EditTextPreference android:key="usage_gauge_url" android:title="@string/pref_usage_gauge_url" android:defaultValue="http://10.10.10.1:8771/usage" android:inputType="textUri"/>
|
||||
<EditTextPreference android:key="usage_gauge_token" android:title="@string/pref_usage_gauge_token" android:defaultValue="" android:inputType="textPassword"/>
|
||||
<juloo.keyboard2.prefs.LayoutsPreference/>
|
||||
<PreferenceScreen android:title="@string/pref_extra_keys_title">
|
||||
<PreferenceCategory android:title="@string/pref_extra_keys_custom">
|
||||
|
||||
@@ -43,7 +43,12 @@ public final class ComposeKey
|
||||
new String(states, next + 1, next_length - 1));
|
||||
}
|
||||
else // Character final state.
|
||||
{
|
||||
// Preserve the named-key identity used by compose sequences for NBSP.
|
||||
if (next_header == '\u00a0')
|
||||
return KeyValue.getKeyByName("nbsp");
|
||||
return KeyValue.makeCharKey((char)next_header);
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply char [c] to the pending compose sequence. If the application resolves
|
||||
|
||||
@@ -29,121 +29,121 @@ public final class ComposeKeyData
|
||||
"`abcdefghijklmnoprstuvwxyz{|~\u00a8\u00af\u00b4\u00b8\u00f7\u02d8\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0406\u0408\u0410\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0421\u0422\u0423\u0425\u0427\u0428\u042e" +
|
||||
"\u0430\u0431\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0442\u0443\u0445\u0447\u0448\u044a\u044e\u0456\u0458\u0460\u0461\u0467\u05b4\u05b7\u05b8\u05b9\u05bc\u05bf\u05c1\u05c2\u0627\u062a\u062c\u062d\u062f\u0633\u0634\u0639\u063a\u0641\u0642\u0644\u0646\u0647\u0648\u064a\u0653\u0654\u0655\u0667\u0668\u06cc\u06f7\u06f8\u093c\u09bc\u09c7\u0a3c\u0b3c\u0b47\u0bc6\u0bc7\u0bd7\u0c46\u0cbf\u0cc6\u0cca" +
|
||||
"\u0d46\u0d47\u0dd9\u0ddc\u0f71\u0f90\u0f92\u0f9c\u0fa1\u0fa6\u0fab\u0fb2\u0fb3\u0fb5\u0fb7\u102e\u1100\u1102\u1103\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110e\u1111\u1112\u1121\u1132\u113c\u113e\u114e\u1150\u1161\u1163\u1165\u1167\u1169\u116a\u116d\u116e\u116f\u1172\u1173\u1174\u1175\u119e\u11a8\u11aa\u11ab\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b7\u11b8\u11ba\u11bc\u11c1\u11c2\u11ce\u11dd\u11ec\u11f0\u2190\u2191\u2192" +
|
||||
"\u2193\u2203\u2206\u2207\u2208\u220a\u220b\u2218\u2223\u2225\u2227\u2228\u2229\u222a\u223c\u2243\u2248\u224d\u2260\u2261\u2264\u2265\u2272\u2273\u2276\u2277\u227a\u227b\u227c\u227d\u2282\u2283\u2286\u2287\u2291\u2292\u22a3\u22a4\u22a5\u22a8\u22a9\u22ab\u22b2\u22b3\u22b4\u22b5\u22c4\u2373\u2375\u237a\u2395\u25cb\u2add\u0000 (,-.<>_\uFFFF\u006e\u0062\u0073\u0070\u02d8\u00b8~\u2008\u02c7" +
|
||||
"^\u00af\u0000!+?ABDEHIKLMNORSTUVWYZ^abdehiklmnorstuvwyz\u01a0\u01a1\u01af\u01b0\u00a1\u0000OUou\u1ee2\u1ef0\u203d\u1ea0\u1e04\u1e0c\u1eb8\u1e24\u1eca\u1e32\u1e36\u1e42\u1e46\u1ecc\u1e5a\u1e62\u1e6c" +
|
||||
"\u1ee4\u1e7e\u1e88\u1ef4\u1e92\u00a6\u0000 \"',<>AEHIOUWXY_aehiotuwxy~\u00af\u00b4\u00d5\u00f5\u016a\u016b\u0399\u03a5\u03b9\u03c5\u03d2\u0406\u0410\u0415\u0416\u0417\u0418\u041e\u0423\u0427\u042b\u042d\u0430\u0435\u0436\u0437\u0438\u043e\u0443\u0447\u044b\u044d\u0456\u04d8\u04d9\u04e8\u04e9\u00a8" +
|
||||
"\u0344\u201e\u201c\u201d\u00c4\u00cb\u1e26\u00cf\u00d6\u00dc\u1e84\u1e8c\u0178\u0000Uu\u1e7a\u1e7b\u0000Oo\u1e4e\u0000Uu\u03aa\u03ab\u0407\u04d2\u0401\u04dc\u04de\u04e4\u04e6\u04f0\u04f4\u04f8\u04ec\u0451\u0457\u04da\u04ea\u0000#ESbefq\u266f\u266b\u266c\u266d\u266a\u266e\u2669\u0000o\u2030\u0000\"'()+,/<>AC" +
|
||||
"EGIJKLMNOPRSUWYZ^_abcegijklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00d8\u00dc\u00e2\u00e5\u00e6\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u0168\u0169\u01a0\u01a1\u01af\u01b0" +
|
||||
"\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0413\u0415\u0418\u041a\u041e\u0420\u0423\u042b\u042d\u042e\u042f\u0430\u0433\u0435\u0438\u043a\u043e\u0440\u0443\u044b\u044d\u044e\u044f\u2395\u0000 IUiu\u03b9\u03c5\u0385\u1e2e\u01d7\u0390\u00b4\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0d\u1f1d\u1f2d\u1f3d\u1f4d" +
|
||||
"\u1f5d\u1f6d\u1f05\u1f15\u1f25\u1f35\u1f45\u1f55\u1f65\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0c\u1f1c\u1f2c\u1f3c\u1f4c\u1f6c\u1f04\u1f14\u1f24\u1f34\u1f44\u1f54\u1f64\u0000OUou\u1eda\u1ee8\u201a\u0000Oo\u01fe\u2018\u2019\u00c1\u0106\u00c9\u01f4\u00cd\uFFFF\u004a\u0301\u1e30\u0139\u1e3e\u0143\u00d3\u1e54\u0154\u015a\u00da\u1e82\u00dd\u0179\u0000A" +
|
||||
"EOaeo\u1ea4\u1ebe\u1ed0\u0000EOeo\u1e16\u1e52\u0000Aa\u1eae\u0000OUou\u1e4c\u1e78\u0000EOeo\u0000Cc\u1e08\u01fa\u01fc\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u03ce\uFFFF\u0410\u0301\u0403\uFFFF\u0415\u0301\uFFFF\u0418\u0301\u040c\uFFFF\u041e\u0301\uFFFF\u0420\u0301\uFFFF\u0423\u0301\uFFFF\u042b\u0301\uFFFF\u042d\u0301\uFFFF" +
|
||||
"\u042e\u0301\uFFFF\u042f\u0301\uFFFF\u0440\u0301\u235e\u0000 ()-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv" +
|
||||
"wxyz\u0391\u0395\u0397\u0399\u039f\u03a1\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c1\u03c5\u03c9\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110b\u110c\u110e\u110f\u1110\u1111\u1112\u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3\u30b5\u30b7\u30b9\u30bb\u30bd\u30bf\u30c1\u30c4\u30c6\u30c8\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d2\u30d5\u30d8\u30db\u30de\u30df\u30e0\u30e1\u30e2\u30e4\u30e6\u30e8" +
|
||||
"\u30e9\u30ea\u30eb\u30ec\u30ed\u30ef\u30f0\u30f1\u30f2\u4e00\u4e03\u4e09\u4e0a\u4e0b\u4e2d\u4e5d\u4e8c\u4e94\u4f01\u4f11\u512a\u516b\u516d\u5199\u52b4\u533b\u5341\u5354\u5370\u53f3\u540d\u56db\u571f\u591c\u5973\u5b66\u5b97\u5de6\u65e5\u6708\u6709\u6728\u682a\u6b63\u6c34\u6ce8\u706b\u7279\u7537\u76e3\u793e\u795d\u79d8\u8ca1\u8cc7\u9069\u91d1\u9805[\u0000)\uFFFF\ud83c\udd2f{\u0000)\u24ea\u0000)01" +
|
||||
"23456789\u2460\u0000)\u2469\u0000)\u246a\u0000)\u246b\u0000)\u246c\u0000)\u246d\u0000)\u246e\u0000)\u246f\u0000)\u2470\u0000)\u2471\u0000)\u2472\u0000)0123456789\u2461\u0000)\u2473\u0000)\u3251\u0000)\u3252\u0000)\u3253\u0000)\u3254\u0000)\u3255\u0000)" +
|
||||
"\u3256\u0000)\u3257\u0000)\u3258\u0000)\u3259\u0000)0123456789\u2462\u0000)\u325a\u0000)\u325b\u0000)\u325c\u0000)\u325d\u0000)\u325e\u0000)\u325f\u0000)\u32b1\u0000)\u32b2\u0000)\u32b3\u0000)\u32b4\u0000)0123456789\u2463\u0000)\u32b5\u0000)\u32b6" +
|
||||
"\u0000)\u32b7\u0000)\u32b8\u0000)\u32b9\u0000)\u32ba\u0000)\u32bb\u0000)\u32bc\u0000)\u32bd\u0000)\u32be\u0000)0\u2464\u0000)\u32bf\u0000)\u2465\u0000)\u2466\u0000)\u2467\u0000)\u2468\u0000)\u24b6\u0000)\u24b7\u0000)\u24b8\u0000)\u24b9\u0000)\u24ba\u0000)\u24bb\u0000)\u24bc\u0000)\u24bd\u0000)\u24be\u0000)" +
|
||||
"\u24bf\u0000)\u24c0\u0000)\u24c1\u0000)\u24c2\u0000)\u24c3\u0000)\u24c4\u0000)\u24c5\u0000)\u24c6\u0000)\u24c7\u0000)\u24c8\u0000)\u24c9\u0000)\u24ca\u0000)\u24cb\u0000)\u24cc\u0000)\u24cd\u0000)\u24ce\u0000)\u24cf\u0000)\u24d0\u0000)\u24d1\u0000)\u24d2\u0000)\u24d3\u0000)\u24d4\u0000)\u24d5\u0000)\u24d6\u0000)" +
|
||||
"\u24d7\u0000)\u24d8\u0000)\u24d9\u0000)\u24da\u0000)\u24db\u0000)\u24dc\u0000)\u24dd\u0000)\u24de\u0000)\u24df\u0000)\u24e0\u0000)\u24e1\u0000)\u24e2\u0000)\u24e3\u0000)\u24e4\u0000)\u24e5\u0000)\u24e6\u0000)\u24e7\u0000)\u24e8\u0000)\u24e9\u1f09\u1f19\u1f29\u1f39\u1f49\u1fec\u1f59\u1f69\u1f01\u1f11\u1f21\u1f31\u1f41\u1fe5\u1f51\u1f61\u0000" +
|
||||
")\u1161\u3260\u0000)\u326e\u0000)\u1161\u3261\u0000)\u326f\u0000)\u1161\u3262\u0000)\u3270\u0000)\u1161\u3263\u0000)\u3271\u0000)\u1161\u3264\u0000)\u3272\u0000)\u1161\u3265\u0000)\u3273\u0000)\u1161\u3266\u0000)\u3274\u0000)\u1161\u3267\u0000)\u3275\u0000)\u1161\u3268\u0000)\u3276\u0000)\u1161\u3269\u0000)\u3277\u0000)\u1161" +
|
||||
"\u326a\u0000)\u3278\u0000)\u1161\u326b\u0000)\u3279\u0000)\u1161\u326c\u0000)\u327a\u0000)\u1161\u326d\u0000)\u327b\u0000)\u32d0\u0000)\u32d1\u0000)\u32d2\u0000)\u32d3\u0000)\u32d4\u0000)\u32d5\u0000)\u32d6\u0000)\u32d7\u0000)\u32d8\u0000)\u32d9\u0000)\u32da\u0000)\u32db\u0000)\u32dc\u0000)\u32dd\u0000)\u32de\u0000)" +
|
||||
"\u32df\u0000)\u32e0\u0000)\u32e1\u0000)\u32e2\u0000)\u32e3\u0000)\u32e4\u0000)\u32e5\u0000)\u32e6\u0000)\u32e7\u0000)\u32e8\u0000)\u32e9\u0000)\u32ea\u0000)\u32eb\u0000)\u32ec\u0000)\u32ed\u0000)\u32ee\u0000)\u32ef\u0000)\u32f0\u0000)\u32f1\u0000)\u32f2\u0000)\u32f3\u0000)\u32f4\u0000)\u32f5\u0000)\u32f6\u0000)" +
|
||||
"\u32f7\u0000)\u32f8\u0000)\u32f9\u0000)\u32fa\u0000)\u32fb\u0000)\u32fc\u0000)\u32fd\u0000)\u32fe\u0000)\u3280\u0000)\u3286\u0000)\u3282\u0000)\u32a4\u0000)\u32a6\u0000)\u32a5\u0000)\u3288\u0000)\u3281\u0000)\u3284\u0000)\u32ad\u0000)\u32a1\u0000)\u329d\u0000)\u3287\u0000)\u3285\u0000)\u32a2\u0000)\u3298\u0000)" +
|
||||
"\u32a9\u0000)\u3289\u0000)\u32af\u0000)\u329e\u0000)\u32a8\u0000)\u3294\u0000)\u3283\u0000)\u328f\u0000)\u32b0\u0000)\u329b\u0000)\u32ab\u0000)\u32aa\u0000)\u32a7\u0000)\u3290\u0000)\u328a\u0000)\u3292\u0000)\u328d\u0000)\u3291\u0000)\u32a3\u0000)\u328c\u0000)\u329f\u0000)\u328b\u0000)\u3295\u0000)\u329a\u0000)" +
|
||||
"\u32ac\u0000)\u3293\u0000)\u3297\u0000)\u3299\u0000)\u3296\u0000)\u32ae\u0000)\u329c\u0000)\u328e\u0000)\u32a0\u0000)-\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c1\u03c5\u03c9]}\u1f08\u1f18\u1f28\u1f38\u1f48\u1f68\u1f00\u1f10\u1f20\u1f30\u1f40\u1fe4\u1f50\u1f60\u0000'0AUau\u00a8\u25cb\u0000Aa\u00c5\u016e" +
|
||||
"\u235f\u0000+-OUou#\u00b1\u01a0\u01af\u0000 \"',-ACDEGHIKLNORSTUacdeghiklnorstu\u0413\u041a\u041b\u041d\u0425\u0433\u043a\u043b\u043d\u0445\u0466\u0467\u00ac\u0104\u00c7\u1e10\u0118\u0122\u1e28\u012e\u0136\u013b\u0145\u01ea" +
|
||||
"\u0156\u015e\u0162\u0172\u04fa\u04c3\u0512\u04c7\u04fc\u04fb\u04c4\u0513\u04c8\u04fd\u04c9\u04ca\u0000 ()+,-./:>ADEIKLOU\\^_adeilou\u2191\u2193\u25cb\u0000 -.\u00ad\u2014\u2013\u0000Ee\uFFFF\u0116\u0304\uFFFF\u0117\u0304\u233f\u00f7\u0100\u0110\u0112\u012a\u20ad\u00a3" +
|
||||
"\u014c\u016a\u2340\u2212\u234f\u2356\u2296\u0000 !'-.:<=>ABCDEFGHIMNOPRSTWXYZ^abcdefghimnoprstwxyz\u00b4\u015a\u015b\u0160\u0161\u017f\u0406\u0413\u0416\u0419\u041a\u041b\u041c\u041d" +
|
||||
"\u0425\u0427\u0433\u0436\u0439\u043a\u043b\u043c\u043d\u0445\u0447\u0456\u1e62\u1e63\u25cb\u02d9\u0000Ss\u1e68\u0000Ss\u1e64\u00b7\u2026\u2235\u2039\u2022\u203a\u0226\u1e02\u010a\u1e0a\u0116\u1e1e\u0120\u1e22\u0130\u1e40\u1e44\u022e\u1e56\u1e58\u1e60\u1e6a\u1e86\u1e8a\u1e8e\u017b\u0000Ss\u1e66\u04f6\u0496\u048a\u049a\u052e\u04cd\u04b2\u04b6\u04f7\u0497\u048b\u049b\u052f\u04ce\u04b3\u04b7\u2299\u0000" +
|
||||
"-/<=BCDGHILOTZ^bcdghilmotuvz\u0294\u0413\u041a\u0433\u043a\u04ae\u04af\u2190\u2192\u2194\u2395\\\u2260\u0243\u20a1\u01e4\u0126\u0197\u0141\u00d8\u0166\u01b5|\u00a2\u20a5\u00b5\u221a\u02a1\u0492\u049e\u0493\u049f\u04b0\u219a\u219b\u21ae\u2341\u0000*3~\u2189\u236c\u0000" +
|
||||
"123456789^\u00000\u2152\u00bd\u2153\u00bc\u2155\u2159\u2150\u215b\u2151\u000035^\u2154\u2156\u0000458^\u00be\u2157\u215c\u00005\u2158\u000068\u215a\u215d\u00008\u215e\u00008\u221e\u0000()-.\u2395\u2639\u263a\u2234\u2360\u0000 AEIOSTU_aei" +
|
||||
"ostu\u02db\u0218\u021a\u236e\u0219\u021b\u0000 \"'-/3<=>CDELNRSTZ_cdelnrstz\u2395\u226e\u2665\u00ab\u2264\u22c4\u010c\u010e\u011a\u013d\u0147\u0158\u0160\u0164\u017d\u2343\u0000/<>CELNOPRTUWY^_" +
|
||||
"cdeopruvy\u0415\u0417\u0421\u0423\u0437\u0443\u2395\u21d0\u21d2\u20ac\u20a4\u20a6\u0150\u20bd\u20b9\u20ae\u0170\u20a9\u00a5\u21d1\u2261\u20ab\u21d3\u04f2\u2338\u0000 \"'/<=>AEIOU_aeiou\u00a8\u2395\u226f\u2265\u00bb\u00c2\u00ca\u00ce\u00d4\u00db\u2369\u2344\u0000!+?AEI" +
|
||||
"OUY\\^abeiouy\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u0102\u0103\u01a0\u01a1\u01af\u01b0\u2395\u2e18\u0000OUou\u1ede\u1eec\u00bf\u1ea2\u1eba\u1ec8\u1ece\u1ee6\u1ef6\u262d\u0000AEOaeo\u1ea8\u1ec2\u1ed4\u0000Aa\u1eb2\u2370\u0000\"'(*,-;>AET^_`a" +
|
||||
"et~\u00a8\u00b4\u0102\u00c6@\u00c0\u00c3\u0000.|\u0e3f\u0000',./<=CEOr|\u2102\u20a0\u00a9\u20a2\u20b5\u0000,-.<Hh\u00d0\u0000\"',-.;<=>E^_`e~\u00a8\u00b4\u018f\u00c8\u1ebc\u0000.Uilr\uFFFF\ud83d\udd95\uFFFF\ufb03\uFFFF" +
|
||||
"\ufb04\u20a3\u0000(,.TUnu|\u02d8\u011e>\u0000~\uFFFF\u004e\u0360\u0067\u20b2\u0000,\u0000\"',-.;>J^_`j~\u00a8\u00b4\u0132\u00cc\u0128\u0000'\u00b4\u0000,-\u0000',-/<=LTV\u0000A\u0000P\uFFFF\ud83d\udd96<\u0000.\u0000',<" +
|
||||
"=GNOgno~\u014a\u2115\u2116\u00d1\u0000\"',-/;>ACERSXY^_`e~\u00a8\u00b4\u0152\u00ae\u00a7\u00a4\u262e\u00d2\u00d5\u0000!.=Pt\u00b6\u20a7\u0000Qq\u211a\u0000',<=ORrs\u211d\u20a8\u0000!',.;<M" +
|
||||
"OSs|\u2120\u1e9e$\u0000,-./;<=HMhz\u00de\u2122\uFFFF\ua728\u0000 !\"'*,-;>AEGIOU^_`aegiou~\u00a8\u00b4\u00b8\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0416\u0418\u0423\u0430\u0435\u0436\u0438\u0443\u0000,\u0000E" +
|
||||
"e\u1e1c\u0000Aa\u1eb6\u0114\u012c\u014e\u016c\u00d9\u0103\u0115\u011f\u012d\u014f\u016d\u0168\u0000Ee\u1fb8\u1fd8\u1fe8\u1fb0\u1fd0\u1fe0\u04d0\u04d6\u04c1\u0419\u040e\u04d1\u04d7\u04c2\u0439\u045e\u0000Lcsz~\u1e7c\u0000=^\u0174\u0000O\u0000\"'=^~\u00a8\u00b4\u0176\u1ef8\u0000'.<Zz\u2124\u0000]\u2337\u0000-" +
|
||||
"?not\u2395\u25cb\uFFFF\\\u006e\u0000/\uFFFF\ud83d\ude4c\uFFFF\\\u0074\u2342\u2349\u0000[\u0000!()+-./0123456789=ACEGHIJOSUWYZ_aceghijosuwyz|\u0410\u0415\u0418\u041e" +
|
||||
"\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u1eb8\u1eb9\u1ecc\u1ecd\u2212\u4e00\u4e01\u4e09\u4e0a\u4e0b\u4e19\u4e2d\u4e59\u4e8c\u4eba\u56db\u5730\u5929\u7532\u0000AEOaeo\u1eac\u1ec6\u1ed8\u0108\u011c\u0124\u0134\u015c\u1e90\u0000ahijlnorswxy\u0263\u0266\u0279\u027b\u0281\u0295\uFFFF\u0410\u0302\uFFFF\u0415\u0302\uFFFF\u0418\u0302\uFFFF" +
|
||||
"\u041e\u0302\uFFFF\u0420\u0302\uFFFF\u0423\u0302\uFFFF\u0440\u0302\u3192\u319c\u3194\u3196\u3198\u319b\u3197\u319a\u3193\u319f\u3195\u319e\u319d\u3199\u0000 !\"'()+-.0123456789;<=>AEGIOUY^_aegiouy~\u00c4\u00c6\u00d5\u00d6\u00dc\u00e4" +
|
||||
"\u00e6\u00f5\u00f6\u00fc\u0226\u0227\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u1e36\u1e37\u1e5a\u1e5b\u2206\u220a\u2212\u2218\u2260\u2282\u2283\u22a5\u22c4\u2373\u2375\u237a\u25cb\u0000LRlr\u1e38\u1e5c\u0000AOUaou\u01de\u022a\u01d5\u2358\u0000AEOaeo\u01e0\u0230\u0000Oo\u01ec" +
|
||||
"\u1e20\u0232\u0000Oo\u022c\u01e2\u1fb9\u1fd9\u1fe9\uFFFF\u0410\u0304\uFFFF\u0415\u0304\u04e2\uFFFF\u041e\u0304\uFFFF\u0420\u0304\u04ee\uFFFF\u0430\u0304\uFFFF\u0435\u0304\uFFFF\u043e\u0304\uFFFF\u0440\u0304\u2359\u2377\u235b\u2262\u2286\u2287\u234a\u235a\u2378\u2379\u2376\u235c\u0000\"()+AEINOUWY^_`abeinouw" +
|
||||
"y\u00af\u00c2\u00ca\u00d4\u00dc\u00e2\u00ea\u00f4\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u01a0\u01a1\u01af\u01b0\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u0000Uu\u03b9\u03c5\u01db\u1fd2\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0b\u1f1b\u1f2b\u1f3b" +
|
||||
"\u1f4b\u1f5b\u1f6b\u1f03\u1f13\u1f23\u1f33\u1f43\u1f53\u1f63\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0a\u1f1a\u1f2a\u1f3a\u1f4a\u1f6a\u1f02\u1f12\u1f22\u1f32\u1f42\u1f52\u1f62\u0000OUou\u1edc\u1eea\u01f8\u1e80\u1ef2\u0000AEOaeo\u1ea6\u1ec0\u1ed2\u0000EOeo\u1e14\u1e50\u0000\u0410\u0415\u0418\u041e\u0420\u0423\u0430" +
|
||||
"\u0435\u0438\u043e\u0440\u0443\uFFFF\u0410\u030f\uFFFF\u0415\u030f\uFFFF\u0418\u030f\uFFFF\u041e\u030f\uFFFF\u0420\u030f\uFFFF\u0423\u030f\u0000Aa\u1eb0\u0000EOeo\u1fba\u1fc8\u1fca\u1fda\u1ff8\u1fea\u1ffa\uFFFF\u0410\u0300\u0400\u040d\uFFFF\u041e\u0300\uFFFF\u0420\u0300\uFFFF\u0423\u0300\uFFFF\u0430\u0300\uFFFF\u043e\u0300\uFFFF\u0440\u0300\uFFFF\u0443\u0300\u0000\"'(*,-" +
|
||||
";>E^_`ae~\u00a8\u00b4\u00e6\u0000!,.AEGIOUaegiou\u00b8\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0416\u0418\u0423\u0430\u0435\u0436\u0438\u0443\u0000Aa\u0000Ee\u0000Ee\u0000\"',./<=ACDEGHIKLN" +
|
||||
"ORSTUZacdeghijklnorstuz|\u00dc\u00fc\u0000Uu\u01d9\u01cd\u01e6\u021e\u01cf\u01e8\u01d1\u01d3\u0000,-.<=hi\u00f0\u2300\u0000\"',-.;<=>^_`e~\u00a8\u00b4\u0259\u0000.Sfils" +
|
||||
"\u017f\uFFFF\ufb00\uFFFF\ufb01\uFFFF\ufb02\u0000(,.Untu~\u02d8\u0000~\uFFFF\u006e\u0360\u0067\uFFFF\u0067\u0303\u0000,\u0000\"',-.;>^_`j~\u00a8\u00b4\u0133\u0000'\u00b4\u0000,k\u0138\u0000',-/<tv\u0000./u\u0000',<g~\u014b\u0000\"" +
|
||||
"',-/;>AU^_`aceorsuwxy~\u00a8\u00b4\u0153\u0000!.=o\u0000o\uFFFF\ud83d\udca9\u0000',<=\u0000!',.;<mos\u00b8\u00df\u0000,-./;<hmz\u00fe\uFFFF\ua729\u0000\"'*,-/" +
|
||||
";>AEGIOU^_`aegiou~\u00a8\u00b4\u0000/ACDEGHIKNORSTUZacdeghijklnorstuz|~\u0000^\u0000ox\u00d7\u0000\"'=^~\u00a8\u00b4\u0000'" +
|
||||
".<\u0000}\u2205\u0000-=BCGS^cv~\u2190\u2192\u2206\u2207\u222a\u2282\u25cb\u2020\u2021\u236d\u2345\u2346\u234b\u2352\u2366\u2367\u233d\u0000\"()+0AEINOUVY^abeinouvy|~\u00a8\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u0102\u0103\u01a0\u01a1\u01af\u01b0" +
|
||||
"\u03b1\u03b7\u03b9\u03c5\u03c9\u2207\u2227\u2228\u0000\u03b9\u03c5\u1fd7\u0000\u0391\u0397\u0399\u03a5\u03a9\u03b1\u03b7\u03b9\u03c5\u03c9\u1f0f\u1f2f\u1f3f\u1f5f\u1f6f\u1f07\u1f27\u1f37\u1f57\u1f67\u0000\u0391\u0397\u0399\u03a9\u03b1\u03b7\u03b9\u03c5\u03c9\u1f0e\u1f2e\u1f3e\u1f6e\u1f06\u1f26\u1f36\u1f56\u1f66\u0000OUou\u1ee0\u1eee\u0000AEOaeo\u1eaa\u1ec4\u1ed6\u0000Aa" +
|
||||
"\u1eb4\u2248\u1fb6\u1fc6\u1fd6\u1fe6\u1ff6\u236b\u2372\u2371\u0000'*>AEIOUY`aeiouy~\u00b4\u2207\u2218\u22a4\u25cb\u1fed\u1fc1\u2361\u2365\u0000!\".;AEGIOUYaegiouy~\u00c4\u00c6\u00d5\u00d6\u00dc\u00e4\u00e6\u00f5\u00f6\u00fc\u0226\u0227\u0391\u0399\u03a5" +
|
||||
"\u03b1\u03b9\u03c5\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u1e36\u1e37\u1e5a\u1e5b\u22a4\u0000LRlr\u0000AOUaou\u0000AOao\u0000Oo\u0000Oo\u2351\u0000\"()+,/ACEGIJKLMNOPRSUWYZ^_a" +
|
||||
"bcegijklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00d8\u00dc\u00e2\u00e5\u00e6\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u0168\u0169\u01a0\u01a1\u01af\u01b0\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0413\u0415\u0418\u041a" +
|
||||
"\u041e\u0420\u0423\u042b\u042d\u042e\u042f\u0430\u0433\u0435\u0438\u043a\u043e\u0440\u0443\u044b\u044d\u044e\u044f\u0000IUiu\u03b9\u03c5\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0000OUou\u0000Cc\u0000Oo\u0000AEOae" +
|
||||
"o\u0000EOeo\u0000Aa\u0000OUou\u0000EOeo\u0000Cc\u0000CDEGHKLNRSTcdeghklnrst\u0228\u0000\u2395\u2339\u0000Gg\u0000'\u0000'\u0000'\u0000\"'\u0000'\u0000\"'\u0000'\u0000'\u0000'" +
|
||||
"\u0000'\u0000\"'()`~\u00b4\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8d\u1f9d\u1fad\u1f85\u1f95\u1fa5\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8c\u1f9c\u1fac\u1f84\u1f94\u1fa4\u1fb4\u1fc4\u1ff4\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f89\u1f99\u1fa9\u1f81\u1f91\u1fa1\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f88" +
|
||||
"\u1f98\u1fa8\u1f80\u1f90\u1fa0\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8b\u1f9b\u1fab\u1f83\u1f93\u1fa3\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8a\u1f9a\u1faa\u1f82\u1f92\u1fa2\u1fb2\u1fc2\u1ff2\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8f\u1f9f\u1faf\u1f87\u1f97\u1fa7\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8e\u1f9e\u1fae\u1f86\u1f96\u1fa6" +
|
||||
"\u1fb7\u1fc7\u1ff7\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1fbc\u1fcc\u1ffc\u1fb3\u1fc3\u1ff3\u0000'\u0000\"'\u0000'\u0000\".\u0406\u0423\u0443\u0456\u0474\u0000\u0410\u0430\u0462\u0463\uFFFF\ua656\uFFFF\ua65c\u0000\u0415\u0423\u042b\u042c\u042d\u0435\u0443\u044b\u044c\u044d\ua64a\ua64b\ua650\ua651\u04d4\u0460\u0462\uFFFF" +
|
||||
"\ua658\u04d8\uFFFF\ua64c\u0000,.\u0408\u0419\u041a\u0425\u0439\u043a\u0445\u0458\u0490\u0480\u0000\u0408\u0415\u0416\u0417\u0419\u0435\u0436\u0437\u0439\u0458\u0464\u0465\uFFFF\ua648\u040f\uFFFF\ua642\u0402\u0000=\u0000.\u0000=\u0424\u0444\u0498\u0000\u0418\u0423\u0438\u0443\u0000.\u0000,.\u0413\u0421\u0425\u0428\u0433\u0441\u0445\u0448\u046e\u0000,.\u042c\u044c\u0409\u0000." +
|
||||
"\u0000,\u00b7\u041e\u042c\u043e\u044c\u0528\u040a\u0000\u0422\u0423\u0442\u0443\u047e\u0478\u0000\u0421\u0441\u0470\u0000=\u0000\u0408\u0419\u0421\u0424\u0439\u0441\u0444\u0458\u040b\u0426\u0472\u0000\u0406\u0408\u0418\u0419\u0423\u0438\u0439\u0443\u0456\u0458\u0000,.\u0000.\u0000\u0422\u0442\u0429\u0000\u0406\u0410\u0415\u041c\u041d\u041e\u042c\u042d\u0430\u0435\u043c\u043d\u043e\u044c\u044d\u0456\u0464" +
|
||||
"\u046c\u0468\uFFFF\ua652\u0000\u0435\u0443\u0447\u044b\u044c\u044d\ua64b\ua651\u04d5\u0461\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0069\u0067\u0075\u0463\uFFFF\ua659\u04d9\uFFFF\ua64d\u0000\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u006c\u0061\u0076\u006f\u006e\u0069\u0063\u005f\u0070\u0073\u0069\u006c\u0069\u0000,.\u0439\u043a\u0445\u0458\u0491\u0481" +
|
||||
"\u0000\u0435\u0436\u0437\u0439\u0458\u0465\uFFFF\ua649\u045f\uFFFF\ua643\u0452\u0000\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0074\u0072\u0065\u006d\u0061\u0000.\u0000=\u0444\u0499\u0000\u0438\u0443\u0475\u0000.\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0062\u0072\u0065\u0076\u0065\u0000,.\u0433\u0441\u0445\u0448\u046f\u0000,.\u044c" +
|
||||
"\u0459\u0000.\u0000,\u00b7\u0447\u044c\u0529\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0074\u0069\u0074\u006c\u006f\u045a\u0000\u0442\u0443\u0447\u047f\u0479\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0069\u006e\u0076\u0065\u0072\u0074\u0065\u0064\u005f\u0062\u0072\u0065\u0076\u0065\u0000\u0430\u0441\u0471\u0000\u0439\u0441\u0444\u0458\u045b\u0446\u0473\u0000\u0438\u0439" +
|
||||
"\u0443\u0447\u0456\u0458\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0070\u006f\u006b\u0072\u0079\u0074\u0069\u0065\u0000,.\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u006c\u0061\u0076\u006f\u006e\u0069\u0063\u005f\u0064\u0061\u0073\u0069\u0061\u0000.\u0430\u0431\u0435\u0439\u043d\u043e\u0443\u0445\u0447\u044a\u044e\u0458\u0467\uFFFF\u0063\u006f\u006d\u0062" +
|
||||
"\u0069\u006e\u0069\u006e\u0067\u005f\u0070\u0061\u0079\u0065\u0072\u006f\u006b\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0076\u0065\u0072\u0074\u0069\u0063\u0061\u006c\u005f\u0074\u0069\u006c\u0064\u0065\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0067\u0072\u0061\u0076\u0065\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0076\u007a\u006d\u0065\u0074\u0000\u0442" +
|
||||
"\u0449\u0000\u0447\u0000\u0430\u0435\u043c\u043d\u043e\u0447\u044c\u044d\u0456\uFFFF\ua657\u0465\u046d\u0469\uFFFF\ua65d\uFFFF\ua653\u0000\".\u0443\u0456\u0000\u0430\u0447\u0463\u0000\u0422\u0442\u0000\u0442\u0000\u0447\u0000\u05d9\uFFFF\ufb1d\u0000\u05d0\u05f2\uFFFF\ufb2e\uFFFF\ufb1f\u0000\u05d0\uFFFF\ufb2f\u0000\u05d5\uFFFF\ufb4b\u0000\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d8\u05d9\u05da\u05db\u05dc\u05de\u05e0" +
|
||||
"\u05e1\u05e3\u05e4\u05e6\u05e7\u05e8\u05e9\u05ea\uFFFF\ufb30\uFFFF\ufb31\uFFFF\ufb32\uFFFF\ufb33\uFFFF\ufb34\uFFFF\ufb35\uFFFF\ufb36\uFFFF\ufb38\uFFFF\ufb39\uFFFF\ufb3a\uFFFF\ufb3b\uFFFF\ufb3c\uFFFF\ufb3e\uFFFF\ufb40\uFFFF\ufb41\uFFFF\ufb43\uFFFF\ufb44\uFFFF\ufb46\uFFFF\ufb47\uFFFF\ufb48\uFFFF\ufb49\uFFFF\ufb4a\u0000\u05d1\u05db\u05e4\uFFFF\ufb4c\uFFFF\ufb4d\uFFFF\ufb4e\u0000\u05bc\u05e9\ufb49\u0000\u05e9\uFFFF\ufb2c\uFFFF\ufb2a" +
|
||||
"\u0000\u05bc\u05e9\ufb49\u0000\u05e9\uFFFF\ufb2d\uFFFF\ufb2b\u0000\u0627\u0639\u0648\u064a\u0667\u06cc\u06f7\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u006c\u0065\u0066\u005f\u0061\u0062\u006f\u0076\u0065\u0623\u06c9\u063d\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u006c\u0065\u0066\u005f\u0062\u0065\u006c\u006f\u0077\u0000\u062f\u0631\u0634\u0646\u0637\u0691\u062b\u0679" +
|
||||
"\u0000\u0634\u0686\u0000\u062d\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u0075\u006b\u0075\u006e\u0000\u062a\u0632\u06f7\u0630\u0695\u0000\u0634\u0635\u0000\u062a\u0000\u0627\u0647\u0648\u064a\u0667\u0668\u06cc\u06f7\u06f8\u0625\u06c0\u0624\u0626\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0068\u0061\u006d\u007a\u0061\u005f\u0062\u0065\u006c\u006f\u0077\uFFFF\u0063\u006f\u006d" +
|
||||
"\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0068\u0061\u006d\u007a\u0061\u005f\u0061\u0062\u006f\u0076\u0065\u0000\u0643\u06a9\u06af\u0000\u0648\u06a1\u0000\u0648\u06ca\u0000\u0644\u0667\u06f7\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u0068\u0061\u0064\u0064\u0061\u0068\u06b5\u0000\u062a\u0647\u0648\u064a\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0066\u0061\u0074\u0068" +
|
||||
"\u0061\u0074\u0061\u006e\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0064\u0061\u006d\u006d\u0061\u0074\u0061\u006e\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u006b\u0061\u0073\u0072\u0061\u0074\u0061\u006e\u0000 \u062a\u0639\u0646\u0647\u0648\u064a\u06cc\u06d5\u0629\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0066\u0061\u0074\u0068\u0061\u06c6\u06ce\u0000" +
|
||||
"\u062b\u0639\u0641\u0646\u0648\u0667\u0668\u06f7\u06f8\u06cb\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0064\u0061\u006d\u006d\u0061\u0068\u0000 \u0627\u0639\u0646\u064a\u0667\u0668\u06f7\u06f8\u06d2\u0649\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u006b\u0061\u0073\u0072\u0061\u0000\u0627\u0622\u0000\u0627\u0648\u064a\u06c1\u06d2\u06d5\u06c2\u06d3\u0000\u0627\u0000\u0627\u0631" +
|
||||
"\u0639\u0644\u0648\u064a\u0667\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0072\u0061\u0062\u0069\u0063\u005f\u0076\u0000\u0639\u0648\u064a\u0668\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0072\u0061\u0062\u0069\u0063\u005f\u0069\u006e\u0076\u0065\u0072\u0074\u0065\u0064\u005f\u0076\u0000 \u0627\u0639\u0646\u0667\u0668\u06cc\u06f7\u06f8\u0000\u0627\u0631" +
|
||||
"\u0639\u0644\u0648\u064a\u06cc\u06f7\u0000\u0639\u0648\u064a\u06cc\u06f8\u0000\u0915\u0916\u0917\u091c\u0921\u0922\u0928\u092b\u092f\u0930\u0933\u0958\u0959\u095a\u095b\u095c\u095d\u0929\u095e\u095f\u0931\u0934\u0000\u09a1\u09a2\u09af\u09dc\u09dd\u09df\u0000\u09be\u09d7\u09cb\u09cc\u0000\u0a16\u0a17\u0a1c\u0a2b\u0a32\u0a38\u0a59\u0a5a\u0a5b\u0a5e\u0a33\u0a36\u0000\u0b21\u0b22\u0b5c\u0b5d\u0000\u0b3e\u0b56\u0b57\u0b4b\u0b48\u0b4c" +
|
||||
"\u0000\u0bbe\u0bd7\u0bca\u0bcc\u0000\u0bbe\u0bcb\u0000\u0b92\u0b94\u0000\u0c56\u0c48\u0000\u0cd5\u0cc0\u0000\u0cc2\u0cd5\u0cd6\u0cca\u0cc7\u0cc8\u0000\u0cd5\u0ccb\u0000\u0d3e\u0d57\u0d4a\u0d4c\u0000\u0d3e\u0d4b\u0000\u0dca\u0dcf\u0ddf\u0dda\u0ddc\u0dde\u0000\u0dca\u0ddd\u0000\u0f72\u0f74\u0f80\u0f73\u0f75\u0f81\u0000\u0fb5\u0fb9\u0000\u0fb7\u0f93\u0000\u0fb7\u0f9d\u0000\u0fb7\u0fa2\u0000\u0fb7\u0fa7\u0000\u0fb7\u0fac\u0000\u0f80" +
|
||||
"\u0f76\u0000\u0f80\u0f78\u0000\u0f40\u0f69\u0000\u0f42\u0f4c\u0f51\u0f56\u0f5b\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0000\u1025\u1026\u0000\u1100\u1101\u0000\u1100\u1102\u1103\u1107\u1113\u1114\u1115\u1116\u0000\u1100\u1103\u1117\u1104\u0000\u1102\u1105\u110b\u1112\u1118\u1119\u111b\u111a\u0000\u1107\u110b\u111c\u111d\u0000\u1100\u1102\u1103\u1107\u1109\u110a\u110b\u110c\u110e\u1110\u1111\u112b\u112d\u112f\u1132\u1136\u111e\u111f\u1120" +
|
||||
"\u1108\u1121\u1125\u112b\u1127\u1128\u1129\u112a\u112c\u1122\u1123\u1124\u1126\u0000\u110b\u0000\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110a\u110b\u110c\u110e\u110f\u1110\u1111\u1112\u111e\u112d\u112e\u112f\u1130\u1131\u1132\u110a\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u1133\u0000\u1109\u0000\u1100\u1103\u1106\u1107\u1109\u110b\u110c\u110e\u1110\u1111\u1140\u1141\u1142\u1143\u1144\u1145\u1147\u1148\u1149\u114a\u114b" +
|
||||
"\u1146\u0000\u110b\u110c\u114d\u110d\u0000\u110f\u1112\u1152\u1153\u0000\u1107\u110b\u1156\u1157\u0000\u1112\u1158\u0000\u1100\u1103\u1107\u1109\u110c\u0000\u1100\u0000\u113c\u113d\u0000\u113e\u113f\u0000\u114e\u114f\u0000\u1150\u1151\u0000\u1169\u116e\u1175\u1176\u1177\u1162\u0000\u1169\u116d\u1175\u1178\u1179\u1164\u0000\u1169\u116e\u1173\u1175\u117a\u117b\u117c\u1166\u0000\u1169\u116e\u1175\u117d\u117e\u1168\u0000\u1161\u1162" +
|
||||
"\u1165\u1166\u1168\u1169\u116e\u1175\u116a\u116b\u117f\u1180\u1181\u1182\u1183\u116c\u0000\u1175\u0000\u1163\u1164\u1167\u1169\u1175\u1184\u1185\u1186\u1187\u1188\u0000\u1161\u1162\u1165\u1166\u1168\u116e\u1175\u117c\u1189\u118a\u116f\u1170\u118c\u118d\u1171\u118b\u0000\u1173\u1175\u0000\u1161\u1165\u1166\u1167\u1168\u116e\u1175\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u0000\u116e\u1173\u1175\u1195\u1196\u1174\u0000\u116e\u1197" +
|
||||
"\u0000\u1161\u1163\u1169\u116e\u1173\u119e\u1198\u1199\u119a\u119b\u119c\u119d\u0000\u1165\u116e\u1175\u119e\u119f\u11a0\u11a1\u11a2\u0000\u11a8\u11af\u11ba\u11e7\u11a9\u11c3\u11aa\u11c4\u0000\u11a8\u0000\u11a8\u11ae\u11ba\u11bd\u11c0\u11c2\u11eb\u11c5\u11c6\u11c7\u11ac\u11c9\u11ad\u11c8\u0000\u11a8\u11af\u11ca\u11cb\u0000\u11a8\u11aa\u11ab\u11ae\u11af\u11b7\u11b8\u11b9\u11ba\u11bb\u11bf\u11c0\u11c1\u11c2\u11da\u11dd\u11e5\u11e6" +
|
||||
"\u11eb\u11f9\u11b0\u11cc\u11cd\u11ce\u11d0\u11b1\u11b2\u11d3\u11b3\u11d6\u11d8\u11b4\u11b5\u11b6\u11d1\u11d2\u11d4\u11d5\u11d7\u11d9\u0000\u11ba\u0000\u11a8\u11ba\u0000\u11ba\u11bc\u11c2\u0000\u11ba\u0000\u11a8\u11af\u11b8\u11ba\u11bb\u11bc\u11be\u11c2\u11eb\u11da\u11db\u11dc\u11dd\u11de\u11e2\u11e0\u11e1\u11df\u0000\u11af\u11ba\u11bc\u11c1\u11c2\u11e3\u11b9\u11e6\u11e4\u11e5\u0000\u11a8\u11ae\u11af\u11b8\u11ba\u11e7\u11e8\u11e9" +
|
||||
"\u11ea\u11bb\u0000\u11a8\u11a9\u11bc\u11bf\u11ec\u11ed\u11ee\u11ef\u0000\u11b8\u11bc\u11f3\u11f4\u0000\u11ab\u11af\u11b7\u11b8\u11f5\u11f6\u11f7\u11f8\u0000\u11c2\u11cf\u0000\u11ba\u0000\u11a8\u0000\u11ba\u11eb\u11f1\u11f2\u0000|\u2395\u2347\u0000-\u2395\u2350\u0000|\u2395\u2348\u0000-\u2395\u2357\u0000/\u2204\u0000_|\u2395\u234d\u0000|~\u00a8\u2395\u2354\u0000/\u2209\u0000_" +
|
||||
"\u0000/\u220c\u0000_\u00a8\u2229\u22a4\u22a5\u2395\u25cb\u235d\u2355\u234e\u233b\u233e\u0000/\u2224\u0000/\u2226\u0000~\u2228\u2395\u2353\u0000~\u2227\u2395\u234c\u0000\u2218\u0000|\u0000/\u2241\u0000/\u2244\u0000/\u2249\u0000/\u226d\u0000_\u2395\u236f\u0000/\u0000/\u2270\u0000/\u2271\u0000/\u2274\u0000/\u2275\u0000/\u2278\u0000/\u2279" +
|
||||
"\u0000/\u2280\u0000/\u2281\u0000/\u22e0\u0000/\u22e1\u0000/_|\u2284\u0000/_\u2285\u0000/\u2288\u0000/\u2289\u0000/\u22e2\u0000/\u22e3\u0000/\u22ac\u0000\u00a8\u00af\u2218\u22a5\u2336\u0000_\u2218\u22a4\u0000/\u22ad\u0000/\u22ae\u0000/\u22af\u0000/\u22ea\u0000/\u22eb\u0000/\u22ec\u0000/\u22ed\u0000_\u2395\u233a\u0000" +
|
||||
"_\u0000_\u0000_\u0000'/:<=>?\\\u00f7\u2190\u2191\u2192\u2193\u2206\u2207\u2218\u2227\u2228\u2260\u22c4\u25cb\u233c\u0000*-.\\_|\u00a8\u2218\u2395\u0000/\u2adc\u0000 !\"%'()*,-.0123456789:<=>?[]^_" +
|
||||
"abcehlopruyz{|}\u00a3\u00a7\u00b1\u00d7\u0398\u03a0\u03a3\u03b2\u03b3\u03b5\u03b8\u03ba\u03c0\u03c1\u03c3\u03c5\u03c6\u0430\u0433\u0434\u0435\u0437\u0438\u0439\u043b\u043c\u043d\u043e\u0441\u0443\u0444\u0445\u0447\u0448\u044a\u044b\u044c\u044d\u044f\u0452\u0456\u0458\u045b\u045f\u0461\u0481\u0487\u049b\u04b7\u04c8\u0513\u05d1\u05d3\u05d5\u05d6\u05d7\u05dd" +
|
||||
"\u05e1\u05e3\u05e4\u05e6\u05e7\u05e8\u05e9\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062d\u062f\u0631\u0632\u0633\u0634\u0635\u0637\u0639\u063a\u063d\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u064a\u064f\u0650\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u0698\u06a1\u06a9\u06be\u06c6\u06c9\u06cc\u06ce\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0901\u0902\u0905\u0906\u0907\u0908\u0909\u090a" +
|
||||
"\u090b\u090c\u090f\u0910\u0913\u0914\u0915\u0916\u0917\u0918\u091a\u091b\u091c\u091d\u091f\u0921\u0922\u0923\u0925\u0926\u0928\u092a\u092b\u092c\u092f\u0930\u0933\u0935\u0936\u0937\u093c\u093d\u093e\u093f\u0940\u0941\u0943\u0947\u0948\u094b\u094c\u0953\u0956\u0962\u0964\u0970\u0b92\u0b9a\u0baf\u0bb3\u0bb5\u2020\u20ac\u20b9\u2190\u2191\u2192\u2193\u2194\u2195\u2196\u2197\u2198\u2199\u2203\u2206\u2207\u2227\u2228\u2229\u222a\u222b" +
|
||||
"\u2282\u2283\u22a4\u22b7\u22c4\u235d\u2375\u237a\u2395\ua649\ua651\ua657\ua67d\uFFFF\u0066\u0031\u0030\uFFFF\u0066\u0031\uFFFF\u0066\u0032\uFFFF\u0066\u0033\uFFFF\u0066\u0034\uFFFF\u0066\u0035\uFFFF\u0066\u0036\uFFFF\u0066\u0037\uFFFF\u0066\u0038\uFFFF\u0066\u0039\uFFFF\u007a\u0077\u006e\u006a\u20b1\u20b4\u20bf\uFFFF\u0072\u0065\u006d\u006f\u0076\u0065\u0064\u2213\u2219\u03f4\u220f\u2211\u03d0\u0263\u03f5\u03d1\u03f0\u03d6\u03f1" +
|
||||
"\u03c2\u03d2\u03d5\uFFFF\ua641\u0456\u0458\u046b\u0467\u047b\u0455\uFFFF\ua64b\u04bb\u044c\uFFFF\ua651\u044a\u0454\u0438\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0063\u0079\u0072\u0069\u006c\u006c\u0069\u0063\u005f\u0076\u007a\u006d\u0065\u0074\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0073\u0065\u0067\u006f\u006c\uFFFF\u0064\u0061\u0067\u0065\u0073\u0068\uFFFF\u0068\u006f\u006c\u0061\u006d\uFFFF" +
|
||||
"\u0071\u0075\u0062\u0075\u0074\u0073\uFFFF\u0068\u0069\u0072\u0069\u0071\uFFFF\u0072\u0061\u0066\u0065\uFFFF\u0073\u0065\u0067\u006f\u006c\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0070\u0061\u0074\u0061\u0068\uFFFF\u0070\u0061\u0074\u0061\u0068\uFFFF\u0074\u0073\u0065\u0072\u0065\uFFFF\u0071\u0061\u006d\u0061\u0074\u0073\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0071\u0061\u006d\u0061\u0074\u0073\uFFFF\u0073\u0068\u0065\u0076" +
|
||||
"\u0061\u066e\u067e\u06c1\u0698\u0636\u0638\u0621\u06a4\u063a\u06a9\u0640\u06ba\u06be\u06cc\u0643\u0647\u064a\u0900\u0955\u0972\u0911\u0973\u0974\u0976\u0977\u0960\u0961\u090d\u090e\u0912\u0975\u097b\u097c\uFFFF\u0936\u094d\u091a\u0979\u0978\u097e\uFFFF\u0924\u094d\u0930\uFFFF\u0926\u094d\u0930\uFFFF\u092a\u094d\u0930\u097f\u097a\uFFFF\u0936\u094d\u0930\uFFFF\u0915\u094d\u0937\u094e\u097d\u0949\u093a\u093b\u0904\u0944\u0945\u0946" +
|
||||
"\u094a\u094f\u0954\u0957\u0963\u0965\u0971\uFFFF\u0bd0\uFFFF\u0bf2\uFFFF\u0bf0\uFFFF\u0bf1\uFFFF\u0bf3\u21d4\u21d5\u21d6\u21d7\u21d8\u21d9\u22c0\u22c1\u22c2\u22c3\u222e\u22b6\u044b\u044f\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0063\u0079\u0072\u0069\u006c\u006c\u0069\u0063\u005f\u0074\u0069\u0074\u006c\u006f\u00000123456789\u09e6\u09e7\u09e8\u09e9\u09ea" +
|
||||
"\u09eb\u09ec\u09ed\u09ee\u09ef\u00000123456789\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u00000123456789\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u00000123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u0000012" +
|
||||
"3456789\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u00000123456789\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u00000123456789\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0000\u00df\u0131\u01f0\u0237\u02b0\u02b2\u02b3\u02b7\u02e1\u0905\u0907\u0909" +
|
||||
"\u090b\u090c\u090f\u0913\u0915\u0917\u091a\u091c\u091f\u0921\u0924\u0926\u0928\u092c\u092e\u0932\u0938\u0939\u093f\u0941\u0943\u0945\u0947\u0949\u094b\u0952\u0962\u0a85\u0a87\u0a89\u0a8f\u0a93\u0a95\u0a97\u0a9a\u0a9c\u0a9f\u0aa1\u0aa4\u0aa6\u0aa8\u0aaa\u0aac\u0aae\u0ab2\u0ab8\u0ab9\u0abf\u0ac1\u0ac7\u0acb\u0bf9\u1d43\u1d47\u1d48\u1d49\u1d4d\u1d4f\u1d50\u1d52\u1d56\u1d57\u1d58\u1d5b\u1d60\u1d9c\u1da0\u1dbe\u1e97\u1e98\u1e99\u2071" +
|
||||
"\u207f\u20b9\u2190\u2191\u2192\u2193\u2196\u2197\u2198\u2199\u2208\u220b\u2282\u2283\u2286\u2287\u2500\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\ud835\uFFFF\u004a\u030c\uFFFF\u004a\u0307\u1d34\u1d36\u1d3f\u1d42\u1d38\u0906\u0908\u090a\u0910\u0914\u0916\u0918\u091b\u091d\u0920\u0922\u0925\u0927\u0923\u092d\u0902\u0933\u0936\u0903\u0940\u0942\u0948\u094c\u0951\u0a86\u0a88\u0a8a\u0a90\u0a94\u0a96\u0a98\u0a9b\u0a9d" +
|
||||
"\u0aa0\u0aa2\u0aa5\u0aa7\u0aa3\u0aab\u0aad\u0a82\u0ab3\u0ab6\u0a83\u0ac0\u0ac2\u0ac8\u0acc\u1d2c\u1d2e\u1d30\u1d31\u1d33\u1d37\u1d39\u1d3c\u1d3e\u1d40\u1d41\u2c7d\u1db2\uFFFF\ua7f2\uFFFF\ua7f3\uFFFF\u0054\u0308\uFFFF\u0057\u030a\uFFFF\u0059\u030a\u1d35\u1d3a\u2550\u2551\u2554\u2557\u255a\u255d\u2560\u2563\u2566\u2569\u256c\u0000\udd57\udd58\udd64\udd68\udd69\uFFFF\ud835\udd3d\uFFFF\ud835\udd3e\uFFFF\ud835\udd4a\uFFFF\ud835\udd4e" +
|
||||
"\uFFFF\ud835\udd4f\u0000ABCDEFGHIJKLMNOPQRSTUVWXYZx\u00a2\u00a3\u00a5\u00aa\u00b5\u00ba\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5\u00e6" +
|
||||
"\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129\u012a\u012b\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b\u013c" +
|
||||
"\u013d\u013e\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014c\u014d\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d\u017e\u0180\u0194\u0197\u019a\u019f\u01a0\u01a1\u01a6\u01af\u01b0\u01b5\u01b6\u01cd\u01ce\u01cf" +
|
||||
"\u01d0\u01d1\u01d2\u01d3\u01d4\u01d5\u01d6\u01d7\u01d8\u01d9\u01da\u01db\u01dc\u01de\u01df\u01e0\u01e1\u01e2\u01e3\u01e4\u01e5\u01e6\u01e7\u01e8\u01e9\u01ea\u01eb\u01ec\u01ed\u01ee\u01ef\u01f0\u01f4\u01f5\u01f8\u01f9\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff\u0200\u0201\u0204\u0205\u0208\u0209\u020c\u020d\u0210\u0211\u0214\u0215\u021e\u021f\u0226\u0227\u0228\u0229\u022a\u022b\u022c\u022d\u022e\u022f\u0230\u0231\u0232\u0233\u0237\u023a" +
|
||||
"\u023b\u023c\u023d\u023e\u0243\u0244\u0246\u0247\u0248\u0249\u024c\u024d\u024e\u024f\u025f\u0262\u0263\u0268\u026a\u0274\u0275\u0276\u0280\u0289\u028f\u0299\u029b\u029c\u029f\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02e0\u02e1\u02e2\u02e3\u0386\u0388\u0389\u038a\u038c\u038e\u0392\u0395\u0398\u039a\u039c\u03a0\u03a1\u03a3\u03a6\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03c2\u03ca\u03cb\u03cc\u03cd\u03d0\u03d1\u03d2" +
|
||||
"\u03d4\u03d5\u03d6\u03f0\u03f1\u03f4\u03f5\u0400\u0402\u0403\u0404\u0405\u0406\u0408\u040b\u040c\u040d\u040f\u0418\u0419\u0427\u042a\u042b\u042c\u042f\u0438\u0439\u0447\u044a\u044b\u044c\u044f\u0450\u0452\u0453\u0454\u0455\u0456\u0458\u045b\u045c\u045d\u045f\u0462\u0463\u0466\u0467\u046a\u046b\u0470\u0471\u0472\u0473\u0476\u0477\u047a\u047b\u0480\u0481\u048c\u048d\u0490\u0491\u0498\u0499\u049a\u049b\u04b0\u04b1\u04b6\u04b7\u04ba" +
|
||||
"\u04bb\u04d2\u04d3\u04da\u04db\u04dc\u04dd\u04de\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04e8\u04e9\u04ea\u04eb\u04ec\u04ed\u04ee\u04ef\u04f0\u04f1\u04f2\u04f3\u04f4\u04f5\u04f8\u04f9\u04fe\u04ff\u0528\u0529\u052e\u052f\u0621\u0622\u0630\u0635\u0636\u0637\u0638\u063a\u0640\u0643\u0647\u064a\u066e\u067e\u0695\u0698\u06a1\u06a4\u06a9\u06af\u06b5\u06ba\u06be\u06c1\u06cc\u0902\u0903\u0906\u0908\u090a\u090d\u090e\u0910\u0911\u0912" +
|
||||
"\u0914\u0916\u0918\u091b\u091d\u0920\u0922\u0923\u0925\u0927\u0929\u092d\u0931\u0933\u0934\u0936\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097d\u097e\u097f\u0a82\u0a83\u0a86\u0a88\u0a8a\u0a90\u0a94\u0a96\u0a98\u0a9b\u0a9d\u0aa0\u0aa2\u0aa3\u0aa5\u0aa7\u0aab\u0aad\u0ab3\u0ab6\u1d00\u1d01\u1d03\u1d04\u1d05\u1d06\u1d07\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e" +
|
||||
"\u1d0f\u1d10\u1d15\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d20\u1d21\u1d22\u1d23\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d43\u1d44\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5d\u1d5f\u1d60\u1d61\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d7b\u1d7d\u1d7e\u1d9b\u1d9c\u1d9d\u1d9f\u1da0\u1da1\u1da3\u1da4\u1da5\u1dac\u1dad\u1db1\u1db3\u1db4\u1db6\u1db7" +
|
||||
"\u1dba\u1dbb\u1dbe\u1dbf\u1e02\u1e03\u1e04\u1e05\u1e08\u1e09\u1e0a\u1e0b\u1e0c\u1e0d\u1e10\u1e11\u1e14\u1e15\u1e16\u1e17\u1e1c\u1e1d\u1e1e\u1e1f\u1e20\u1e21\u1e22\u1e23\u1e24\u1e25\u1e26\u1e27\u1e28\u1e29\u1e2e\u1e2f\u1e30\u1e31\u1e32\u1e33\u1e36\u1e37\u1e38\u1e39\u1e3e\u1e3f\u1e40\u1e41\u1e42\u1e43\u1e44\u1e45\u1e46\u1e47\u1e4c\u1e4d\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u1e54\u1e55\u1e56\u1e57\u1e58\u1e59\u1e5a\u1e5b\u1e5c\u1e5d" +
|
||||
"\u1e60\u1e61\u1e62\u1e63\u1e64\u1e65\u1e66\u1e67\u1e68\u1e69\u1e6a\u1e6b\u1e6c\u1e6d\u1e78\u1e79\u1e7c\u1e7d\u1e7e\u1e7f\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1e86\u1e87\u1e88\u1e89\u1e8a\u1e8b\u1e8c\u1e8d\u1e8e\u1e8f\u1e90\u1e91\u1e92\u1e93\u1e97\u1e98\u1e99\u1e9b\u1e9e\u1ea0\u1ea1\u1ea2\u1ea3\u1ea4\u1ea5\u1ea6\u1ea7\u1ea8\u1ea9\u1eaa\u1eab\u1eac\u1ead\u1eae\u1eaf\u1eb0\u1eb1\u1eb2\u1eb3\u1eb4\u1eb5\u1eb6\u1eb7\u1eb8\u1eb9\u1eba" +
|
||||
"\u1ebb\u1ebc\u1ebd\u1ebe\u1ebf\u1ec0\u1ec1\u1ec2\u1ec3\u1ec4\u1ec5\u1ec6\u1ec7\u1ec8\u1ec9\u1eca\u1ecb\u1ecc\u1ecd\u1ece\u1ecf\u1ed0\u1ed1\u1ed2\u1ed3\u1ed4\u1ed5\u1ed6\u1ed7\u1ed8\u1ed9\u1eda\u1edb\u1edc\u1edd\u1ede\u1edf\u1ee0\u1ee1\u1ee2\u1ee3\u1ee4\u1ee5\u1ee6\u1ee7\u1ee8\u1ee9\u1eea\u1eeb\u1eec\u1eed\u1eee\u1eef\u1ef0\u1ef1\u1ef2\u1ef3\u1ef4\u1ef5\u1ef6\u1ef7\u1ef8\u1ef9\u1f70\u1f72\u1f74\u1f76\u1f78\u1f7a\u1f7c\u1fb1\u1fb9" +
|
||||
"\u1fba\u1fc8\u1fca\u1fd1\u1fd9\u1fda\u1fe1\u1fe2\u1fe7\u1fe9\u1fea\u1ff8\u1ffa\u2071\u207f\u2090\u2091\u2092\u2093\u2094\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u20ac\u20b1\u20b4\u20b9\u20bd\u20bf\u220f\u2211\u2422\u2c63\u2c65\u2c66\u2c7b\u2c7c\ua640\ua641\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua650\ua651\ua656\ua657\ua730\ua731\ua740\ua741\ua742\ua743\ua756\ua757\ua75e\ua75f\ua776\ua792\ua793\ua798\ua799\ua7a0\ua7a1\ua7a4" +
|
||||
"\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7ae\ua7af\ua7b8\ua7b9\ua7f4\ua7f9\ua7fa\uab3f\uab65\uab69abcdefghijklmnopqrstuvwyz\u03b3\u0292\u0260\u0266\u0279\u027b\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03b2\u03b8\u03ba\u03c0\u03c1\u03c3\u03c6\u0435\u0433\u044d\u0441\u043a\u043d\u043c\u0448\u0444\u043e\u0434\u04af\u0445" +
|
||||
"\u0430\u0436\u0437\u0443\u043b\u0639\u0627\u062f\u0633\u062a\u0632\u0642\u0645\u0628\u0631\u0648\u0641\u0644\u0646\u062d\u092e\u0939\u0905\u0907\u0909\u090f\u0913\u0915\u0917\u091a\u091c\u091f\u0921\u0928\u0924\u0926\u092c\u0930\u0932\u0938\u092b\u092f\u090b\u090c\u0935\u093d\u0aae\u0ab9\u0a85\u0a87\u0a89\u0a8f\u0a93\u0a95\u0a97\u0a9a\u0a9c\u0a9f\u0aa1\u0aa8\u0aa4\u0aa6\u0aaa\u0aac\u0ab2\u0ab8\u0254\u0223\u03bb\u03c8\u0250\u1d02" +
|
||||
"\u025b\u1d08\u1d09\u1d1d\u026f\u03b4\u03c7\u0252\u0255\u025c\u0265\u0271\u0270\u0282\u0283\u028a\u028c\u03c9\u01dd\uFFFF\ua775\u028d").toCharArray();
|
||||
"\u2193\u2203\u2206\u2207\u2208\u220a\u220b\u2218\u2223\u2225\u2227\u2228\u2229\u222a\u223c\u2243\u2248\u224d\u2260\u2261\u2264\u2265\u2272\u2273\u2276\u2277\u227a\u227b\u227c\u227d\u2282\u2283\u2286\u2287\u2291\u2292\u22a3\u22a4\u22a5\u22a8\u22a9\u22ab\u22b2\u22b3\u22b4\u22b5\u22c4\u2373\u2375\u237a\u2395\u25cb\u2add\u0000 (,-.<>_\u00a0\u02d8\u00b8~\u2008\u02c7^\u00af\u0000!" +
|
||||
"+?ABDEHIKLMNORSTUVWYZ^abdehiklmnorstuvwyz\u01a0\u01a1\u01af\u01b0\u00a1\u0000OUou\u1ee2\u1ef0\u203d\u1ea0\u1e04\u1e0c\u1eb8\u1e24\u1eca\u1e32\u1e36\u1e42\u1e46\u1ecc\u1e5a\u1e62\u1e6c\u1ee4\u1e7e\u1e88\u1ef4" +
|
||||
"\u1e92\u00a6\u0000 \"',<>AEHIOUWXY_aehiotuwxy~\u00af\u00b4\u00d5\u00f5\u016a\u016b\u0399\u03a5\u03b9\u03c5\u03d2\u0406\u0410\u0415\u0416\u0417\u0418\u041e\u0423\u0427\u042b\u042d\u0430\u0435\u0436\u0437\u0438\u043e\u0443\u0447\u044b\u044d\u0456\u04d8\u04d9\u04e8\u04e9\u00a8\u0344\u201e\u201c\u201d" +
|
||||
"\u00c4\u00cb\u1e26\u00cf\u00d6\u00dc\u1e84\u1e8c\u0178\u0000Uu\u1e7a\u1e7b\u0000Oo\u1e4e\u0000Uu\u03aa\u03ab\u0407\u04d2\u0401\u04dc\u04de\u04e4\u04e6\u04f0\u04f4\u04f8\u04ec\u0451\u0457\u04da\u04ea\u0000#ESbefq\u266f\u266b\u266c\u266d\u266a\u266e\u2669\u0000o\u2030\u0000\"'()+,/<>ACEGIJ" +
|
||||
"KLMNOPRSUWYZ^_abcegijklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00d8\u00dc\u00e2\u00e5\u00e6\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u0168\u0169\u01a0\u01a1\u01af\u01b0\u0391\u0395\u0397\u0399" +
|
||||
"\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0413\u0415\u0418\u041a\u041e\u0420\u0423\u042b\u042d\u042e\u042f\u0430\u0433\u0435\u0438\u043a\u043e\u0440\u0443\u044b\u044d\u044e\u044f\u2395\u0000 IUiu\u03b9\u03c5\u0385\u1e2e\u01d7\u0390\u00b4\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0d\u1f1d\u1f2d\u1f3d\u1f4d\u1f5d\u1f6d\u1f05\u1f15" +
|
||||
"\u1f25\u1f35\u1f45\u1f55\u1f65\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0c\u1f1c\u1f2c\u1f3c\u1f4c\u1f6c\u1f04\u1f14\u1f24\u1f34\u1f44\u1f54\u1f64\u0000OUou\u1eda\u1ee8\u201a\u0000Oo\u01fe\u2018\u2019\u00c1\u0106\u00c9\u01f4\u00cd\uFFFF\u004a\u0301\u1e30\u0139\u1e3e\u0143\u00d3\u1e54\u0154\u015a\u00da\u1e82\u00dd\u0179\u0000AEOae" +
|
||||
"o\u1ea4\u1ebe\u1ed0\u0000EOeo\u1e16\u1e52\u0000Aa\u1eae\u0000OUou\u1e4c\u1e78\u0000EOeo\u0000Cc\u1e08\u01fa\u01fc\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u03ce\uFFFF\u0410\u0301\u0403\uFFFF\u0415\u0301\uFFFF\u0418\u0301\u040c\uFFFF\u041e\u0301\uFFFF\u0420\u0301\uFFFF\u0423\u0301\uFFFF\u042b\u0301\uFFFF\u042d\u0301\uFFFF\u042e\u0301\uFFFF\u042f" +
|
||||
"\u0301\uFFFF\u0440\u0301\u235e\u0000 ()-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
|
||||
"\u0391\u0395\u0397\u0399\u039f\u03a1\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c1\u03c5\u03c9\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110b\u110c\u110e\u110f\u1110\u1111\u1112\u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3\u30b5\u30b7\u30b9\u30bb\u30bd\u30bf\u30c1\u30c4\u30c6\u30c8\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d2\u30d5\u30d8\u30db\u30de\u30df\u30e0\u30e1\u30e2\u30e4\u30e6\u30e8\u30e9\u30ea\u30eb\u30ec" +
|
||||
"\u30ed\u30ef\u30f0\u30f1\u30f2\u4e00\u4e03\u4e09\u4e0a\u4e0b\u4e2d\u4e5d\u4e8c\u4e94\u4f01\u4f11\u512a\u516b\u516d\u5199\u52b4\u533b\u5341\u5354\u5370\u53f3\u540d\u56db\u571f\u591c\u5973\u5b66\u5b97\u5de6\u65e5\u6708\u6709\u6728\u682a\u6b63\u6c34\u6ce8\u706b\u7279\u7537\u76e3\u793e\u795d\u79d8\u8ca1\u8cc7\u9069\u91d1\u9805[\u0000)\uFFFF\ud83c\udd2f{\u0000)\u24ea\u0000)012345" +
|
||||
"6789\u2460\u0000)\u2469\u0000)\u246a\u0000)\u246b\u0000)\u246c\u0000)\u246d\u0000)\u246e\u0000)\u246f\u0000)\u2470\u0000)\u2471\u0000)\u2472\u0000)0123456789\u2461\u0000)\u2473\u0000)\u3251\u0000)\u3252\u0000)\u3253\u0000)\u3254\u0000)\u3255\u0000)\u3256\u0000)\u3257" +
|
||||
"\u0000)\u3258\u0000)\u3259\u0000)0123456789\u2462\u0000)\u325a\u0000)\u325b\u0000)\u325c\u0000)\u325d\u0000)\u325e\u0000)\u325f\u0000)\u32b1\u0000)\u32b2\u0000)\u32b3\u0000)\u32b4\u0000)0123456789\u2463\u0000)\u32b5\u0000)\u32b6\u0000)\u32b7\u0000" +
|
||||
")\u32b8\u0000)\u32b9\u0000)\u32ba\u0000)\u32bb\u0000)\u32bc\u0000)\u32bd\u0000)\u32be\u0000)0\u2464\u0000)\u32bf\u0000)\u2465\u0000)\u2466\u0000)\u2467\u0000)\u2468\u0000)\u24b6\u0000)\u24b7\u0000)\u24b8\u0000)\u24b9\u0000)\u24ba\u0000)\u24bb\u0000)\u24bc\u0000)\u24bd\u0000)\u24be\u0000)\u24bf\u0000)\u24c0" +
|
||||
"\u0000)\u24c1\u0000)\u24c2\u0000)\u24c3\u0000)\u24c4\u0000)\u24c5\u0000)\u24c6\u0000)\u24c7\u0000)\u24c8\u0000)\u24c9\u0000)\u24ca\u0000)\u24cb\u0000)\u24cc\u0000)\u24cd\u0000)\u24ce\u0000)\u24cf\u0000)\u24d0\u0000)\u24d1\u0000)\u24d2\u0000)\u24d3\u0000)\u24d4\u0000)\u24d5\u0000)\u24d6\u0000)\u24d7\u0000)\u24d8" +
|
||||
"\u0000)\u24d9\u0000)\u24da\u0000)\u24db\u0000)\u24dc\u0000)\u24dd\u0000)\u24de\u0000)\u24df\u0000)\u24e0\u0000)\u24e1\u0000)\u24e2\u0000)\u24e3\u0000)\u24e4\u0000)\u24e5\u0000)\u24e6\u0000)\u24e7\u0000)\u24e8\u0000)\u24e9\u1f09\u1f19\u1f29\u1f39\u1f49\u1fec\u1f59\u1f69\u1f01\u1f11\u1f21\u1f31\u1f41\u1fe5\u1f51\u1f61\u0000)\u1161\u3260\u0000" +
|
||||
")\u326e\u0000)\u1161\u3261\u0000)\u326f\u0000)\u1161\u3262\u0000)\u3270\u0000)\u1161\u3263\u0000)\u3271\u0000)\u1161\u3264\u0000)\u3272\u0000)\u1161\u3265\u0000)\u3273\u0000)\u1161\u3266\u0000)\u3274\u0000)\u1161\u3267\u0000)\u3275\u0000)\u1161\u3268\u0000)\u3276\u0000)\u1161\u3269\u0000)\u3277\u0000)\u1161\u326a\u0000)\u3278" +
|
||||
"\u0000)\u1161\u326b\u0000)\u3279\u0000)\u1161\u326c\u0000)\u327a\u0000)\u1161\u326d\u0000)\u327b\u0000)\u32d0\u0000)\u32d1\u0000)\u32d2\u0000)\u32d3\u0000)\u32d4\u0000)\u32d5\u0000)\u32d6\u0000)\u32d7\u0000)\u32d8\u0000)\u32d9\u0000)\u32da\u0000)\u32db\u0000)\u32dc\u0000)\u32dd\u0000)\u32de\u0000)\u32df\u0000)\u32e0" +
|
||||
"\u0000)\u32e1\u0000)\u32e2\u0000)\u32e3\u0000)\u32e4\u0000)\u32e5\u0000)\u32e6\u0000)\u32e7\u0000)\u32e8\u0000)\u32e9\u0000)\u32ea\u0000)\u32eb\u0000)\u32ec\u0000)\u32ed\u0000)\u32ee\u0000)\u32ef\u0000)\u32f0\u0000)\u32f1\u0000)\u32f2\u0000)\u32f3\u0000)\u32f4\u0000)\u32f5\u0000)\u32f6\u0000)\u32f7\u0000)\u32f8" +
|
||||
"\u0000)\u32f9\u0000)\u32fa\u0000)\u32fb\u0000)\u32fc\u0000)\u32fd\u0000)\u32fe\u0000)\u3280\u0000)\u3286\u0000)\u3282\u0000)\u32a4\u0000)\u32a6\u0000)\u32a5\u0000)\u3288\u0000)\u3281\u0000)\u3284\u0000)\u32ad\u0000)\u32a1\u0000)\u329d\u0000)\u3287\u0000)\u3285\u0000)\u32a2\u0000)\u3298\u0000)\u32a9\u0000)\u3289" +
|
||||
"\u0000)\u32af\u0000)\u329e\u0000)\u32a8\u0000)\u3294\u0000)\u3283\u0000)\u328f\u0000)\u32b0\u0000)\u329b\u0000)\u32ab\u0000)\u32aa\u0000)\u32a7\u0000)\u3290\u0000)\u328a\u0000)\u3292\u0000)\u328d\u0000)\u3291\u0000)\u32a3\u0000)\u328c\u0000)\u329f\u0000)\u328b\u0000)\u3295\u0000)\u329a\u0000)\u32ac\u0000)\u3293" +
|
||||
"\u0000)\u3297\u0000)\u3299\u0000)\u3296\u0000)\u32ae\u0000)\u329c\u0000)\u328e\u0000)\u32a0\u0000)-\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c1\u03c5\u03c9]}\u1f08\u1f18\u1f28\u1f38\u1f48\u1f68\u1f00\u1f10\u1f20\u1f30\u1f40\u1fe4\u1f50\u1f60\u0000'0AUau\u00a8\u25cb\u0000Aa\u00c5\u016e\u235f\u0000+-" +
|
||||
"OUou#\u00b1\u01a0\u01af\u0000 \"',-ACDEGHIKLNORSTUacdeghiklnorstu\u0413\u041a\u041b\u041d\u0425\u0433\u043a\u043b\u043d\u0445\u0466\u0467\u00ac\u0104\u00c7\u1e10\u0118\u0122\u1e28\u012e\u0136\u013b\u0145\u01ea\u0156\u015e\u0162\u0172" +
|
||||
"\u04fa\u04c3\u0512\u04c7\u04fc\u04fb\u04c4\u0513\u04c8\u04fd\u04c9\u04ca\u0000 ()+,-./:>ADEIKLOU\\^_adeilou\u2191\u2193\u25cb\u0000 -.\u00ad\u2014\u2013\u0000Ee\uFFFF\u0116\u0304\uFFFF\u0117\u0304\u233f\u00f7\u0100\u0110\u0112\u012a\u20ad\u00a3\u014c\u016a\u2340\u2212" +
|
||||
"\u234f\u2356\u2296\u0000 !'-.:<=>ABCDEFGHIMNOPRSTWXYZ^abcdefghimnoprstwxyz\u00b4\u015a\u015b\u0160\u0161\u017f\u0406\u0413\u0416\u0419\u041a\u041b\u041c\u041d\u0425\u0427\u0433\u0436" +
|
||||
"\u0439\u043a\u043b\u043c\u043d\u0445\u0447\u0456\u1e62\u1e63\u25cb\u02d9\u0000Ss\u1e68\u0000Ss\u1e64\u00b7\u2026\u2235\u2039\u2022\u203a\u0226\u1e02\u010a\u1e0a\u0116\u1e1e\u0120\u1e22\u0130\u1e40\u1e44\u022e\u1e56\u1e58\u1e60\u1e6a\u1e86\u1e8a\u1e8e\u017b\u0000Ss\u1e66\u04f6\u0496\u048a\u049a\u052e\u04cd\u04b2\u04b6\u04f7\u0497\u048b\u049b\u052f\u04ce\u04b3\u04b7\u2299\u0000-/<=" +
|
||||
"BCDGHILOTZ^bcdghilmotuvz\u0294\u0413\u041a\u0433\u043a\u04ae\u04af\u2190\u2192\u2194\u2395\\\u2260\u0243\u20a1\u01e4\u0126\u0197\u0141\u00d8\u0166\u01b5|\u00a2\u20a5\u00b5\u221a\u02a1\u0492\u049e\u0493\u049f\u04b0\u219a\u219b\u21ae\u2341\u0000*3~\u2189\u236c\u00001234" +
|
||||
"56789^\u00000\u2152\u00bd\u2153\u00bc\u2155\u2159\u2150\u215b\u2151\u000035^\u2154\u2156\u0000458^\u00be\u2157\u215c\u00005\u2158\u000068\u215a\u215d\u00008\u215e\u00008\u221e\u0000()-.\u2395\u2639\u263a\u2234\u2360\u0000 AEIOSTU_aeiostu" +
|
||||
"\u02db\u0218\u021a\u236e\u0219\u021b\u0000 \"'-/3<=>CDELNRSTZ_cdelnrstz\u2395\u226e\u2665\u00ab\u2264\u22c4\u010c\u010e\u011a\u013d\u0147\u0158\u0160\u0164\u017d\u2343\u0000/<>CELNOPRTUWY^_cdeo" +
|
||||
"pruvy\u0415\u0417\u0421\u0423\u0437\u0443\u2395\u21d0\u21d2\u20ac\u20a4\u20a6\u0150\u20bd\u20b9\u20ae\u0170\u20a9\u00a5\u21d1\u2261\u20ab\u21d3\u04f2\u2338\u0000 \"'/<=>AEIOU_aeiou\u00a8\u2395\u226f\u2265\u00bb\u00c2\u00ca\u00ce\u00d4\u00db\u2369\u2344\u0000!+?AEIOUY\\" +
|
||||
"^abeiouy\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u0102\u0103\u01a0\u01a1\u01af\u01b0\u2395\u2e18\u0000OUou\u1ede\u1eec\u00bf\u1ea2\u1eba\u1ec8\u1ece\u1ee6\u1ef6\u262d\u0000AEOaeo\u1ea8\u1ec2\u1ed4\u0000Aa\u1eb2\u2370\u0000\"'(*,-;>AET^_`aet~\u00a8" +
|
||||
"\u00b4\u0102\u00c6@\u00c0\u00c3\u0000.|\u0e3f\u0000',./<=CEOr|\u2102\u20a0\u00a9\u20a2\u20b5\u0000,-.<Hh\u00d0\u0000\"',-.;<=>E^_`e~\u00a8\u00b4\u018f\u00c8\u1ebc\u0000.Uilr\uFFFF\ud83d\udd95\uFFFF\ufb03\uFFFF\ufb04\u20a3\u0000(" +
|
||||
",.TUnu|\u02d8\u011e>\u0000~\uFFFF\u004e\u0360\u0067\u20b2\u0000,\u0000\"',-.;>J^_`j~\u00a8\u00b4\u0132\u00cc\u0128\u0000'\u00b4\u0000,-\u0000',-/<=LTV\u0000A\u0000P\uFFFF\ud83d\udd96<\u0000.\u0000',<=GNO" +
|
||||
"gno~\u014a\u2115\u2116\u00d1\u0000\"',-/;>ACERSXY^_`e~\u00a8\u00b4\u0152\u00ae\u00a7\u00a4\u262e\u00d2\u00d5\u0000!.=Pt\u00b6\u20a7\u0000Qq\u211a\u0000',<=ORrs\u211d\u20a8\u0000!',.;<MOSs|" +
|
||||
"\u2120\u1e9e$\u0000,-./;<=HMhz\u00de\u2122\uFFFF\ua728\u0000 !\"'*,-;>AEGIOU^_`aegiou~\u00a8\u00b4\u00b8\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0416\u0418\u0423\u0430\u0435\u0436\u0438\u0443\u0000,\u0000Ee\u1e1c\u0000A" +
|
||||
"a\u1eb6\u0114\u012c\u014e\u016c\u00d9\u0103\u0115\u011f\u012d\u014f\u016d\u0168\u0000Ee\u1fb8\u1fd8\u1fe8\u1fb0\u1fd0\u1fe0\u04d0\u04d6\u04c1\u0419\u040e\u04d1\u04d7\u04c2\u0439\u045e\u0000Lcsz~\u1e7c\u0000=^\u0174\u0000O\u0000\"'=^~\u00a8\u00b4\u0176\u1ef8\u0000'.<Zz\u2124\u0000]\u2337\u0000-?not" +
|
||||
"\u2395\u25cb\uFFFF\\\u006e\u0000/\uFFFF\ud83d\ude4c\uFFFF\\\u0074\u2342\u2349\u0000[\u0000!()+-./0123456789=ACEGHIJOSUWYZ_aceghijosuwyz|\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435" +
|
||||
"\u0438\u043e\u0440\u0443\u1eb8\u1eb9\u1ecc\u1ecd\u2212\u4e00\u4e01\u4e09\u4e0a\u4e0b\u4e19\u4e2d\u4e59\u4e8c\u4eba\u56db\u5730\u5929\u7532\u0000AEOaeo\u1eac\u1ec6\u1ed8\u0108\u011c\u0124\u0134\u015c\u1e90\u0000ahijlnorswxy\u0263\u0266\u0279\u027b\u0281\u0295\uFFFF\u0410\u0302\uFFFF\u0415\u0302\uFFFF\u0418\u0302\uFFFF\u041e\u0302\uFFFF\u0420" +
|
||||
"\u0302\uFFFF\u0423\u0302\uFFFF\u0440\u0302\u3192\u319c\u3194\u3196\u3198\u319b\u3197\u319a\u3193\u319f\u3195\u319e\u319d\u3199\u0000 !\"'()+-.0123456789;<=>AEGIOUY^_aegiouy~\u00c4\u00c6\u00d5\u00d6\u00dc\u00e4\u00e6\u00f5\u00f6\u00fc" +
|
||||
"\u0226\u0227\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u1e36\u1e37\u1e5a\u1e5b\u2206\u220a\u2212\u2218\u2260\u2282\u2283\u22a5\u22c4\u2373\u2375\u237a\u25cb\u0000LRlr\u1e38\u1e5c\u0000AOUaou\u01de\u022a\u01d5\u2358\u0000AEOaeo\u01e0\u0230\u0000Oo\u01ec\u1e20\u0232\u0000O" +
|
||||
"o\u022c\u01e2\u1fb9\u1fd9\u1fe9\uFFFF\u0410\u0304\uFFFF\u0415\u0304\u04e2\uFFFF\u041e\u0304\uFFFF\u0420\u0304\u04ee\uFFFF\u0430\u0304\uFFFF\u0435\u0304\uFFFF\u043e\u0304\uFFFF\u0440\u0304\u2359\u2377\u235b\u2262\u2286\u2287\u234a\u235a\u2378\u2379\u2376\u235c\u0000\"()+AEINOUWY^_`abeinouwy\u00af\u00c2\u00ca" +
|
||||
"\u00d4\u00dc\u00e2\u00ea\u00f4\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u01a0\u01a1\u01af\u01b0\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u0000Uu\u03b9\u03c5\u01db\u1fd2\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0b\u1f1b\u1f2b\u1f3b\u1f4b\u1f5b\u1f6b\u1f03" +
|
||||
"\u1f13\u1f23\u1f33\u1f43\u1f53\u1f63\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u1f0a\u1f1a\u1f2a\u1f3a\u1f4a\u1f6a\u1f02\u1f12\u1f22\u1f32\u1f42\u1f52\u1f62\u0000OUou\u1edc\u1eea\u01f8\u1e80\u1ef2\u0000AEOaeo\u1ea6\u1ec0\u1ed2\u0000EOeo\u1e14\u1e50\u0000\u0410\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440" +
|
||||
"\u0443\uFFFF\u0410\u030f\uFFFF\u0415\u030f\uFFFF\u0418\u030f\uFFFF\u041e\u030f\uFFFF\u0420\u030f\uFFFF\u0423\u030f\u0000Aa\u1eb0\u0000EOeo\u1fba\u1fc8\u1fca\u1fda\u1ff8\u1fea\u1ffa\uFFFF\u0410\u0300\u0400\u040d\uFFFF\u041e\u0300\uFFFF\u0420\u0300\uFFFF\u0423\u0300\uFFFF\u0430\u0300\uFFFF\u043e\u0300\uFFFF\u0440\u0300\uFFFF\u0443\u0300\u0000\"'(*,-;>E^" +
|
||||
"_`ae~\u00a8\u00b4\u00e6\u0000!,.AEGIOUaegiou\u00b8\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410\u0415\u0416\u0418\u0423\u0430\u0435\u0436\u0438\u0443\u0000Aa\u0000Ee\u0000Ee\u0000\"',./<=ACDEGHIKLNORST" +
|
||||
"UZacdeghijklnorstuz|\u00dc\u00fc\u0000Uu\u01d9\u01cd\u01e6\u021e\u01cf\u01e8\u01d1\u01d3\u0000,-.<=hi\u00f0\u2300\u0000\"',-.;<=>^_`e~\u00a8\u00b4\u0259\u0000.Sfils\u017f\uFFFF\ufb00\uFFFF" +
|
||||
"\ufb01\uFFFF\ufb02\u0000(,.Untu~\u02d8\u0000~\uFFFF\u006e\u0360\u0067\uFFFF\u0067\u0303\u0000,\u0000\"',-.;>^_`j~\u00a8\u00b4\u0133\u0000'\u00b4\u0000,k\u0138\u0000',-/<tv\u0000./u\u0000',<g~\u014b\u0000\"',-/" +
|
||||
";>AU^_`aceorsuwxy~\u00a8\u00b4\u0153\u0000!.=o\u0000o\uFFFF\ud83d\udca9\u0000',<=\u0000!',.;<mos\u00b8\u00df\u0000,-./;<hmz\u00fe\uFFFF\ua729\u0000\"'*,-/;>AE" +
|
||||
"GIOU^_`aegiou~\u00a8\u00b4\u0000/ACDEGHIKNORSTUZacdeghijklnorstuz|~\u0000^\u0000ox\u00d7\u0000\"'=^~\u00a8\u00b4\u0000'.<\u0000}" +
|
||||
"\u2205\u0000-=BCGS^cv~\u2190\u2192\u2206\u2207\u222a\u2282\u25cb\u2020\u2021\u236d\u2345\u2346\u234b\u2352\u2366\u2367\u233d\u0000\"()+0AEINOUVY^abeinouvy|~\u00a8\u00c2\u00ca\u00d4\u00e2\u00ea\u00f4\u0102\u0103\u01a0\u01a1\u01af\u01b0\u03b1\u03b7\u03b9\u03c5" +
|
||||
"\u03c9\u2207\u2227\u2228\u0000\u03b9\u03c5\u1fd7\u0000\u0391\u0397\u0399\u03a5\u03a9\u03b1\u03b7\u03b9\u03c5\u03c9\u1f0f\u1f2f\u1f3f\u1f5f\u1f6f\u1f07\u1f27\u1f37\u1f57\u1f67\u0000\u0391\u0397\u0399\u03a9\u03b1\u03b7\u03b9\u03c5\u03c9\u1f0e\u1f2e\u1f3e\u1f6e\u1f06\u1f26\u1f36\u1f56\u1f66\u0000OUou\u1ee0\u1eee\u0000AEOaeo\u1eaa\u1ec4\u1ed6\u0000Aa\u1eb4\u2248\u1fb6\u1fc6" +
|
||||
"\u1fd6\u1fe6\u1ff6\u236b\u2372\u2371\u0000'*>AEIOUY`aeiouy~\u00b4\u2207\u2218\u22a4\u25cb\u1fed\u1fc1\u2361\u2365\u0000!\".;AEGIOUYaegiouy~\u00c4\u00c6\u00d5\u00d6\u00dc\u00e4\u00e6\u00f5\u00f6\u00fc\u0226\u0227\u0391\u0399\u03a5\u03b1\u03b9\u03c5\u0410" +
|
||||
"\u0415\u0418\u041e\u0420\u0423\u0430\u0435\u0438\u043e\u0440\u0443\u1e36\u1e37\u1e5a\u1e5b\u22a4\u0000LRlr\u0000AOUaou\u0000AOao\u0000Oo\u0000Oo\u2351\u0000\"()+,/ACEGIJKLMNOPRSUWYZ^_abceg" +
|
||||
"ijklmnoprsuwyz~\u00af\u00b8\u00c2\u00c5\u00c6\u00c7\u00ca\u00cf\u00d4\u00d5\u00d8\u00dc\u00e2\u00e5\u00e6\u00e7\u00ea\u00ef\u00f4\u00f5\u00f8\u00fc\u0102\u0103\u0112\u0113\u014c\u014d\u0168\u0169\u01a0\u01a1\u01af\u01b0\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0410\u0413\u0415\u0418\u041a\u041e\u0420\u0423\u042b" +
|
||||
"\u042d\u042e\u042f\u0430\u0433\u0435\u0438\u043a\u043e\u0440\u0443\u044b\u044d\u044e\u044f\u0000IUiu\u03b9\u03c5\u0000\u0391\u0395\u0397\u0399\u039f\u03a5\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0000\u0391\u0395\u0397\u0399\u039f\u03a9\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03c9\u0000OUou\u0000Cc\u0000Oo\u0000AEOaeo\u0000EO" +
|
||||
"eo\u0000Aa\u0000OUou\u0000EOeo\u0000Cc\u0000CDEGHKLNRSTcdeghklnrst\u0228\u0000\u2395\u2339\u0000Gg\u0000'\u0000'\u0000'\u0000\"'\u0000'\u0000\"'\u0000'\u0000'\u0000'\u0000'\u0000\"" +
|
||||
"'()`~\u00b4\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8d\u1f9d\u1fad\u1f85\u1f95\u1fa5\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8c\u1f9c\u1fac\u1f84\u1f94\u1fa4\u1fb4\u1fc4\u1ff4\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f89\u1f99\u1fa9\u1f81\u1f91\u1fa1\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f88\u1f98\u1fa8\u1f80\u1f90" +
|
||||
"\u1fa0\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8b\u1f9b\u1fab\u1f83\u1f93\u1fa3\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8a\u1f9a\u1faa\u1f82\u1f92\u1fa2\u1fb2\u1fc2\u1ff2\u0000()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8f\u1f9f\u1faf\u1f87\u1f97\u1fa7\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1f8e\u1f9e\u1fae\u1f86\u1f96\u1fa6\u1fb7\u1fc7\u1ff7\u0000" +
|
||||
"()\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u0000\u0391\u0397\u03a9\u03b1\u03b7\u03c9\u1fbc\u1fcc\u1ffc\u1fb3\u1fc3\u1ff3\u0000'\u0000\"'\u0000'\u0000\".\u0406\u0423\u0443\u0456\u0474\u0000\u0410\u0430\u0462\u0463\uFFFF\ua656\uFFFF\ua65c\u0000\u0415\u0423\u042b\u042c\u042d\u0435\u0443\u044b\u044c\u044d\ua64a\ua64b\ua650\ua651\u04d4\u0460\u0462\uFFFF\ua658\u04d8\uFFFF\ua64c" +
|
||||
"\u0000,.\u0408\u0419\u041a\u0425\u0439\u043a\u0445\u0458\u0490\u0480\u0000\u0408\u0415\u0416\u0417\u0419\u0435\u0436\u0437\u0439\u0458\u0464\u0465\uFFFF\ua648\u040f\uFFFF\ua642\u0402\u0000=\u0000.\u0000=\u0424\u0444\u0498\u0000\u0418\u0423\u0438\u0443\u0000.\u0000,.\u0413\u0421\u0425\u0428\u0433\u0441\u0445\u0448\u046e\u0000,.\u042c\u044c\u0409\u0000.\u0000,\u00b7\u041e" +
|
||||
"\u042c\u043e\u044c\u0528\u040a\u0000\u0422\u0423\u0442\u0443\u047e\u0478\u0000\u0421\u0441\u0470\u0000=\u0000\u0408\u0419\u0421\u0424\u0439\u0441\u0444\u0458\u040b\u0426\u0472\u0000\u0406\u0408\u0418\u0419\u0423\u0438\u0439\u0443\u0456\u0458\u0000,.\u0000.\u0000\u0422\u0442\u0429\u0000\u0406\u0410\u0415\u041c\u041d\u041e\u042c\u042d\u0430\u0435\u043c\u043d\u043e\u044c\u044d\u0456\u0464\u046c\u0468\uFFFF\ua652" +
|
||||
"\u0000\u0435\u0443\u0447\u044b\u044c\u044d\ua64b\ua651\u04d5\u0461\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0069\u0067\u0075\u0463\uFFFF\ua659\u04d9\uFFFF\ua64d\u0000\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u006c\u0061\u0076\u006f\u006e\u0069\u0063\u005f\u0070\u0073\u0069\u006c\u0069\u0000,.\u0439\u043a\u0445\u0458\u0491\u0481\u0000\u0435\u0436\u0437" +
|
||||
"\u0439\u0458\u0465\uFFFF\ua649\u045f\uFFFF\ua643\u0452\u0000\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0074\u0072\u0065\u006d\u0061\u0000.\u0000=\u0444\u0499\u0000\u0438\u0443\u0475\u0000.\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0062\u0072\u0065\u0076\u0065\u0000,.\u0433\u0441\u0445\u0448\u046f\u0000,.\u044c\u0459\u0000.\u0000" +
|
||||
",\u00b7\u0447\u044c\u0529\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0074\u0069\u0074\u006c\u006f\u045a\u0000\u0442\u0443\u0447\u047f\u0479\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0069\u006e\u0076\u0065\u0072\u0074\u0065\u0064\u005f\u0062\u0072\u0065\u0076\u0065\u0000\u0430\u0441\u0471\u0000\u0439\u0441\u0444\u0458\u045b\u0446\u0473\u0000\u0438\u0439\u0443\u0447\u0456\u0458" +
|
||||
"\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0070\u006f\u006b\u0072\u0079\u0074\u0069\u0065\u0000,.\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u006c\u0061\u0076\u006f\u006e\u0069\u0063\u005f\u0064\u0061\u0073\u0069\u0061\u0000.\u0430\u0431\u0435\u0439\u043d\u043e\u0443\u0445\u0447\u044a\u044e\u0458\u0467\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e" +
|
||||
"\u0067\u005f\u0070\u0061\u0079\u0065\u0072\u006f\u006b\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0076\u0065\u0072\u0074\u0069\u0063\u0061\u006c\u005f\u0074\u0069\u006c\u0064\u0065\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0067\u0072\u0061\u0076\u0065\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0076\u007a\u006d\u0065\u0074\u0000\u0442\u0449\u0000\u0447\u0000" +
|
||||
"\u0430\u0435\u043c\u043d\u043e\u0447\u044c\u044d\u0456\uFFFF\ua657\u0465\u046d\u0469\uFFFF\ua65d\uFFFF\ua653\u0000\".\u0443\u0456\u0000\u0430\u0447\u0463\u0000\u0422\u0442\u0000\u0442\u0000\u0447\u0000\u05d9\uFFFF\ufb1d\u0000\u05d0\u05f2\uFFFF\ufb2e\uFFFF\ufb1f\u0000\u05d0\uFFFF\ufb2f\u0000\u05d5\uFFFF\ufb4b\u0000\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d8\u05d9\u05da\u05db\u05dc\u05de\u05e0\u05e1\u05e3\u05e4\u05e6" +
|
||||
"\u05e7\u05e8\u05e9\u05ea\uFFFF\ufb30\uFFFF\ufb31\uFFFF\ufb32\uFFFF\ufb33\uFFFF\ufb34\uFFFF\ufb35\uFFFF\ufb36\uFFFF\ufb38\uFFFF\ufb39\uFFFF\ufb3a\uFFFF\ufb3b\uFFFF\ufb3c\uFFFF\ufb3e\uFFFF\ufb40\uFFFF\ufb41\uFFFF\ufb43\uFFFF\ufb44\uFFFF\ufb46\uFFFF\ufb47\uFFFF\ufb48\uFFFF\ufb49\uFFFF\ufb4a\u0000\u05d1\u05db\u05e4\uFFFF\ufb4c\uFFFF\ufb4d\uFFFF\ufb4e\u0000\u05bc\u05e9\ufb49\u0000\u05e9\uFFFF\ufb2c\uFFFF\ufb2a\u0000\u05bc\u05e9\ufb49" +
|
||||
"\u0000\u05e9\uFFFF\ufb2d\uFFFF\ufb2b\u0000\u0627\u0639\u0648\u064a\u0667\u06cc\u06f7\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u006c\u0065\u0066\u005f\u0061\u0062\u006f\u0076\u0065\u0623\u06c9\u063d\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u006c\u0065\u0066\u005f\u0062\u0065\u006c\u006f\u0077\u0000\u062f\u0631\u0634\u0646\u0637\u0691\u062b\u0679\u0000\u0634\u0686\u0000" +
|
||||
"\u062d\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u0075\u006b\u0075\u006e\u0000\u062a\u0632\u06f7\u0630\u0695\u0000\u0634\u0635\u0000\u062a\u0000\u0627\u0647\u0648\u064a\u0667\u0668\u06cc\u06f7\u06f8\u0625\u06c0\u0624\u0626\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0068\u0061\u006d\u007a\u0061\u005f\u0062\u0065\u006c\u006f\u0077\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069" +
|
||||
"\u006e\u0067\u005f\u0068\u0061\u006d\u007a\u0061\u005f\u0061\u0062\u006f\u0076\u0065\u0000\u0643\u06a9\u06af\u0000\u0648\u06a1\u0000\u0648\u06ca\u0000\u0644\u0667\u06f7\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0073\u0068\u0061\u0064\u0064\u0061\u0068\u06b5\u0000\u062a\u0647\u0648\u064a\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0066\u0061\u0074\u0068\u0061\u0074\u0061\u006e" +
|
||||
"\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0064\u0061\u006d\u006d\u0061\u0074\u0061\u006e\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u006b\u0061\u0073\u0072\u0061\u0074\u0061\u006e\u0000 \u062a\u0639\u0646\u0647\u0648\u064a\u06cc\u06d5\u0629\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0066\u0061\u0074\u0068\u0061\u06c6\u06ce\u0000\u062b\u0639\u0641\u0646" +
|
||||
"\u0648\u0667\u0668\u06f7\u06f8\u06cb\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0064\u0061\u006d\u006d\u0061\u0068\u0000 \u0627\u0639\u0646\u064a\u0667\u0668\u06f7\u06f8\u06d2\u0649\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u006b\u0061\u0073\u0072\u0061\u0000\u0627\u0622\u0000\u0627\u0648\u064a\u06c1\u06d2\u06d5\u06c2\u06d3\u0000\u0627\u0000\u0627\u0631\u0639\u0644\u0648\u064a" +
|
||||
"\u0667\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0072\u0061\u0062\u0069\u0063\u005f\u0076\u0000\u0639\u0648\u064a\u0668\u06cc\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0061\u0072\u0061\u0062\u0069\u0063\u005f\u0069\u006e\u0076\u0065\u0072\u0074\u0065\u0064\u005f\u0076\u0000 \u0627\u0639\u0646\u0667\u0668\u06cc\u06f7\u06f8\u0000\u0627\u0631\u0639\u0644\u0648\u064a" +
|
||||
"\u06cc\u06f7\u0000\u0639\u0648\u064a\u06cc\u06f8\u0000\u0915\u0916\u0917\u091c\u0921\u0922\u0928\u092b\u092f\u0930\u0933\u0958\u0959\u095a\u095b\u095c\u095d\u0929\u095e\u095f\u0931\u0934\u0000\u09a1\u09a2\u09af\u09dc\u09dd\u09df\u0000\u09be\u09d7\u09cb\u09cc\u0000\u0a16\u0a17\u0a1c\u0a2b\u0a32\u0a38\u0a59\u0a5a\u0a5b\u0a5e\u0a33\u0a36\u0000\u0b21\u0b22\u0b5c\u0b5d\u0000\u0b3e\u0b56\u0b57\u0b4b\u0b48\u0b4c\u0000\u0bbe\u0bd7\u0bca" +
|
||||
"\u0bcc\u0000\u0bbe\u0bcb\u0000\u0b92\u0b94\u0000\u0c56\u0c48\u0000\u0cd5\u0cc0\u0000\u0cc2\u0cd5\u0cd6\u0cca\u0cc7\u0cc8\u0000\u0cd5\u0ccb\u0000\u0d3e\u0d57\u0d4a\u0d4c\u0000\u0d3e\u0d4b\u0000\u0dca\u0dcf\u0ddf\u0dda\u0ddc\u0dde\u0000\u0dca\u0ddd\u0000\u0f72\u0f74\u0f80\u0f73\u0f75\u0f81\u0000\u0fb5\u0fb9\u0000\u0fb7\u0f93\u0000\u0fb7\u0f9d\u0000\u0fb7\u0fa2\u0000\u0fb7\u0fa7\u0000\u0fb7\u0fac\u0000\u0f80\u0f76\u0000\u0f80\u0f78" +
|
||||
"\u0000\u0f40\u0f69\u0000\u0f42\u0f4c\u0f51\u0f56\u0f5b\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0000\u1025\u1026\u0000\u1100\u1101\u0000\u1100\u1102\u1103\u1107\u1113\u1114\u1115\u1116\u0000\u1100\u1103\u1117\u1104\u0000\u1102\u1105\u110b\u1112\u1118\u1119\u111b\u111a\u0000\u1107\u110b\u111c\u111d\u0000\u1100\u1102\u1103\u1107\u1109\u110a\u110b\u110c\u110e\u1110\u1111\u112b\u112d\u112f\u1132\u1136\u111e\u111f\u1120\u1108\u1121\u1125\u112b" +
|
||||
"\u1127\u1128\u1129\u112a\u112c\u1122\u1123\u1124\u1126\u0000\u110b\u0000\u1100\u1102\u1103\u1105\u1106\u1107\u1109\u110a\u110b\u110c\u110e\u110f\u1110\u1111\u1112\u111e\u112d\u112e\u112f\u1130\u1131\u1132\u110a\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u1133\u0000\u1109\u0000\u1100\u1103\u1106\u1107\u1109\u110b\u110c\u110e\u1110\u1111\u1140\u1141\u1142\u1143\u1144\u1145\u1147\u1148\u1149\u114a\u114b\u1146\u0000\u110b\u110c" +
|
||||
"\u114d\u110d\u0000\u110f\u1112\u1152\u1153\u0000\u1107\u110b\u1156\u1157\u0000\u1112\u1158\u0000\u1100\u1103\u1107\u1109\u110c\u0000\u1100\u0000\u113c\u113d\u0000\u113e\u113f\u0000\u114e\u114f\u0000\u1150\u1151\u0000\u1169\u116e\u1175\u1176\u1177\u1162\u0000\u1169\u116d\u1175\u1178\u1179\u1164\u0000\u1169\u116e\u1173\u1175\u117a\u117b\u117c\u1166\u0000\u1169\u116e\u1175\u117d\u117e\u1168\u0000\u1161\u1162\u1165\u1166\u1168\u1169" +
|
||||
"\u116e\u1175\u116a\u116b\u117f\u1180\u1181\u1182\u1183\u116c\u0000\u1175\u0000\u1163\u1164\u1167\u1169\u1175\u1184\u1185\u1186\u1187\u1188\u0000\u1161\u1162\u1165\u1166\u1168\u116e\u1175\u117c\u1189\u118a\u116f\u1170\u118c\u118d\u1171\u118b\u0000\u1173\u1175\u0000\u1161\u1165\u1166\u1167\u1168\u116e\u1175\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u0000\u116e\u1173\u1175\u1195\u1196\u1174\u0000\u116e\u1197\u0000\u1161\u1163\u1169" +
|
||||
"\u116e\u1173\u119e\u1198\u1199\u119a\u119b\u119c\u119d\u0000\u1165\u116e\u1175\u119e\u119f\u11a0\u11a1\u11a2\u0000\u11a8\u11af\u11ba\u11e7\u11a9\u11c3\u11aa\u11c4\u0000\u11a8\u0000\u11a8\u11ae\u11ba\u11bd\u11c0\u11c2\u11eb\u11c5\u11c6\u11c7\u11ac\u11c9\u11ad\u11c8\u0000\u11a8\u11af\u11ca\u11cb\u0000\u11a8\u11aa\u11ab\u11ae\u11af\u11b7\u11b8\u11b9\u11ba\u11bb\u11bf\u11c0\u11c1\u11c2\u11da\u11dd\u11e5\u11e6\u11eb\u11f9\u11b0\u11cc" +
|
||||
"\u11cd\u11ce\u11d0\u11b1\u11b2\u11d3\u11b3\u11d6\u11d8\u11b4\u11b5\u11b6\u11d1\u11d2\u11d4\u11d5\u11d7\u11d9\u0000\u11ba\u0000\u11a8\u11ba\u0000\u11ba\u11bc\u11c2\u0000\u11ba\u0000\u11a8\u11af\u11b8\u11ba\u11bb\u11bc\u11be\u11c2\u11eb\u11da\u11db\u11dc\u11dd\u11de\u11e2\u11e0\u11e1\u11df\u0000\u11af\u11ba\u11bc\u11c1\u11c2\u11e3\u11b9\u11e6\u11e4\u11e5\u0000\u11a8\u11ae\u11af\u11b8\u11ba\u11e7\u11e8\u11e9\u11ea\u11bb\u0000\u11a8" +
|
||||
"\u11a9\u11bc\u11bf\u11ec\u11ed\u11ee\u11ef\u0000\u11b8\u11bc\u11f3\u11f4\u0000\u11ab\u11af\u11b7\u11b8\u11f5\u11f6\u11f7\u11f8\u0000\u11c2\u11cf\u0000\u11ba\u0000\u11a8\u0000\u11ba\u11eb\u11f1\u11f2\u0000|\u2395\u2347\u0000-\u2395\u2350\u0000|\u2395\u2348\u0000-\u2395\u2357\u0000/\u2204\u0000_|\u2395\u234d\u0000|~\u00a8\u2395\u2354\u0000/\u2209\u0000_\u0000/\u220c\u0000" +
|
||||
"_\u00a8\u2229\u22a4\u22a5\u2395\u25cb\u235d\u2355\u234e\u233b\u233e\u0000/\u2224\u0000/\u2226\u0000~\u2228\u2395\u2353\u0000~\u2227\u2395\u234c\u0000\u2218\u0000|\u0000/\u2241\u0000/\u2244\u0000/\u2249\u0000/\u226d\u0000_\u2395\u236f\u0000/\u0000/\u2270\u0000/\u2271\u0000/\u2274\u0000/\u2275\u0000/\u2278\u0000/\u2279\u0000/\u2280\u0000" +
|
||||
"/\u2281\u0000/\u22e0\u0000/\u22e1\u0000/_|\u2284\u0000/_\u2285\u0000/\u2288\u0000/\u2289\u0000/\u22e2\u0000/\u22e3\u0000/\u22ac\u0000\u00a8\u00af\u2218\u22a5\u2336\u0000_\u2218\u22a4\u0000/\u22ad\u0000/\u22ae\u0000/\u22af\u0000/\u22ea\u0000/\u22eb\u0000/\u22ec\u0000/\u22ed\u0000_\u2395\u233a\u0000_\u0000_\u0000" +
|
||||
"_\u0000'/:<=>?\\\u00f7\u2190\u2191\u2192\u2193\u2206\u2207\u2218\u2227\u2228\u2260\u22c4\u25cb\u233c\u0000*-.\\_|\u00a8\u2218\u2395\u0000/\u2adc\u0000 !\"%'()*,-.0123456789:<=>?[]^_abce" +
|
||||
"hlopruyz{|}\u00a3\u00a7\u00b1\u00d7\u0398\u03a0\u03a3\u03b2\u03b3\u03b5\u03b8\u03ba\u03c0\u03c1\u03c3\u03c5\u03c6\u0430\u0433\u0434\u0435\u0437\u0438\u0439\u043b\u043c\u043d\u043e\u0441\u0443\u0444\u0445\u0447\u0448\u044a\u044b\u044c\u044d\u044f\u0452\u0456\u0458\u045b\u045f\u0461\u0481\u0487\u049b\u04b7\u04c8\u0513\u05d1\u05d3\u05d5\u05d6\u05d7\u05dd\u05e1\u05e3\u05e4\u05e6" +
|
||||
"\u05e7\u05e8\u05e9\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062d\u062f\u0631\u0632\u0633\u0634\u0635\u0637\u0639\u063a\u063d\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u064a\u064f\u0650\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u0698\u06a1\u06a9\u06be\u06c6\u06c9\u06cc\u06ce\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0901\u0902\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090f\u0910" +
|
||||
"\u0913\u0914\u0915\u0916\u0917\u0918\u091a\u091b\u091c\u091d\u091f\u0921\u0922\u0923\u0925\u0926\u0928\u092a\u092b\u092c\u092f\u0930\u0933\u0935\u0936\u0937\u093c\u093d\u093e\u093f\u0940\u0941\u0943\u0947\u0948\u094b\u094c\u0953\u0956\u0962\u0964\u0970\u0b92\u0b9a\u0baf\u0bb3\u0bb5\u2020\u20ac\u20b9\u2190\u2191\u2192\u2193\u2194\u2195\u2196\u2197\u2198\u2199\u2203\u2206\u2207\u2227\u2228\u2229\u222a\u222b\u2282\u2283\u22a4\u22b7" +
|
||||
"\u22c4\u235d\u2375\u237a\u2395\ua649\ua651\ua657\ua67d\uFFFF\u006e\u0062\u0073\u0070\uFFFF\u0066\u0031\u0030\uFFFF\u0066\u0031\uFFFF\u0066\u0032\uFFFF\u0066\u0033\uFFFF\u0066\u0034\uFFFF\u0066\u0035\uFFFF\u0066\u0036\uFFFF\u0066\u0037\uFFFF\u0066\u0038\uFFFF\u0066\u0039\uFFFF\u007a\u0077\u006e\u006a\u20b1\u20b4\u20bf\uFFFF\u0072\u0065\u006d\u006f\u0076\u0065\u0064\u2213\u2219\u03f4\u220f\u2211\u03d0\u0263\u03f5\u03d1\u03f0\u03d6" +
|
||||
"\u03f1\u03c2\u03d2\u03d5\uFFFF\ua641\u0456\u0458\u046b\u0467\u047b\u0455\uFFFF\ua64b\u04bb\u044c\uFFFF\ua651\u044a\u0454\u0438\u0447\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0063\u0079\u0072\u0069\u006c\u006c\u0069\u0063\u005f\u0076\u007a\u006d\u0065\u0074\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0073\u0065\u0067\u006f\u006c\uFFFF\u0064\u0061\u0067\u0065\u0073\u0068\uFFFF\u0068\u006f\u006c\u0061\u006d" +
|
||||
"\uFFFF\u0071\u0075\u0062\u0075\u0074\u0073\uFFFF\u0068\u0069\u0072\u0069\u0071\uFFFF\u0072\u0061\u0066\u0065\uFFFF\u0073\u0065\u0067\u006f\u006c\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0070\u0061\u0074\u0061\u0068\uFFFF\u0070\u0061\u0074\u0061\u0068\uFFFF\u0074\u0073\u0065\u0072\u0065\uFFFF\u0071\u0061\u006d\u0061\u0074\u0073\uFFFF\u0068\u0061\u0074\u0061\u0066\u005f\u0071\u0061\u006d\u0061\u0074\u0073\uFFFF\u0073\u0068\u0065" +
|
||||
"\u0076\u0061\u066e\u067e\u06c1\u0698\u0636\u0638\u0621\u06a4\u063a\u06a9\u0640\u06ba\u06be\u06cc\u0643\u0647\u064a\u0900\u0955\u0972\u0911\u0973\u0974\u0976\u0977\u0960\u0961\u090d\u090e\u0912\u0975\u097b\u097c\uFFFF\u0936\u094d\u091a\u0979\u0978\u097e\uFFFF\u0924\u094d\u0930\uFFFF\u0926\u094d\u0930\uFFFF\u092a\u094d\u0930\u097f\u097a\uFFFF\u0936\u094d\u0930\uFFFF\u0915\u094d\u0937\u094e\u097d\u0949\u093a\u093b\u0904\u0944\u0945" +
|
||||
"\u0946\u094a\u094f\u0954\u0957\u0963\u0965\u0971\uFFFF\u0bd0\uFFFF\u0bf2\uFFFF\u0bf0\uFFFF\u0bf1\uFFFF\u0bf3\u21d4\u21d5\u21d6\u21d7\u21d8\u21d9\u22c0\u22c1\u22c2\u22c3\u222e\u22b6\u044b\u044f\uFFFF\u0063\u006f\u006d\u0062\u0069\u006e\u0069\u006e\u0067\u005f\u0063\u0079\u0072\u0069\u006c\u006c\u0069\u0063\u005f\u0074\u0069\u0074\u006c\u006f\u00000123456789\u09e6\u09e7\u09e8\u09e9" +
|
||||
"\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u00000123456789\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u00000123456789\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u00000123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u000001" +
|
||||
"23456789\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u00000123456789\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u00000123456789\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0000\u00df\u0131\u01f0\u0237\u02b0\u02b2\u02b3\u02b7\u02e1\u0905\u0907" +
|
||||
"\u0909\u090b\u090c\u090f\u0913\u0915\u0917\u091a\u091c\u091f\u0921\u0924\u0926\u0928\u092c\u092e\u0932\u0938\u0939\u093f\u0941\u0943\u0945\u0947\u0949\u094b\u0952\u0962\u0a85\u0a87\u0a89\u0a8f\u0a93\u0a95\u0a97\u0a9a\u0a9c\u0a9f\u0aa1\u0aa4\u0aa6\u0aa8\u0aaa\u0aac\u0aae\u0ab2\u0ab8\u0ab9\u0abf\u0ac1\u0ac7\u0acb\u0bf9\u1d43\u1d47\u1d48\u1d49\u1d4d\u1d4f\u1d50\u1d52\u1d56\u1d57\u1d58\u1d5b\u1d60\u1d9c\u1da0\u1dbe\u1e97\u1e98\u1e99" +
|
||||
"\u2071\u207f\u20b9\u2190\u2191\u2192\u2193\u2196\u2197\u2198\u2199\u2208\u220b\u2282\u2283\u2286\u2287\u2500\u2502\u250c\u2510\u2514\u2518\u251c\u2524\u252c\u2534\u253c\ud835\uFFFF\u004a\u030c\uFFFF\u004a\u0307\u1d34\u1d36\u1d3f\u1d42\u1d38\u0906\u0908\u090a\u0910\u0914\u0916\u0918\u091b\u091d\u0920\u0922\u0925\u0927\u0923\u092d\u0902\u0933\u0936\u0903\u0940\u0942\u0948\u094c\u0951\u0a86\u0a88\u0a8a\u0a90\u0a94\u0a96\u0a98\u0a9b" +
|
||||
"\u0a9d\u0aa0\u0aa2\u0aa5\u0aa7\u0aa3\u0aab\u0aad\u0a82\u0ab3\u0ab6\u0a83\u0ac0\u0ac2\u0ac8\u0acc\u1d2c\u1d2e\u1d30\u1d31\u1d33\u1d37\u1d39\u1d3c\u1d3e\u1d40\u1d41\u2c7d\u1db2\uFFFF\ua7f2\uFFFF\ua7f3\uFFFF\u0054\u0308\uFFFF\u0057\u030a\uFFFF\u0059\u030a\u1d35\u1d3a\u2550\u2551\u2554\u2557\u255a\u255d\u2560\u2563\u2566\u2569\u256c\u0000\udd57\udd58\udd64\udd68\udd69\uFFFF\ud835\udd3d\uFFFF\ud835\udd3e\uFFFF\ud835\udd4a\uFFFF\ud835" +
|
||||
"\udd4e\uFFFF\ud835\udd4f\u0000ABCDEFGHIJKLMNOPQRSTUVWXYZx\u00a2\u00a3\u00a5\u00aa\u00b5\u00ba\u00c0\u00c1\u00c2\u00c3\u00c4\u00c5\u00c6\u00c7\u00c8\u00c9\u00ca\u00cb\u00cc\u00cd\u00ce\u00cf\u00d1\u00d2\u00d3\u00d4\u00d5\u00d6\u00d8\u00d9\u00da\u00db\u00dc\u00dd\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5" +
|
||||
"\u00e6\u00e7\u00e8\u00e9\u00ea\u00eb\u00ec\u00ed\u00ee\u00ef\u00f1\u00f2\u00f3\u00f4\u00f5\u00f6\u00f8\u00f9\u00fa\u00fb\u00fc\u00fd\u00ff\u0100\u0101\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0116\u0117\u0118\u0119\u011a\u011b\u011c\u011d\u0120\u0121\u0122\u0123\u0124\u0125\u0126\u0127\u0128\u0129\u012a\u012b\u012e\u012f\u0130\u0131\u0134\u0135\u0136\u0137\u0139\u013a\u013b" +
|
||||
"\u013c\u013d\u013e\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u014c\u014d\u0150\u0151\u0152\u0153\u0154\u0155\u0156\u0157\u0158\u0159\u015a\u015b\u015c\u015d\u015e\u015f\u0160\u0161\u0162\u0163\u0164\u0165\u0166\u0167\u0168\u0169\u016a\u016b\u016e\u016f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0178\u0179\u017a\u017b\u017c\u017d\u017e\u0180\u0194\u0197\u019a\u019f\u01a0\u01a1\u01a6\u01af\u01b0\u01b5\u01b6\u01cd\u01ce" +
|
||||
"\u01cf\u01d0\u01d1\u01d2\u01d3\u01d4\u01d5\u01d6\u01d7\u01d8\u01d9\u01da\u01db\u01dc\u01de\u01df\u01e0\u01e1\u01e2\u01e3\u01e4\u01e5\u01e6\u01e7\u01e8\u01e9\u01ea\u01eb\u01ec\u01ed\u01ee\u01ef\u01f0\u01f4\u01f5\u01f8\u01f9\u01fa\u01fb\u01fc\u01fd\u01fe\u01ff\u0200\u0201\u0204\u0205\u0208\u0209\u020c\u020d\u0210\u0211\u0214\u0215\u021e\u021f\u0226\u0227\u0228\u0229\u022a\u022b\u022c\u022d\u022e\u022f\u0230\u0231\u0232\u0233\u0237" +
|
||||
"\u023a\u023b\u023c\u023d\u023e\u0243\u0244\u0246\u0247\u0248\u0249\u024c\u024d\u024e\u024f\u025f\u0262\u0263\u0268\u026a\u0274\u0275\u0276\u0280\u0289\u028f\u0299\u029b\u029c\u029f\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02e0\u02e1\u02e2\u02e3\u0386\u0388\u0389\u038a\u038c\u038e\u0392\u0395\u0398\u039a\u039c\u03a0\u03a1\u03a3\u03a6\u03aa\u03ab\u03ac\u03ad\u03ae\u03af\u03b0\u03c2\u03ca\u03cb\u03cc\u03cd\u03d0\u03d1" +
|
||||
"\u03d2\u03d4\u03d5\u03d6\u03f0\u03f1\u03f4\u03f5\u0400\u0402\u0403\u0404\u0405\u0406\u0408\u040b\u040c\u040d\u040f\u0418\u0419\u0427\u042a\u042b\u042c\u042f\u0438\u0439\u0447\u044a\u044b\u044c\u044f\u0450\u0452\u0453\u0454\u0455\u0456\u0458\u045b\u045c\u045d\u045f\u0462\u0463\u0466\u0467\u046a\u046b\u0470\u0471\u0472\u0473\u0476\u0477\u047a\u047b\u0480\u0481\u048c\u048d\u0490\u0491\u0498\u0499\u049a\u049b\u04b0\u04b1\u04b6\u04b7" +
|
||||
"\u04ba\u04bb\u04d2\u04d3\u04da\u04db\u04dc\u04dd\u04de\u04df\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04e8\u04e9\u04ea\u04eb\u04ec\u04ed\u04ee\u04ef\u04f0\u04f1\u04f2\u04f3\u04f4\u04f5\u04f8\u04f9\u04fe\u04ff\u0528\u0529\u052e\u052f\u0621\u0622\u0630\u0635\u0636\u0637\u0638\u063a\u0640\u0643\u0647\u064a\u066e\u067e\u0695\u0698\u06a1\u06a4\u06a9\u06af\u06b5\u06ba\u06be\u06c1\u06cc\u0902\u0903\u0906\u0908\u090a\u090d\u090e\u0910\u0911" +
|
||||
"\u0912\u0914\u0916\u0918\u091b\u091d\u0920\u0922\u0923\u0925\u0927\u0929\u092d\u0931\u0933\u0934\u0936\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097d\u097e\u097f\u0a82\u0a83\u0a86\u0a88\u0a8a\u0a90\u0a94\u0a96\u0a98\u0a9b\u0a9d\u0aa0\u0aa2\u0aa3\u0aa5\u0aa7\u0aab\u0aad\u0ab3\u0ab6\u1d00\u1d01\u1d03\u1d04\u1d05\u1d06\u1d07\u1d0a\u1d0b\u1d0c\u1d0d" +
|
||||
"\u1d0e\u1d0f\u1d10\u1d15\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d20\u1d21\u1d22\u1d23\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d43\u1d44\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5d\u1d5f\u1d60\u1d61\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d7b\u1d7d\u1d7e\u1d9b\u1d9c\u1d9d\u1d9f\u1da0\u1da1\u1da3\u1da4\u1da5\u1dac\u1dad\u1db1\u1db3\u1db4\u1db6" +
|
||||
"\u1db7\u1dba\u1dbb\u1dbe\u1dbf\u1e02\u1e03\u1e04\u1e05\u1e08\u1e09\u1e0a\u1e0b\u1e0c\u1e0d\u1e10\u1e11\u1e14\u1e15\u1e16\u1e17\u1e1c\u1e1d\u1e1e\u1e1f\u1e20\u1e21\u1e22\u1e23\u1e24\u1e25\u1e26\u1e27\u1e28\u1e29\u1e2e\u1e2f\u1e30\u1e31\u1e32\u1e33\u1e36\u1e37\u1e38\u1e39\u1e3e\u1e3f\u1e40\u1e41\u1e42\u1e43\u1e44\u1e45\u1e46\u1e47\u1e4c\u1e4d\u1e4e\u1e4f\u1e50\u1e51\u1e52\u1e53\u1e54\u1e55\u1e56\u1e57\u1e58\u1e59\u1e5a\u1e5b\u1e5c" +
|
||||
"\u1e5d\u1e60\u1e61\u1e62\u1e63\u1e64\u1e65\u1e66\u1e67\u1e68\u1e69\u1e6a\u1e6b\u1e6c\u1e6d\u1e78\u1e79\u1e7c\u1e7d\u1e7e\u1e7f\u1e80\u1e81\u1e82\u1e83\u1e84\u1e85\u1e86\u1e87\u1e88\u1e89\u1e8a\u1e8b\u1e8c\u1e8d\u1e8e\u1e8f\u1e90\u1e91\u1e92\u1e93\u1e97\u1e98\u1e99\u1e9b\u1e9e\u1ea0\u1ea1\u1ea2\u1ea3\u1ea4\u1ea5\u1ea6\u1ea7\u1ea8\u1ea9\u1eaa\u1eab\u1eac\u1ead\u1eae\u1eaf\u1eb0\u1eb1\u1eb2\u1eb3\u1eb4\u1eb5\u1eb6\u1eb7\u1eb8\u1eb9" +
|
||||
"\u1eba\u1ebb\u1ebc\u1ebd\u1ebe\u1ebf\u1ec0\u1ec1\u1ec2\u1ec3\u1ec4\u1ec5\u1ec6\u1ec7\u1ec8\u1ec9\u1eca\u1ecb\u1ecc\u1ecd\u1ece\u1ecf\u1ed0\u1ed1\u1ed2\u1ed3\u1ed4\u1ed5\u1ed6\u1ed7\u1ed8\u1ed9\u1eda\u1edb\u1edc\u1edd\u1ede\u1edf\u1ee0\u1ee1\u1ee2\u1ee3\u1ee4\u1ee5\u1ee6\u1ee7\u1ee8\u1ee9\u1eea\u1eeb\u1eec\u1eed\u1eee\u1eef\u1ef0\u1ef1\u1ef2\u1ef3\u1ef4\u1ef5\u1ef6\u1ef7\u1ef8\u1ef9\u1f70\u1f72\u1f74\u1f76\u1f78\u1f7a\u1f7c\u1fb1" +
|
||||
"\u1fb9\u1fba\u1fc8\u1fca\u1fd1\u1fd9\u1fda\u1fe1\u1fe2\u1fe7\u1fe9\u1fea\u1ff8\u1ffa\u2071\u207f\u2090\u2091\u2092\u2093\u2094\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u20ac\u20b1\u20b4\u20b9\u20bd\u20bf\u220f\u2211\u2422\u2c63\u2c65\u2c66\u2c7b\u2c7c\ua640\ua641\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua650\ua651\ua656\ua657\ua730\ua731\ua740\ua741\ua742\ua743\ua756\ua757\ua75e\ua75f\ua776\ua792\ua793\ua798\ua799\ua7a0\ua7a1" +
|
||||
"\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7ae\ua7af\ua7b8\ua7b9\ua7f4\ua7f9\ua7fa\uab3f\uab65\uab69abcdefghijklmnopqrstuvwyz\u03b3\u0292\u0260\u0266\u0279\u027b\u03b1\u03b5\u03b7\u03b9\u03bf\u03c5\u03b2\u03b8\u03ba\u03c0\u03c1\u03c3\u03c6\u0435\u0433\u044d\u0441\u043a\u043d\u043c\u0448\u0444\u043e\u0434\u04af" +
|
||||
"\u0445\u0430\u0436\u0437\u0443\u043b\u0639\u0627\u062f\u0633\u062a\u0632\u0642\u0645\u0628\u0631\u0648\u0641\u0644\u0646\u062d\u092e\u0939\u0905\u0907\u0909\u090f\u0913\u0915\u0917\u091a\u091c\u091f\u0921\u0928\u0924\u0926\u092c\u0930\u0932\u0938\u092b\u092f\u090b\u090c\u0935\u093d\u0aae\u0ab9\u0a85\u0a87\u0a89\u0a8f\u0a93\u0a95\u0a97\u0a9a\u0a9c\u0a9f\u0aa1\u0aa8\u0aa4\u0aa6\u0aaa\u0aac\u0ab2\u0ab8\u0254\u0223\u03bb\u03c8\u0250" +
|
||||
"\u1d02\u025b\u1d08\u1d09\u1d1d\u026f\u03b4\u03c7\u0252\u0255\u025c\u0265\u0271\u0270\u0282\u0283\u028a\u028c\u03c9\u01dd\uFFFF\ua775\u028d").toCharArray();
|
||||
|
||||
public static final char[] edges =
|
||||
("\u0001\u0036\u0037\u0038\u0039\u003a\u003b\u003c\u003f\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u0055\u0056\u0059\u005a\u005b\\\u005d\u005e\u005f\u0060\u0061\u0062\u0063\u0064\u0067\u0068\u006b\u006e\u006f\u0072\u0075\u0078\u007b\u007e\u0081\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
@@ -166,125 +166,125 @@ public final class ComposeKeyData
|
||||
"\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u0515\u052a\u052b\u052c\u052d\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0012\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0180\u0199\u0052\u0548\u005b\u0549\u032d\u02ff\u0300\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u002f\u0579\u057a\u057b\u057e\u057f\u0580\u0581\u0584\u0587\u058a\u058d\u0590\u0591" +
|
||||
"\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u0050\u0548\u02c5\u0054\u032b\u032e\u032f\u0115\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u00cf\u05a9\u05aa\u05ab\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0149\u06f5\u070a\u0756\u07c2\u07d1\u07d4\u0909\u0cc1\u0ce2\u0cf1\u0cfc\u0d48\u0d87\u0e0f\u0e51\u0e57\u0e6d\u0e73\u0e7b\u0e7e\u0e83\u0e86\u0e89\u0e93\u0eaa\u0ed7\u0f0a\u0f29\u0f68\u0f82\u0f86\u0f97\u0f9f\u0fb4\u0fc2\u0fd5\u0fd7\u0fea\u0fed\u0ff0\u1002\u1004\u1014\u1031\u1039\u103d\u1048\u1057\u1067\u10bd\u10c4\u10c8\u10ca\u10d4\u10db\u10de\u10f3\u10f5\u1189" +
|
||||
"\u1230\u1319\u132c\u1356\u138d\u1397\u13a9\u13b7\u13ca\u13cc\u13dc\u13df\u13e3\u13eb\u13ef\u13f6\u1411\u141b\u1420\u142c\u1439\u1454\u1478\u147a\u147e\u1486\u148a\u148d\u14a9\u1522\u153d\u158c\u164e\u1666\u1669\u166c\u166e\u1670\u1672\u1675\u1677\u167a\u167c\u167e\u1680\u1682\u172d\u172f\u1732\u1734\u173c\u1745\u175c\u1769\u177c\u177e\u1780\u1785\u178a\u178c\u1798\u179e\u17a0\u17a9\u17b0\u17b4\u17b6\u17c2\u17cd\u17d0\u17d2\u17d6" +
|
||||
"\u17ec\u180c\u1827\u1830\u183d\u184f\u1851\u1855\u1859\u186c\u1874\u1879\u187b\u1892\u18b1\u18b5\u18bd\u18d7\u18f4\u194e\u1951\u1953\u1966\u196b\u196f\u1972\u1974\u1976\u197a\u1981\u1985\u1989\u19cc\u19d6\u19e0\u19ea\u1a1f\u1a28\u1a2b\u1a3d\u1a43\u1a46\u1a48\u1a82\u1a86\u1a89\u1a8c\u1aa3\u1ae2\u1aff\u1b1b\u1b37\u1b3a\u1b43\u1b45\u1b61\u1b83\u1b8d\u1b96\u1b9c\u1bb3\u1bba\u1bbf\u1bcc\u1bd1\u1bd8\u1bdd\u1be0\u1be3\u1be6\u1be9\u1bf0" +
|
||||
"\u1bf3\u1bf8\u1bfb\u1c02\u1c05\u1c0c\u1c0f\u1c12\u1c15\u1c18\u1c1b\u1c1e\u1c21\u1c24\u1c27\u1c32\u1c35\u1c38\u1c41\u1c46\u1c4f\u1c54\u1c75\u1c77\u1c98\u1c9a\u1cb1\u1cb6\u1cbb\u1cc0\u1cc3\u1cc9\u1ccb\u1cce\u1cd1\u1cd4\u1cd7\u1cde\u1ce5\u1cee\u1cf5\u1d06\u1d08\u1d13\u1d24\u1d27\u1d36\u1d3d\u1d40\u1d4d\u1d56\u1d5f\u1d61\u1d70\u1d75\u1d9e\u1da0\u1da3\u1da7\u1da9\u1dbc\u1dc7\u1dd2\u1ddb\u1de0\u1de9\u1dec\u1dee\u1df0\u1df5\u1df9\u1dfd" +
|
||||
"\u1e01\u1e05\u1e08\u1e0d\u1e13\u1e16\u1e18\u1e1b\u1e28\u1e2b\u1e2e\u1e33\u1e38\u1e3a\u1e3c\u1e3f\u1e42\u1e45\u1e48\u1e4c\u1e4e\u1e51\u1e54\u1e57\u1e5a\u1e5d\u1e60\u1e63\u1e66\u1e69\u1e6c\u1e71\u1e75\u1e78\u1e7b\u1e7e\u1e81\u1e84\u1e8a\u1e8e\u1e91\u1e94\u1e97\u1e9a\u1e9d\u1ea0\u1ea3\u1ea7\u1ea9\u1eab\u1ead\u1ec4\u1ece\u0009\u06fe\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u0005\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u002f\u0739\u073a\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u074b\u074c\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u023e\u023f\u0240\u0241\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0250\u073f\u0252\u0740\u0253\u0001\u0005\u073f\u0740\u0252\u0253\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0041\u0797\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u057a\u057e\u057f\u0580\u0590\u0594\u0595\u0596\u0597\u0598\u07aa\u07ae\u0798\u07ad\u0548\u07a8\u07a9\u07b1\u07b2\u059b\u059c\u059e\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u059f\u07be\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u07bf\u07c0\u05a8\u07c1\u00cf\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u07a8\u07a9\u0001\u0001\u0003\u07ad\u0548\u0001\u0003\u07a8\u07a9\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u07d3\u0001\u007b\u084f\u085b\u085c\u0879\u0894\u089b\u089c\u08a0\u08a1\u08a2\u08a3" +
|
||||
"\u08a4\u08a5\u08a6\u08a7\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08c0\u0037\u08c7\u0038\u0039\u003a\u003b\u003c\u003f\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u08cb\u08d2\u08d7\u08bd\u08db\u08dc\u08da\u08be\u0858\u08bf\u08d0\u089f\u0859\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u08ca\u0055\u08c5\u0059\u08c6\u005a\u08d1\u005b\u0899\\\u089a\u005d" +
|
||||
"\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u005e\u005f\u0060\u0061\u0062\u0063\u08e4\u08e5\u08e8\u08e9\u08ec\u08ef\u08f0\u08f3\u08f6\u08f9\u08fc\u08ff\u0902\u0064\u0067\u0068\u006b\u006e\u006f\u0905\u0072\u0075\u0078\u007b\u007e\u0908\u0008\u0857\u0858\u0859\u0050\u0054\u085a\u059d\u0001\u0001\u0001\u0001\u0001\u000f\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000e\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0899\u089a\\\u005d\u0001\u0001\u0001\u0003\u089f\u0053\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u08bd" +
|
||||
"\u08be\u08bf\u004b\u004f\u0051\u0001\u0001\u0001\u0005\u08c5\u08c6\u0059\u005a\u0001\u0001\u0003\u08ca\u0055\u0001\u0005\u08d0\u08d1\u0052\u005b\u0001\u0001\u0005\u08c5\u08c6\u0059\u005a\u0003\u08da\u004e\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003" +
|
||||
"\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u00c1\u0703\u09ca\u09cb\u09d0\u09d1\u09d4\u09ff\u0a2a\u0a55\u0a80\u0a87\u0a8a\u0a8d\u0a90\u0a93\u0a96\u0a99\u0a9c\u0a9f\u0aa2\u0aa5\u0aa8\u0aab\u0aae\u0ab1\u0ab4\u0ab7\u0aba\u0abd\u0ac0\u0ac3\u0ac6\u0ac9\u0acc\u0acf\u0ad2\u0ad5\u0ad8\u0adb\u0ade\u0ae1\u0ae4\u0ae7\u0aea\u0aed\u0af0\u0af3\u0af6\u0af9\u0afc\u0aff\u0b02\u0b05\u0b08\u0b0b\u0b0e\u0b11\u0b14\u0b17\u0b1a\u0b1d\u0b20" +
|
||||
"\u0b23\u0b26\u0b29\u0b2c\u0b2f\u0b30\u0b31\u0b32\u0b33\u0b34\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3a\u0b3b\u0b3c\u0b3d\u0b3e\u0b3f\u0b46\u0b4d\u0b54\u0b5b\u0b62\u0b69\u0b70\u0b77\u0b7e\u0b85\u0b8c\u0b93\u0b9a\u0ba1\u0ba4\u0ba7\u0baa\u0bad\u0bb0\u0bb3\u0bb6\u0bb9\u0bbc\u0bbf\u0bc2\u0bc5\u0bc8\u0bcb\u0bce\u0bd1\u0bd4\u0bd7\u0bda\u0bdd\u0be0\u0be3\u0be6\u0be9\u0bec\u0bef\u0bf2\u0bf5\u0bf8\u0bfb\u0bfe\u0c01\u0c04\u0c07\u0c0a\u0c0d\u0c10" +
|
||||
"\u0c13\u0c16\u0c19\u0c1c\u0c1f\u0c22\u0c25\u0c28\u0c2b\u0c2e\u0c31\u0c34\u0c37\u0c3a\u0c3d\u0c40\u0c43\u0c46\u0c49\u0c4c\u0c4f\u0c52\u0c55\u0c58\u0c5b\u0c5e\u0c61\u0c64\u0c67\u0c6a\u0c6d\u0c70\u0c73\u0c76\u0c79\u0c7c\u0c7f\u0c82\u0c85\u0c88\u0c8b\u0c8e\u0c91\u0c94\u0c97\u0c9a\u0c9d\u0ca0\u0ca3\u0ca6\u0ca9\u0cac\u0caf\u0cb2\u0cb5\u0cb8\u0cbb\u0cbe\u0001\u0002\u09cd\u0003\u0000\u0000\u0001\u0002\u09d3\u0001\u000c\u09e0\u09e1\u09e4" +
|
||||
"\u09e7\u09ea\u09ed\u09f0\u09f3\u09f6\u09f9\u09fc\u0001\u0002\u09e3\u0001\u0002\u09e6\u0001\u0002\u09e9\u0001\u0002\u09ec\u0001\u0002\u09ef\u0001\u0002\u09f2\u0001\u0002\u09f5\u0001\u0002\u09f8\u0001\u0002\u09fb\u0001\u0002\u09fe\u0001\u000c\u0a0b\u0a0c\u0a0f\u0a12\u0a15\u0a18\u0a1b\u0a1e\u0a21\u0a24\u0a27\u0001\u0002\u0a0e\u0001\u0002\u0a11\u0001\u0002\u0a14\u0001\u0002\u0a17\u0001\u0002\u0a1a\u0001\u0002\u0a1d\u0001\u0002\u0a20" +
|
||||
"\u0001\u0002\u0a23\u0001\u0002\u0a26\u0001\u0002\u0a29\u0001\u000c\u0a36\u0a37\u0a3a\u0a3d\u0a40\u0a43\u0a46\u0a49\u0a4c\u0a4f\u0a52\u0001\u0002\u0a39\u0001\u0002\u0a3c\u0001\u0002\u0a3f\u0001\u0002\u0a42\u0001\u0002\u0a45\u0001\u0002\u0a48\u0001\u0002\u0a4b\u0001\u0002\u0a4e\u0001\u0002\u0a51\u0001\u0002\u0a54\u0001\u000c\u0a61\u0a62\u0a65\u0a68\u0a6b\u0a6e\u0a71\u0a74\u0a77\u0a7a\u0a7d\u0001\u0002\u0a64\u0001\u0002\u0a67\u0001" +
|
||||
"\u0002\u0a6a\u0001\u0002\u0a6d\u0001\u0002\u0a70\u0001\u0002\u0a73\u0001\u0002\u0a76\u0001\u0002\u0a79\u0001\u0002\u0a7c\u0001\u0002\u0a7f\u0001\u0003\u0a83\u0a84\u0001\u0002\u0a86\u0001\u0002\u0a89\u0001\u0002\u0a8c\u0001\u0002\u0a8f\u0001\u0002\u0a92\u0001\u0002\u0a95\u0001\u0002\u0a98\u0001\u0002\u0a9b\u0001\u0002\u0a9e\u0001\u0002\u0aa1\u0001\u0002\u0aa4\u0001\u0002\u0aa7\u0001\u0002\u0aaa\u0001\u0002\u0aad\u0001\u0002\u0ab0" +
|
||||
"\u0001\u0002\u0ab3\u0001\u0002\u0ab6\u0001\u0002\u0ab9\u0001\u0002\u0abc\u0001\u0002\u0abf\u0001\u0002\u0ac2\u0001\u0002\u0ac5\u0001\u0002\u0ac8\u0001\u0002\u0acb\u0001\u0002\u0ace\u0001\u0002\u0ad1\u0001\u0002\u0ad4\u0001\u0002\u0ad7\u0001\u0002\u0ada\u0001\u0002\u0add\u0001\u0002\u0ae0\u0001\u0002\u0ae3\u0001\u0002\u0ae6\u0001\u0002\u0ae9\u0001\u0002\u0aec\u0001\u0002\u0aef\u0001\u0002\u0af2\u0001\u0002\u0af5\u0001\u0002\u0af8" +
|
||||
"\u0001\u0002\u0afb\u0001\u0002\u0afe\u0001\u0002\u0b01\u0001\u0002\u0b04\u0001\u0002\u0b07\u0001\u0002\u0b0a\u0001\u0002\u0b0d\u0001\u0002\u0b10\u0001\u0002\u0b13\u0001\u0002\u0b16\u0001\u0002\u0b19\u0001\u0002\u0b1c\u0001\u0002\u0b1f\u0001\u0002\u0b22\u0001\u0002\u0b25\u0001\u0002\u0b28\u0001\u0002\u0b2b\u0001\u0002\u0b2e\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003" +
|
||||
"\u0b42\u0b43\u0001\u0002\u0b45\u0001\u0003\u0b49\u0b4a\u0001\u0002\u0b4c\u0001\u0003\u0b50\u0b51\u0001\u0002\u0b53\u0001\u0003\u0b57\u0b58\u0001\u0002\u0b5a\u0001\u0003\u0b5e\u0b5f\u0001\u0002\u0b61\u0001\u0003\u0b65\u0b66\u0001\u0002\u0b68\u0001\u0003\u0b6c\u0b6d\u0001\u0002\u0b6f\u0001\u0003\u0b73\u0b74\u0001\u0002\u0b76\u0001\u0003\u0b7a\u0b7b\u0001\u0002\u0b7d\u0001\u0003\u0b81\u0b82\u0001\u0002\u0b84\u0001\u0003\u0b88\u0b89" +
|
||||
"\u0001\u0002\u0b8b\u0001\u0003\u0b8f\u0b90\u0001\u0002\u0b92\u0001\u0003\u0b96\u0b97\u0001\u0002\u0b99\u0001\u0003\u0b9d\u0b9e\u0001\u0002\u0ba0\u0001\u0002\u0ba3\u0001\u0002\u0ba6\u0001\u0002\u0ba9\u0001\u0002\u0bac\u0001\u0002\u0baf\u0001\u0002\u0bb2\u0001\u0002\u0bb5\u0001\u0002\u0bb8\u0001\u0002\u0bbb\u0001\u0002\u0bbe\u0001\u0002\u0bc1\u0001\u0002\u0bc4\u0001\u0002\u0bc7\u0001\u0002\u0bca\u0001\u0002\u0bcd\u0001\u0002\u0bd0" +
|
||||
"\u0001\u0002\u0bd3\u0001\u0002\u0bd6\u0001\u0002\u0bd9\u0001\u0002\u0bdc\u0001\u0002\u0bdf\u0001\u0002\u0be2\u0001\u0002\u0be5\u0001\u0002\u0be8\u0001\u0002\u0beb\u0001\u0002\u0bee\u0001\u0002\u0bf1\u0001\u0002\u0bf4\u0001\u0002\u0bf7\u0001\u0002\u0bfa\u0001\u0002\u0bfd\u0001\u0002\u0c00\u0001\u0002\u0c03\u0001\u0002\u0c06\u0001\u0002\u0c09\u0001\u0002\u0c0c\u0001\u0002\u0c0f\u0001\u0002\u0c12\u0001\u0002\u0c15\u0001\u0002\u0c18" +
|
||||
"\u0001\u0002\u0c1b\u0001\u0002\u0c1e\u0001\u0002\u0c21\u0001\u0002\u0c24\u0001\u0002\u0c27\u0001\u0002\u0c2a\u0001\u0002\u0c2d\u0001\u0002\u0c30\u0001\u0002\u0c33\u0001\u0002\u0c36\u0001\u0002\u0c39\u0001\u0002\u0c3c\u0001\u0002\u0c3f\u0001\u0002\u0c42\u0001\u0002\u0c45\u0001\u0002\u0c48\u0001\u0002\u0c4b\u0001\u0002\u0c4e\u0001\u0002\u0c51\u0001\u0002\u0c54\u0001\u0002\u0c57\u0001\u0002\u0c5a\u0001\u0002\u0c5d\u0001\u0002\u0c60" +
|
||||
"\u0001\u0002\u0c63\u0001\u0002\u0c66\u0001\u0002\u0c69\u0001\u0002\u0c6c\u0001\u0002\u0c6f\u0001\u0002\u0c72\u0001\u0002\u0c75\u0001\u0002\u0c78\u0001\u0002\u0c7b\u0001\u0002\u0c7e\u0001\u0002\u0c81\u0001\u0002\u0c84\u0001\u0002\u0c87\u0001\u0002\u0c8a\u0001\u0002\u0c8d\u0001\u0002\u0c90\u0001\u0002\u0c93\u0001\u0002\u0c96\u0001\u0002\u0c99\u0001\u0002\u0c9c\u0001\u0002\u0c9f\u0001\u0002\u0ca2\u0001\u0002\u0ca5\u0001\u0002\u0ca8" +
|
||||
"\u0001\u0002\u0cab\u0001\u0002\u0cae\u0001\u0002\u0cb1\u0001\u0002\u0cb4\u0001\u0002\u0cb7\u0001\u0002\u0cba\u0001\u0002\u0cbd\u0001\u0002\u0cc0\u0001\u0011\u0cd2\u0cd3\u0cd4\u0cd5\u0cd6\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cde\u0cdf\u0ce0\u0ce1\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0009\u0ceb\u0351\u0cee\u0cef\u0362\u0363\u0579\u0cf0\u0003\u08db\u004c\u0001\u0001" +
|
||||
"\u0001\u0007\u0cf8\u0cf9\u0cfa\u0cfb\u02fd\u02fe\u0001\u0001\u0001\u0001\u0030\u0704\u0799\u089b\u0704\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d3a\u0d3b\u033f\u013e\u013f\u0340\u0141\u0142\u0341\u0143\u0144\u0145\u0342\u0146\u0147\u0148\u0343\u0d3c\u0d3d\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43\u0d44\u0d45\u0d46\u0d47\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0020\u0705\u09d0\u0cd3\u0cf9\u0d2c\u0d68\u0d6f\u0d78\u0d79\u0095\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d82\u0709\u0d83\u031e\u00b7\u031f\u0321\u0d7f\u0325\u0329\u0d84\u0d85\u0d86\u0004\u0d6c\u0d6d\u0d6e\u0001\u0001\u0001\u0003\u0d72\u0d75\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0050\u0dd7\u0dd8\u0ddc\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df2\u0df3\u0df4\u0df5\u0df6\u0df7\u0df8\u0df9\u0de0\u01ed\u01ee\u01ef\u01f0\u01f1\u01f2\u01f3\u01f4\u01f5\u01fd\u01fe\u01ff\u0200\u0204\u0205\u0206\u020d\u020e\u020f\u0210\u0dfa\u0ddf\u0081\u0dfd\u012f\u0214\u07b3\u0dfe\u0dff\u0e00\u0e01\u0e02\u0e03\u0d46" +
|
||||
"\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0d47\u0e0c\u0e0d\u07bf\u0ddb\u0215\u0e0e\u0001\u0003\u0ddb\u0215\u0001\u0003\u0ddf\u0081\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0ddf\u0081\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0028" +
|
||||
"\u0d78\u0e37\u0e37\u0e38\u0e39\u0e3a\u0d7b\u0e3b\u0e3c\u0e3d\u0e3e\u0e3f\u0e40\u0e41\u0e42\u00b4\u0e43\u00b7\u00ba\u00bb\u00bc\u0380\u0e44\u0383\u00c6\u0e45\u0e46\u00c9\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u00ce\u0e4d\u0e4e\u0e4f\u0e50\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0351\u0e55\u0e56\u0001\u0001\u000b" +
|
||||
"\u0e62\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u04e3\u0002\u0e64\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0e71\u0e72\u04e4\u0001\u0001\u0005\u0e78\u0e79\u0e7a\u04e5\u0001\u0001\u0001\u0002\u0e7d\u0001\u0003\u0e81\u0e82\u0001\u0001\u0002\u0e85\u0001\u0002\u0e88\u0001\u0006\u0e8f\u0e90\u0d79\u0e91\u0e92\u0001\u0001\u0001\u0001\u0011\u0ea4\u0d2d\u0d30\u0d33\u0d37\u0ea5\u0ea6\u0d3b\u0ea7\u033f\u0340\u0341" +
|
||||
"\u0342\u0ea8\u0ea9\u0343\u0001\u0001\u0001\u0001\u0001\u0001\u001e\u0707\u079a\u08a0\u0093\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0ece\u0ecf\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ecb\u0105\u0106\u0107\u010d\u010e\u0110\u0111\u0112\u0114\u0ed6\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0021\u0e38\u0ef8\u0ef9\u0efa\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f00\u0f01\u0f02\u0f03\u0f04\u0f05" +
|
||||
"\u0efa\u0f06\u0efa\u026b\u0efe\u0eff\u026c\u0f07\u0f03\u0efa\u0efe\u0efa\u0f08\u0efe\u0270\u0f09\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0015\u0708\u079b\u08a1\u0f1f\u0ecc\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f20\u0170\u0172\u0175\u0177\u0178\u0f27\u0f28\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0020\u0f49\u0f4a\u0f51\u0f52\u0f53\u0f54" +
|
||||
"\u0f55\u0f56\u0f57\u0f58\u0f59\u02e7\u0f63\u02e8\u02e9\u02ea\u02eb\u02ec\u0f60\u0f61\u0f62\u0196\u0198\u0185\u0f66\u02ed\u0f4f\u02ee\u0f50\u02ef\u0f67\u0001\u0005\u0f4f\u0f50\u02ee\u02ef\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0f60\u0f61\u0f62\u0196\u0198\u0185\u0001\u0001\u0001\u0003\u0f66\u02ed\u0001\u0001\u0015\u079c\u08a2\u0f7d\u0cee\u0d2d\u0d7a\u0d2d\u0f22\u0cee\u0f7e\u0f7f\u0f22\u0d7a\u0f80\u0cee" +
|
||||
"\u0f7e\u0f7f\u0f81\u079c\u08a2\u0001\u0001\u0001\u0001\u0001\u0003\u0de7\u0f85\u0001\u000c\u08a3\u0d2e\u0de8\u0e3a\u0ecd\u0efa\u0f92\u0f93\u0f94\u0f95\u0f96\u0001\u0001\u0001\u0001\u0001\u0007\u0d2f\u0d7b\u0de9\u0ece\u0f9e\u0f9e\u0001\u0012\u079d\u08a4\u0d30\u0d7c\u0dea\u0d30\u0ecf\u0efa\u0f23\u0fb1\u0f23\u0d7c\u0fb2\u0fb1\u0fb3\u079d\u08a4\u0001\u0001\u0001\u0006\u0deb\u0fba\u0fbd\u0fbf\u0fc1\u0003\u0000\u0000\u0002\u0000\u0002" +
|
||||
"\u0000\u0001\n\u0fcc\u0d31\u0dec\u0fcd\u0fcc\u0fce\u0fcc\u0fd4\u0fcc\u0001\u0001\u0002\u0fd0\u0004\u0000\u0000\u0000\u0001\u0002\u0d32\u0010\u079f\u08a6\u0d33\u0d7d\u0dee\u0d33\u0f24\u0fe7\u0f24\u0d7d\u0fe8\u0fe7\u0fe9\u079f\u08a6\u0001\u0001\u0001\u0003\u08a7\u08a7\u0003\u0d34\u0d7e\n\u08ab\u0d35\u0d7f\u0e3e\u0ed0\u0efb\u0ffa\u1001\u0e42\u0002\u0ffc\u0002\u0ffe\u0003\u0000\u0000\u0001\u0002\u0def\u000c\u08ad\u0d36\u0ed1" +
|
||||
"\u0efc\u1010\u1011\u1012\u1010\u1011\u1012\u1013\u0001\u0001\u0001\u0001\u0016\u07a0\u08ae\u0d37\u0d80\u0e3f\u0d37\u0f25\u0a95\u0f94\u102a\u102b\u102c\u102d\u102e\u0f25\u0d80\u102f\u102a\u1030\u07a0\u08ae\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u1037\u0df2\u0efe\u1037\u1038\u0001\u0001\u0003\u103c\u103c\u0001\u0009\u08b0\u0d38\u0ed2\u0eff\u102b\u1046\u1046\u1047\u0001\u0001\u000c\u102c\u08b1\u0d39\u0df4\u0ea5\u0ed3\u1054" +
|
||||
"\u102c\u1055\u1055\u1056\u0001\u0001\u0001\u000c\u0d3a\u0e40\u0df5\u0e40\u0ea6\u0ed4\u0f00\u1063\u1064\u1063\u1065\u0001\u0001\u0002\u0000\u002d\u1094\u109a\u07a1\u08b2\u0cef\u0d3b\u0d81\u0d3b\u0f26\u0f7d\u109e\u0fcc\u109f\u10a0\u10a1\u0f26\u0d81\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u07a1\u08b2\u10aa\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u0002\u1096\u0003\u1099" +
|
||||
"\u0149\u0001\u0003\u109d\u0251\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1099\u0149\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u0e42\u0ecd\u0ed3\u0ed5\u10c3\u0001\u0003\u0f02\u10c7\u0001\u0002\u102d\u0008\u07a4\u08b4\u0f03\u10d2\u10d3\u07a4\u08b4\u0001\u0001\u0006\u08b5\u0df9\u0ed5\u10da\u10da\u0001\u0002\u10dd\u0001\u0008\u0d82" +
|
||||
"\u0f58\u10e6\u10e9\u10ee\u10f1\u10f2\u0003\u0000\u0000\u0002\u10eb\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0002\u10dd\u004e\u1143\u04de\u04df\u04e0\u04e1\u0de0\u0e42\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04e8\u04e9\u04ea\u04eb\u04ec\u0f22\u114d\u0f23\u114e\u114f\u0f24\u1150\u0f25\u1151\u0f26\u10c7\u10d2\u1152\u1153\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0184\u0178\u0179\u017d\u017e\u0097\u1166\u1169\u116c\u116f" +
|
||||
"\u1172\u1175\u0186\u0189\u018c\u018f\u1178\u0192\u114b\u0197\u114c\u019a\u04e1\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u0007\u114a\u114b\u114c\u0195\u0197\u019a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0013\u0352\u035a\u04f2\u04f3\u04f5\u0354\u0353\u0357\u0358\u04fd\u04fe\u04ff\u050e\u0510\u0516\u0517\u0518\u0523\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003" +
|
||||
"\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0058\u0709\u11e1\u11e8\u11f2\u0463\u0464\u0465\u0466\u11f3\u0467\u0468\u0469\u046a\u046b\u046c\u046d\u046e\u046f\u0470\u11fc\u0ecb\u0471\u0f20\u0d7a\u0d7c\u1200\u0d7d\u0d80\u0d81\u1201\u0709\u0709\u031e\u031f\u0320\u0321\u0325\u0329\u032a\u1202\u11ef\u1206\u1205\u11f0\u11f1\u032b" +
|
||||
"\u032c\u032d\u032e\u032f\u11fa\u0212\u1207\u1208\u1209\u0331\u0332\u0333\u120a\u120d\u1210\u1211\u1214\u1217\u1218\u121b\u0334\u121e\u1221\u0335\u11e6\u0336\u11e7\u0337\u1224\u1225\u0466\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u0005\u11e6\u11e7\u0336\u0337\u0001\u0001\u0007\u11ef\u11f0\u11f1\u032b\u032e\u032f\u0001\u0001\u0001\u0001\u0007\u11fa\u0d72\u11fb\u0212\u0d75\u0213\u0001\u0001\u0003\u11ff\u0330\u0001" +
|
||||
"\u0001\u0001\u0003\u1205\u032d\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0046\u1276\u127d\u129a\u12b5\u0f80\u0fb2\u0fe8\u12bc\u102f\u10a2\u12bd\u12be\u12bf\u12c9\u12d0\u02b7\u12ef\u02b8\u02b9\u02ba\u02bb\u02bf\u02c0" +
|
||||
"\u02c1\u12f3\u12c6\u12c7\u12c8\u127b\u017f\u0181\u0182\u02c5\u12f2\u02c6\u12ce\u02c7\u12cf\u02c8\u12ba\u02c9\u12bb\u02ca\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u02d1\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u12ff\u1302\u1303\u1304\u1307\u130a\u130d\u02d8\u02d9\u1310\u1313\u1316\u0005\u127b\u02c5\u127c\u05a9\u0001\u0001\u000f\u128c\u128d\u128e\u128f\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000e\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b1\u12b2\u12b3\u12b4\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u12ba\u12bb\u02c9\u02ca\u0001\u0001\u0001\u0001\u0001\u0007\u12c6\u12c7\u12c8\u017f\u0181\u0182\u0001\u0001\u0001\u0005\u12ce\u12cf\u02c7\u02c8\u0001\u0001\r\u12dd\u12e0\u12e3\u12e6\u12e9\u12ec\u0285" +
|
||||
"\u0288\u028b\u028e\u0291\u0294\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u12f2\u02c6\u0001\u0005\u12ce\u12cf\u02c7\u02c8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0012\u057a\u0037\u10a3\u0362\u033f\u031e" +
|
||||
"\u033f\u0170\u0f7e\u0170\u031e\u02b7\u0362\u132b\u0540\u057a\u0037\u0001\u0021\u134d\u1350\u01ee\u0f7d\u109e\u0fcc\u109f\u10a0\u10a1\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u1353\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u0003\u109d\u0251\u0003\u1099\u0149\u0003\u1099\u0149\u002c\u1382\u0038\u013e\u01ef\u0e43\u0105\u0efa\u1386\u0ecd\u0ece\u0ecf\u1387\u1388\u1389\u138a\u0ed0\u0ed1" +
|
||||
"\u138b\u0ed2\u0ed3\u0ed4\u138c\u0ed5\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0e43\u1385\u0115\u0003\u1385\u0115\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u013f\u00b7\u01f0\u0106\u0f06\u1395\u1396\u0001\u0001\u0011\u057e\u0039\u0340\u031f\u01f1\u0340\u0107\u0efa\u0172\u0172\u031f\u02b8\u13a8\u0541\u057e\u0039\u0001\u0007\u01f2\u13b0\u13b1\u13b3\u13b5\u13b0" +
|
||||
"\u0001\u0002\u0000\u0002\u0000\u0002\u0000\n\u10a5\u0141\u01f3\u10a5\u13c1\u0fcd\u10a5\u13c7\u10a5\u0002\u13c3\u0004\u0000\u0000\u0000\u0003\u0000\u0000\u0002\u0142\u000f\u0580\u003b\u0341\u0321\u01f5\u0341\u0175\u0175\u0321\u02b9\u13db\u0542\u0580\u003b\u0001\u0003\u003c\u003c\u0003\u0143\u13e2\u0001\u0008\u0040\u0144\u0d7f\u0380\u010d\u1001\u0e42\u0004\u01fd\u0e44\u0e45\u0006\u0042\u0145\u010e\u13f5\u0543\u0001\u001a\u0590" +
|
||||
"\u0043\u0342\u0325\u0383\u0342\u0177\u0cee\u0cef\u0177\u0325\u02bb\u0362\u0f94\u1410\u0351\u102b\u102c\u0363\u0364\u102d\u0365\u0544\u0590\u0043\u0001\u0005\u1037\u0200\u0efe\u1416\u0002\u1418\u0003\u0000\u0000\u0005\u0045\u0146\u0110\u0eff\u000b\u102c\u0046\u0147\u0205\u0ea8\u0111\u1054\u102c\u142b\u0147\u0001\n\u0148\u00c6\u0206\u00c6\u0ea9\u0112\u1436\u1064\u1437\u0001\u0002\u0000\u001b\u0595\u0047\u0363\u0343\u0329\u0e45" +
|
||||
"\u0343\u0178\u0f7d\u109e\u0fcc\u109f\u10a0\u10a1\u0178\u0329\u02bf\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u0545\u0595\u0047\u0024\u0e46\u1386\u0ecd\u0ece\u0ecf\u1387\u1388\u1389\u138a\u0ed1\u138b\u0ed2\u0ed3\u0ed4\u138c\u0ed5\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u0e42\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0091\u0546\u0002\u0179\u0003\u102d\u147d\u0001\u0008\u0598\u0049\u0f03\u017d\u0547\u0598\u0049\u0004\u004a" +
|
||||
"\u0210\u0114\u0002\u148c\u0001\u0012\u149f\u14a0\u0f85\u0f96\u0fd4\u1056\u0097\u0e43\u0091\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u002f\u14d8\u14dc\u14f1\u1504\u0e56\u0f81\u0fb3\u0fe9\u1013\u1030\u10a9\u10c3\u10d3\u150b\u0540\u1515\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u14a1\u1519\u0599\u1512\u1513\u1514\u0180\u0199\u0183\u1518\u0549\u1509\u02ff\u150a\u0300" +
|
||||
"\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u0003\u14db\u05aa\u0001\u000b\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\n\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u1509\u150a\u02ff\u0300\u0001\u0001\u0007\u1512\u1513\u1514\u0180\u0199\u0183\u0001\u0001\u0001\u0003\u1518\u0549" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0017\u0857\u0579\u0f27\u079c\u079d\u079f\u07a0\u07a1\u07a4\u1539\u057a\u057e\u0580\u0590\u0595\u0598\u153a\u0857\u05ab\u059a\u153b\u153c\u0001\u0001\u0001\u0001\u0037\u1574\u1579\u1580\u1585\u0d7a\u0d7c\u1200\u0d7d\u0d80\u0d81\u1201\u031e\u031f\u0320\u0321\u0325\u0329\u032a\u1588\u11ef\u1206\u1205\u11f0\u11f1\u032b\u032c\u032d\u032e\u032f\u11fa\u0212\u1207\u1208\u1209" +
|
||||
"\u0331\u0332\u0333\u120a\u120d\u1210\u1211\u1214\u1217\u1218\u121b\u0334\u121e\u1221\u0335\u11e6\u0336\u11e7\u0337\u158b\u0005\u11e6\u11e7\u0336\u0337\u0007\u11ef\u11f0\u11f1\u032b\u032e\u032f\u0005\u11fa\u11fb\u0212\u0213\u0003\u11ff\u0330\u0003\u1205\u032d\u0001\u0077\u1603\u160a\u1619\u1627\u162c\u162f\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u1632\u1639\u0037" +
|
||||
"\u163e\u0038\u0039\u003a\u003b\u003c\u003f\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u1641\u1646\u164b\u08bd\u08db\u08dc\u08da\u08be\u0858\u08bf\u08d0\u089f\u0859\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u08ca\u0055\u08c5\u0059\u08c6\u005a\u08d1\u005b\u0899\\\u089a\u005d\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u005e\u005f\u0060\u0061\u0062\u0063\u08e4\u08e5\u08e8\u08e9\u08ec\u08ef" +
|
||||
"\u08f0\u08f3\u08f6\u08f9\u08fc\u08ff\u0902\u0064\u0067\u0068\u006b\u006e\u006f\u0905\u0072\u0075\u0078\u007b\u007e\u0007\u0858\u0859\u0050\u0054\u085a\u059d\u000f\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u000e\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0005\u0899\u089a\\\u005d\u0003\u08da\u004e\u0003\u089f\u0053\u0007\u08bd\u08be\u08bf\u004b\u004f" +
|
||||
"\u0051\u0005\u08c5\u08c6\u0059\u005a\u0003\u08ca\u0055\u0005\u08d0\u08d1\u0052\u005b\u0005\u08c5\u08c6\u0059\u005a\u0003\u08da\u004e\u0017\u0d2e\u0d2f\u1665\u0d31\u0d32\u0d34\u0d35\u0d36\u0d38\u0d39\u0d3a\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0001\u0002\u1668\u0001\u0003\u0fcc\u10a5\u0002\u08dd\u0002\u08de\u0002\u08df\u0003\u07b1\u08e0\u0002\u08e1\u0003\u07b2\u08e2\u0002\u08e3\u0002\u005e\u0002\u005f" +
|
||||
"\u0002\u0060\u000e\u059b\u1690\u16b3\u16c0\u16cd\u16f0\u1713\u1727\u1728\u1729\u172a\u172b\u172c\u0006\u1696\u16a3\u16b0\u16b1\u16b2\u0007\u169d\u169e\u169f\u16a0\u16a1\u16a2\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0006\u16d3\u16e0\u16ed\u16ee\u16ef\u0007\u16da\u16db\u16dc\u16dd\u16de\u16df\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16e7\u16e8\u16e9\u16ea\u16eb\u16ec\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u16f6\u1703\u1710\u1711\u1712\u0007\u16fd\u16fe\u16ff\u1700\u1701\u1702\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u170a\u170b\u170c\u170d\u170e\u170f\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0006\u1719\u1720\u16b0\u16b1\u16b2\u0007\u169d\u169e\u169f\u16a0\u16a1\u16a2\u0007\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0062\u0003\u059c\u0063\u0002\u08e4\u0007\u07b3\u07b3\u1210\u173b\u173b\u1210\u0001\u0005\u1741\u1741\u1743\u1743\u0002\u0000\u0002\u0000\u000f\u1754\u1755\u1756\u1757\u1759\u1754\u1755\u1756\u1757\u1759\u175a\u175a\u1756\u1756\u0001\u0001\u0001\u0002" +
|
||||
"\u0000\u0001\u0002\u0000\u000b\u0d3c\u0dfe\u0e48\u0e48\u1767\u1768\u0e48\u1767\u1768\u0e48\u0001\u0001\r\u1776\u1776\u1778\u1779\u1776\u1776\u1778\u1779\u1776\u1776\u177b\u177b\u0002\u0000\u0001\u0002\u0000\u0001\u0002\u0efa\u0002\u0dff\u0004\u0efe\u1784\u1784\u0001\u0005\u1210\u173b\u1210\u173b\u0002\u0e00\u000b\u0d3d\u0e01\u1767\u1797\u1768\u1797\u1767\u1797\u1768\u1797\u0001\u0005\u0d3e\u0e02\u179d\u179d\u0001\u0002\u0e03" +
|
||||
"\u0007\u0d3f\u17a7\u1012\u17a8\u1012\u17a8\u0001\u0001\u0005\u17ae\u17af\u17ae\u17af\u0001\u0001\u0003\u17b3\u17b3\u0001\u0002\u0efa\u0009\u17bf\u17bf\u17c0\u17c1\u17bf\u17c0\u17c1\u17bf\u0001\u0001\u0001\u000b\u173b\u10b7\u173b\u10b7\u1217\u173b\u10b7\u1217\u173b\u10b7\u0003\u0d40\u0e04\u0002\u0e05\u0003\u17d5\u17d5\u0001\u0011\u07b3\u1741\u17e7\u17e8\u17e9\u07b5\u1743\u17ea\u1741\u17e7\u17e8\u17e9\u07b5\u1743\u17ea\u07b3\u0001" +
|
||||
"\u0001\u0001\u0002\u0000\u0009\u17f5\u17f6\u17f7\u1806\u1807\u1809\u180a\u1806\u0001\u0001\u000f\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0002\u0000\u0001\u0002\u0000\u0002\u180e\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0d41\u0e06\u0e4a\u182e\u182f\u0e4a\u0001\u0001" +
|
||||
"\u0007\u1837\u1839\u183a\u1837\u1837\u183c\u0002\u0000\u0001\u0002\u0000\u0001\u0002\u183f\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u0e07\u0003\u0efe\u1854\u0001\u0003\u0334\u1858\u0001\u0003\u0e08\u185c\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0d42\u0e09\u182e\u1873\u182f\u1873\u0001\u0004\u0d43\u0e0a\u1878" +
|
||||
"\u0001\u0002\u0e0b\u0005\u0d44\u1880\u1881\u1891\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0004\u1896\u1897\u1898\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u102c\u18b4\u0001\u0005\u18ba\u18bb\u18bc\u18ba\u0001\u0001\u0001\u0007\u1858\u10bc" +
|
||||
"\u0335\u18c4\u1858\u10bc\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u0d45\u0e0c\u18db\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000f\u0e0d\u17f7\u180e\u183f\u185c\u1881\u1898\u18c4\u18db\u1903\u1915\u192e\u185c\u193e\u0012\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u1950" +
|
||||
"\u0001\u0002\u1915\n\u195d\u195f\u1960\u1961\u07be\u192e\u1962\u1964\u07bf\u0002\u0000\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0005\u07bf\u07bf\u1858\u0334\u0004\u195d\u185c\u1962\u0003\u17ae\u17ae\u0002\u1896\u0002\u193e\u0002\u1978\u0002\u0000\u0003\u197d\u197f\u0002\u0000\u0002\u0000\u0002\u1983\u0002\u0000\u0002\u1987\u0002\u0000\u0017\u19a0\u19a2\u19a4\u19a6\u19a8\u19aa\u19ac\u19ae\u19b0\u19b2\u19b4\u19b6\u19b8\u19ba" +
|
||||
"\u19bc\u19be\u19c0\u19c2\u19c4\u19c6\u19c8\u19ca\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0004\u19d0\u19d2\u19d4\u0002\u0000\u0002\u0000\u0002\u0000\u0004\u19da\u19de\u19dc\u0002\u19dc\u0002\u0000\u0002\u0000" +
|
||||
"\u0004\u19e4\u19e8\u19e6\u0002\u19e6\u0002\u0000\u0002\u0000\u0008\u19f2\u1a07\u1a08\u1a09\u1a0a\u1a09\u1a0a\u0015\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0015\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0005\u1a24\u1a25\u1a26\u1a27\u0001\u0001\u0001\u0001" +
|
||||
"\u0002\u1a2a\u0001\u0002\u1a2d\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u1a25\u1a41\u1a42\u0001\u0001\u0002\u1a45\u0001\u0002\u1a26\n\u1a52\u1a53\u1a54\u1a55\u1a56\u1a6c\u1a55\u1a56\u1a6c\u0001\u0001\u0001\u0001\u0016\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0016\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u1a85\u1a85\u0001\u0002\u1a88\u0001\u0002\u1a8b\u0001\u0004\u1a90\u1aa2\u1aa2\u0012\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0006\u1a27\u1aa9\u1abc\u1acf\u1acf\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0009\u1aeb\u1aec\u1a53\u1aa9\u1aed\u1afd\u1afe\u1afe\u0001\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\n" +
|
||||
"\u1b09\u1a54\u1a88\u1abc\u1b0a\u1afd\u1a08\u1afd\u1a08\u0001\u0011\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\u1b25\u1b26\u1a55\u1acf\u1b27\u1afe\u1a09\u1afe\u1a09\u0001\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u1b39\u0001\u0007\u1a07\u1a54\u1a55\u1b41\u1b42\u1a53\u0001\u0001\u0002\u1a52\u0009\u1a0a\u1a42" +
|
||||
"\u1a56\u1aa2\u1afd\u1afe\u1b4e\u1afe\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u1a6c\u1a08\u1a09\u1b67\u1a09\u001c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\u1b25\u1b26\u1a55\u1acf\u1afe\u1a09\u1b27\u1afe\u1a09\u0009\u1a0a\u1a42" +
|
||||
"\u1a56\u1aa2\u1afd\u1afe\u1afe\u1b4e\u0006\u1a6c\u1a08\u1a09\u1a09\u1b67\u000c\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u1bb7\u1bb8\u1bb9\u0001\u0001\u0001\u0003\u1bbd\u1bbe\u0001\u0001\u0007\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1bcf\u1bd0\u0001\u0001\u0004\u1bd5\u1bd6\u1bd7\u0001\u0001\u0001" +
|
||||
"\u0003\u1bdb\u1bdc\u0001\u0001\u0002\u1bdf\u0001\u0002\u1be2\u0001\u0002\u1be5\u0001\u0002\u1be8\u0001\u0004\u1bed\u1bee\u1bef\u0001\u0001\u0001\u0002\u1bf2\u0001\u0003\u1bf6\u1bf7\u0001\u0001\u0002\u1bfa\u0001\u0004\u1bff\u1c00\u1c01\u0001\u0001\u0001\u0002\u1c04\u0001\u0004\u1c09\u1c0a\u1c0b\u0001\u0001\u0001\u0002\u1c0e\u0001\u0002\u1c11\u0001\u0002\u1c14\u0001\u0002\u1c17\u0001\u0002\u1c1a\u0001\u0002\u1c1d\u0001\u0002\u1c20" +
|
||||
"\u0001\u0002\u1c23\u0001\u0002\u1c26\u0001\u0006\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u0001\u0001\u0001\u0001\u0001\u0002\u1c34\u0001\u0002\u1c37\u0001\u0005\u1c3d\u1c3e\u1c3f\u1c40\u0001\u0001\u0001\u0001\u0003\u1c44\u1c45\u0001\u0001\u0005\u1c4b\u1c4c\u1c4d\u1c4e\u0001\u0001\u0001\u0001\u0003\u1c52\u1c53\u0001\u0001\u0011\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1c70\u0011\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1c8f\u000c\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0003\u1cb4\u1cb5\u0001\u0001\u0003\u1cb9\u1cba\u0001\u0001\u0003\u1cbe\u1cbf\u0001\u0001\u0002\u1cc2\u0001\u0006\u1c71\u1c72\u1c73\u1c6a\u1c74\u0002\u1c97\u0002\u1ccd\u0001\u0002\u1cd0\u0001\u0002\u1cd3\u0001\u0002\u1cd6\u0001\u0004\u1cdb\u1cdc\u1cdd\u0001\u0001\u0001\u0004\u1ce2\u1ce3\u1ce4\u0001\u0001\u0001\u0005\u1cea\u1ceb\u1cec\u1ced\u0001\u0001\u0001\u0001\u0004\u1cf2\u1cf3\u1cf4\u0001\u0001\u0001\u0009\u1cfe\u1cff" +
|
||||
"\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1cff\u0006\u1d0e\u1d0f\u1d10\u1d11\u1d12\u0001\u0001\u0001\u0001\u0001\u0009\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1d23\u1d1f\u0008\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u1d3a\u1d3b\u1d3c\u0001\u0001\u0001\u0002\u1d3f\u0001" +
|
||||
"\u0007\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u1d52\u1d53\u1d54\u1d55\u0001\u0001\u0001\u0001\u0005\u1d5b\u1d5c\u1d5d\u1d5e\u0001\u0001\u0001\u0001\u0002\u1d5e\u0008\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1d73\u1d74\u0001\u0001\u0015\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1d9b" +
|
||||
"\u1d9c\u1d9d\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1d8b\u0003\u1d98\u1d99\u0004\u1d91\u1d9b\u1d9a\u0002\u1d93\n\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u1dc2\u1dc3\u1dc4\u1dc5\u1dc6\u0001\u0001\u0001\u0001\u0001\u0006\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0005\u1dd7\u1dd8\u1dd9\u1dda\u0001\u0001\u0001\u0001\u0003\u1dde\u1ddf\u0001\u0001\u0005\u1de5\u1de6\u1de7\u1de8\u0001\u0001\u0001\u0001\u0002\u1deb\u0001\u0002\u1db7\u0002\u1dd8\u0003\u1df3\u1df4\u0001\u0001\u0003\u14a2\u1df8\u0001\u0003\u0d84\u1dfc\u0001\u0003\u14a3\u1e00\u0001\u0003\u0d85\u1e04\u0001\u0002\u1e07\u0001\u0004\u1224\u14a4\u1e0c\u0001\u0005\u14a5\u151f\u05ab\u1e12\u0001\u0002\u1e15\u0001\u0002\u1225" +
|
||||
"\u0002\u1e1a\u0001\u0008\u1226\u059a\u1e23\u1e24\u1e25\u1e26\u1e27\u0001\u0001\u0001\u0001\u0001\u0002\u1e2a\u0001\u0002\u1e2d\u0001\u0004\u1520\u0ecc\u1e32\u0001\u0004\u1521\u0ecc\u1e37\u0001\u0002\u1e23\u0002\u14a6\u0002\u1e3e\u0001\u0002\u1e41\u0001\u0002\u1e44\u0001\u0002\u1e47\u0001\u0003\u1227\u1e4b\u0001\u0002\u1227\u0002\u1e50\u0001\u0002\u1e53\u0001\u0002\u1e56\u0001\u0002\u1e59\u0001\u0002\u1e5c\u0001\u0002\u1e5f\u0001" +
|
||||
"\u0002\u1e62\u0001\u0002\u1e65\u0001\u0002\u1e68\u0001\u0002\u1e6b\u0001\u0004\u1e70\u1228\u14a7\u0001\u0003\u1e74\u1229\u0001\u0002\u1e77\u0001\u0002\u1e7a\u0001\u0002\u1e7d\u0001\u0002\u1e80\u0001\u0002\u1e83\u0001\u0005\u153b\u158b\u1e24\u1e89\u0001\u0004\u122a\u1e25\u1e89\u0002\u1e90\u0001\u0002\u1e93\u0001\u0002\u1e96\u0001\u0002\u1e99\u0001\u0002\u1e9c\u0001\u0002\u1e9f\u0001\u0002\u1ea2\u0001\u0003\u122b\u1ea6\u0001\u0002" +
|
||||
"\u122c\u0002\u122d\u0002\u122e\u0016\u0908\u0e50\u0e92\u0ed6\u0f09\u0f28\u0f67\u10f1\u1668\u1df8\u1dfc\u1e00\u1e04\u1e0c\u1e12\u1e26\u1e32\u1e37\u1e4b\u1ea6\u1ec3\u0001\n\u0cf0\u0d86\u0e0e\u10f2\u122f\u14a8\u153c\u1e27\u1ec3\u0002\u1ed0\u0001\u0104\u06fe\u0739\u0799\u07d3\u089b\u079a\u079b\u0351\u0de0\u0d6e\u0de1\u1fd5\u1fd9\u1fdc\u1fdf\u1fe2\u1fe5\u1fe8\u1feb\u1fee\u1ff1\u1ff4\u0eca\u1519\u0f21\u0f51\u08a0\u08a1\u0d2c\u0d6d" +
|
||||
"\u132b\u1ff9\u0e43\u0efa\u1ffa\u0d7f\u1410\u0efe\u0eff\u0e45\u0f03\u1ffb\u0de3\u0755\u0de5\u1ffc\u1037\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u200e\u200f\u2010\u2011\u2012\u1806\u182e\u1854\u1806\u2013\u2015\u2016\u0e0a\u2017\u2018\u2019\u201a\u201b\u18bc\u201d\u18ba\u18b4\u201e\u201f\u2021\u2022\u195d\u1837\u2023\u10bb\u2024\u0e0d\u180a\u0e09\u2025\u182f\u1839\u1880\u0e0a\u203e\u204a\u2051\u2057\u205e\u2064" +
|
||||
"\u2069\u206f\u207b\u2081\u2087\u208e\u209b\u1a56\u1a6c\u1b39\u20a1\u1aed\u1a24\u20a2\u20a3\u1a41\u1a42\u20a4\u1a45\u20a5\u20a5\u20a6\u20a7\u1a85\u1b67\u20a8\u20a9\u20aa\u1aa2\u20ab\u20ac\u20ad\u1a88\u20ae\u1afd\u1afe\u1fd5\u1fd9\u1fdc\u1fdf\u1fe2\u1fe5\u1fe8\u1feb\u1fee\u1ff1\u20a6\u1b0a\u20af\u20b0\u1b4e\u1b67\u20b1\u1b27\u1fd5\u1fd9\u1fdc\u1fdf\u1fe2\u1fe5\u1fe8\u1feb\u1fee\u1ff1\u20b2\u20b3\u20b4\u20b5\u20b6\u20b7\u20b8\u20b9" +
|
||||
"\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u1ba8\u1ba9\u1baa\u20c0\u20c1\u20c2\u1bab\u20c6\u20c7\u1bac\u1bad\u20c8\u20c9\u20cd\u1bae\u20d1\u1baf\u20d5\u1bb0\u1bb1\u1bb2\u20d6\u20d7\u20db\u20df\u20e0\u20e1\u20e2\u20e3\u20e4\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u20ec\u20ed\u20ee\u20ef\u20f1\u20f3\u20f5\u20f7\u14a0\u1ffc\u1047\u0ef8\u0f04\u0ef9\u0f07\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u1e07\u1224\u05ab\u20ff\u2100\u2101\u2102\u2103" +
|
||||
"\u1228\u1229\u153b\u2104\u1ea6\u14a7\u122d\u122e\u0908\u183c\u2105\u2106\u2107\u0004\u0000\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0005\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0007" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0005\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u000c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0007\u0000\u0000\u0000\u0000\u0000\u0000\r\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0000\u0000\u0000\u0001\u0001\u0001\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0001\u0001\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000b\u212b\u212c\u212d\u212e\u212f\u2130\u2131\u2132\u2133\u2134\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u000b\u2140\u2141\u2142\u2143\u2144\u2145\u2146\u2147\u2148\u2149\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u217f\u2180\u2181" +
|
||||
"\u2182\u2183\u2184\u2185\u2186\u2187\u2188\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2194\u2195\u2196\u2197\u2198\u2199\u219a\u219b\u219c\u219d\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u21a9\u21aa\u21ab\u21ac\u21ad\u21ae\u21af\u21b0\u21b1\u21b2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0071\u1055\u0dee\u2224\u2227\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231" +
|
||||
"\u20ba\u20bb\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u20e5\u20b4\u2244\u20b5\u2245\u2246\u20ec\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u1047\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226e\u0424\u2270\u2273\u2276\u2279" +
|
||||
"\u227a\u1047\u0ef8\u0f04\u0ef9\u0f07\u20fb\u20fc\u20fd\u20fe\u1e15\u1e1a\u1e70\u1e74\u1e77\u1e7a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u228c\u228f\u2292\u2295\u2298\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000" +
|
||||
"\u0003\u0000\u0000\u03b4\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u0415\u2666\u2667\u0415\u2651\u265a\u2666\u264f\u2663\u265d\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u2651\u2653\u2653\u2653\u2653\u2657\u2657\u2657\u2657\u265c\u265d\u265d\u265d\u265d\u265d\u265d\u2663\u2663\u2663\u2663\u2666\u264f\u264f\u264f\u264f\u264f\u264f\u264f" +
|
||||
"\u2651\u2653\u2653\u2653\u2653\u2657\u2657\u2657\u2657\u265c\u265d\u265d\u265d\u265d\u265d\u265d\u2663\u2663\u2663\u2663\u2666\u2666\u264f\u264f\u264f\u264f\u2651\u2651\u2651\u2651\u2651\u2651\u2651\u2651\u2652\u2652\u2652\u2652\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2655\u2655\u2655\u2655\u2655\u2655\u2656\u2656\u2656\u2656\u2657\u2657\u2657\u2657\u2657\u2657\u2657\u2657\u2658\u2658\u2659\u2659\u265a\u265a\u265a\u265a" +
|
||||
"\u265a\u265a\u265a\u265a\u265c\u265c\u265c\u265c\u265c\u265c\u265d\u265d\u265d\u265d\u265d\u265d\u2660\u2660\u2660\u2660\u2660\u2660\u2661\u2661\u0184\u0184\u2661\u2661\u2661\u2661\u2662\u2662\u2662\u2662\u2662\u2662\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2665\u2665\u2666\u2666\u2666\u2667\u2667\u2667\u2667\u2667\u2667\u2650\u2668\u2657\u265a\u265d\u265d\u265d\u2660\u2663\u2663\u2667\u2667\u264f\u264f\u2657" +
|
||||
"\u2657\u265d\u265d\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u264f\u264f\u264f\u264f\u264f\u264f\u2655\u2655\u2655\u2655\u2659\u2659\u265d\u265d\u265d\u265d\u2669\u2669\u2658\u2655\u2655\u265c\u265c\u264f\u264f\u264f\u264f\u265d\u265d\u264f\u264f\u2653\u2653\u2657\u2657\u265d\u265d\u2660\u2660\u2663\u2663\u2656\u2656\u264f\u264f\u2653\u2653\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u2666\u2666\u2658\u264f" +
|
||||
"\u2651\u2651\u265a\u2662\u2650\u2663\u2653\u2653\u2658\u2658\u2660\u2660\u2666\u2666\u2658\u2655\u2668\u2657\u2657\u265c\u265d\u265d\u2660\u2663\u2666\u2650\u266a\u2656\u265a\u2656\u266b\u2658\u2660\u266c\u266d\u042a\u2665\u2666\u2668\u265a\u2661\u0415\u266e\u266f\u2670\u2671\u2672\u2673\u2674\u266f\u2675\u2676\u2663\u2677\u2678\u2679\u267a\u2671\u2673\u266e\u266f\u2670\u2671\u2673\u2679\u2671\u2673\u2672\u2673\u2674\u2675\u2673" +
|
||||
"\u2673\u267a\u2677\u2676\u2678\u2675\u266f\u267b\u1837\u267c\u267d\u267e\u2023\u10bb\u2024\u267f\u2023\u0e0d\u2015\u2016\u18ba\u201e\u201f\u2021\u195d\u2015\u2016\u18ba\u201e\u201f\u2021\u195d\u267b\u1837\u267c\u267d\u267e\u2023\u10bb\u2024\u267f\u2023\u0e0d\u267b\u267b\u2680\u2680\u2681\u2681\u2682\u2682\u2683\u2683\u1858\u1858\u2684\u2684\u0e09\u0e09\u201e\u201e\u267c\u267c\u2685\u2685\u182f\u182f\u2686\u2686\u1839\u1839\u2687" +
|
||||
"\u2687\u2688\u2688\u1809\u1809\u2689\u2689\u268a\u268a\u2023\u2023\u2023\u2023\u2684\u2684\u2684\u2684\u2684\u2684\u267d\u267d\u268b\u268b\u268b\u268b\u268b\u268b\u2024\u2024\u2105\u2105\u2687\u2687\u0d44\u0d44\u268c\u268c\u268d\u268e\u268f\u2690\u2690\u2691\u2692\u2693\u2694\u20aa\u20ad\u20ae\u2695\u1a26\u2696\u2692\u2697\u2698\u20af\u2693\u2699\u269a\u20b0\u269b\u20b1\u269c\u269d\u269e\u269f\u26a0\u26a1\u26a1\u26a1\u269e\u26a2" +
|
||||
"\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26a9\u26ac\u26ad\u26ae\u26ae\u26af\u26a3\u26a3\u26a4\u26a6\u26a8\u26a8\u26b0\u26b1\u26b2\u26b3\u269e\u269f\u269f\u26a2\u26a0\u26a0\u26a7\u26a6\u26b4\u26a4\u26a5\u26b5\u26a9\u26ac\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u264f\u264f\u2650\u2651\u2652\u1395\u2653\u2658\u2659\u265a\u265b\u13f5" +
|
||||
"\u265d\u26ca\u26cb\u265e\u2106\u266c\u2662\u2663\u2664\u2665\u2667\u2669\u2668\u26cc\u2677\u2678\u26cd\u268c\u264f\u26ce\u26cf\u2650\u2652\u2653\u13a8\u26d0\u26d1\u2655\u26d2\u2659\u265b\u13f5\u265d\u26ca\u265e\u2662\u2663\u26d3\u26d4\u2664\u2674\u26d5\u267a\u26d6\u2657\u2660\u2663\u2664\u2674\u2668\u2678\u267a\u26d6\u2657\u265e\u2663\u26d7\u2651\u26d8\u26d9\u2654\u2658\u26da\u2657\u2671\u26db\u26dc\u265d\u26dd\u26de\u2663\u26df" +
|
||||
"\u26e0\u2667\u2669\u2675\u2650\u2650\u2650\u2650\u2651\u2651\u2652\u2652\u2652\u2652\u2652\u2652\u2653\u2653\u2653\u2653\u10a4\u10a4\u2654\u2654\u2655\u2655\u2656\u2656\u2656\u2656\u2656\u2656\u2656\u2656\u2657\u2657\u2659\u2659\u2659\u2659\u265a\u265a\u265a\u265a\u265b\u265b\u265b\u265b\u265b\u265b\u265c\u265c\u265c\u265c\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265e\u265e\u265e\u265e\u2660\u2660\u2660\u2660\u2660\u2660" +
|
||||
"\u2661\u2661\u2661\u2661\u2661\u2661\u2661\u2661\u2661\u2661\u2662\u2662\u2662\u2662\u2663\u2663\u2664\u2664\u2664\u2664\u2665\u2665\u2665\u2665\u2665\u2665\u2665\u2665\u2665\u2665\u0415\u0415\u0415\u0415\u2666\u2666\u2667\u2667\u2667\u2667\u2662\u2665\u2666\u13b0\u142b\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u264f\u10a3\u10a3\u10a3\u10a3\u10a3\u10a3\u10a3\u10a3\u10a3\u10a3\u2653\u2653\u2653" +
|
||||
"\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2653\u2657\u2657\u2657\u2657\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u265d\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2663\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u266e\u266f\u2670\u2671\u2672\u2673\u26e1\u266e\u266e" +
|
||||
"\u266e\u266f\u2670\u2671\u2671\u2671\u2673\u2673\u151d\u2673\u2673\u2672\u26e1\u2657\u265c\u264f\u2653\u265d\u0415\u13a8\u2656\u2659\u265a\u265b\u265c\u265e\u2661\u2662\u2653\u2650\u2656\u2660\u265e\u2667\u2677\u2679\u2650\u265e\u264f\u2662\u26e2\u2658\u268a\u268a\u183c\u183c\u268b\u268b\u17f6\u17f6\u2105\u2105\u2106\u2106\u2654\u2661\u2659\u2659\u2659\u2659\u265f\u265f\u2664\u2664\u26e3\u2651\u2651\u2654\u2654\u2655\u2655\u265c" +
|
||||
"\u265c\u2660\u2660\u2661\u2661\u2657\u265f\u2663\u2663\u265f\u265d\u26d4\u26ca\u26e1\u26e5\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0149\u06f5\u0706\u0752\u07be\u07cd\u07d0\u0905\u0cbd\u0cde\u0ced\u0cf8\u0d44\u0d83\u0e0b\u0e4d\u0e53\u0e69\u0e6f\u0e77\u0e7a\u0e7f\u0e82\u0e85\u0e8f\u0ea6\u0ed3\u0f06\u0f25\u0f64\u0f7e\u0f82\u0f93\u0f9b\u0fb0\u0fbe\u0fd1\u0fd3\u0fe6\u0fe9\u0fec\u0ffe\u1000\u1010\u102d\u1035\u1039\u1044\u1053\u1063\u10b9\u10c0\u10c4\u10c6\u10d0\u10d7\u10da\u10ef\u10f1\u1185" +
|
||||
"\u122c\u1315\u1328\u1352\u1389\u1393\u13a5\u13b3\u13c6\u13c8\u13d8\u13db\u13df\u13e7\u13eb\u13f2\u140d\u1417\u141c\u1428\u1435\u1450\u1474\u1476\u147a\u1482\u1486\u1489\u14a5\u151e\u1539\u1588\u164a\u1662\u1665\u1668\u166a\u166c\u166e\u1671\u1673\u1676\u1678\u167a\u167c\u167e\u1729\u172b\u172e\u1730\u1738\u1741\u1758\u1765\u1778\u177a\u177c\u1781\u1786\u1788\u1794\u179a\u179c\u17a5\u17ac\u17b0\u17b2\u17be\u17c9\u17cc\u17ce\u17d2" +
|
||||
"\u17e8\u1808\u1823\u182c\u1839\u184b\u184d\u1851\u1855\u1868\u1870\u1875\u1877\u188e\u18ad\u18b1\u18b9\u18d3\u18f0\u194a\u194d\u194f\u1962\u1967\u196b\u196e\u1970\u1972\u1976\u197d\u1981\u1985\u19c8\u19d2\u19dc\u19e6\u1a1b\u1a24\u1a27\u1a39\u1a3f\u1a42\u1a44\u1a7e\u1a82\u1a85\u1a88\u1a9f\u1ade\u1afb\u1b17\u1b33\u1b36\u1b3f\u1b41\u1b5d\u1b7f\u1b89\u1b92\u1b98\u1baf\u1bb6\u1bbb\u1bc8\u1bcd\u1bd4\u1bd9\u1bdc\u1bdf\u1be2\u1be5\u1bec" +
|
||||
"\u1bef\u1bf4\u1bf7\u1bfe\u1c01\u1c08\u1c0b\u1c0e\u1c11\u1c14\u1c17\u1c1a\u1c1d\u1c20\u1c23\u1c2e\u1c31\u1c34\u1c3d\u1c42\u1c4b\u1c50\u1c71\u1c73\u1c94\u1c96\u1cad\u1cb2\u1cb7\u1cbc\u1cbf\u1cc5\u1cc7\u1cca\u1ccd\u1cd0\u1cd3\u1cda\u1ce1\u1cea\u1cf1\u1d02\u1d04\u1d0f\u1d20\u1d23\u1d32\u1d39\u1d3c\u1d49\u1d52\u1d5b\u1d5d\u1d6c\u1d71\u1d9a\u1d9c\u1d9f\u1da3\u1da5\u1db8\u1dc3\u1dce\u1dd7\u1ddc\u1de5\u1de8\u1dea\u1dec\u1df1\u1df5\u1df9" +
|
||||
"\u1dfd\u1e01\u1e04\u1e09\u1e0f\u1e12\u1e14\u1e17\u1e24\u1e27\u1e2a\u1e2f\u1e34\u1e36\u1e38\u1e3b\u1e3e\u1e41\u1e44\u1e48\u1e4a\u1e4d\u1e50\u1e53\u1e56\u1e59\u1e5c\u1e5f\u1e62\u1e65\u1e68\u1e6d\u1e71\u1e74\u1e77\u1e7a\u1e7d\u1e80\u1e86\u1e8a\u1e8d\u1e90\u1e93\u1e96\u1e99\u1e9c\u1e9f\u1ea3\u1ea5\u1ea7\u1ea9\u1ec0\u1eca\u0009\u06fe\u06ff\u0700\u0701\u0702\u0703\u0704\u0705\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u002f\u0735" +
|
||||
"\u0736\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u074b\u074c\u074d\u074e\u074f\u0750\u0751\u023e\u023f\u0240\u0241\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0250\u073b\u0252\u073c\u0253\u0001\u0005\u073b\u073c\u0252\u0253\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0041\u0793\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u057a\u057e\u057f\u0580\u0590\u0594\u0595\u0596\u0597\u0598\u07a6\u07aa\u0794\u07a9\u0548\u07a4\u07a5\u07ad\u07ae\u059b\u059c\u059e\u07af\u07b0\u07b1\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u059f\u07ba\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u07bb\u07bc\u05a8\u07bd\u00cf\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u07a4\u07a5\u0001\u0001\u0003\u07a9\u0548\u0001\u0003\u07a4\u07a5\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u07cf\u0001\u007b\u084b\u0857\u0858\u0875\u0890\u0897\u0898\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3" +
|
||||
"\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08bc\u0037\u08c3\u0038\u0039\u003a\u003b\u003c\u003f\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u08c7\u08ce\u08d3\u08b9\u08d7\u08d8\u08d6\u08ba\u0854\u08bb\u08cc\u089b\u0855\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u08c6\u0055\u08c1\u0059\u08c2\u005a\u08cd\u005b\u0895\\\u0896\u005d\u08d9\u08da\u08db\u08dc" +
|
||||
"\u08dd\u08de\u08df\u005e\u005f\u0060\u0061\u0062\u0063\u08e0\u08e1\u08e4\u08e5\u08e8\u08eb\u08ec\u08ef\u08f2\u08f5\u08f8\u08fb\u08fe\u0064\u0067\u0068\u006b\u006e\u006f\u0901\u0072\u0075\u0078\u007b\u007e\u0904\u0008\u0853\u0854\u0855\u0050\u0054\u0856\u059d\u0001\u0001\u0001\u0001\u0001\u000f\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u000e\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0895\u0896\\\u005d\u0001\u0001\u0001\u0003\u089b\u0053\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u08b9\u08ba\u08bb\u004b\u004f" +
|
||||
"\u0051\u0001\u0001\u0001\u0005\u08c1\u08c2\u0059\u005a\u0001\u0001\u0003\u08c6\u0055\u0001\u0005\u08cc\u08cd\u0052\u005b\u0001\u0001\u0005\u08c1\u08c2\u0059\u005a\u0003\u08d6\u004e\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000" +
|
||||
"\u0000\u0003\u0000\u0000\u0001\u00c1\u06ff\u09c6\u09c7\u09cc\u09cd\u09d0\u09fb\u0a26\u0a51\u0a7c\u0a83\u0a86\u0a89\u0a8c\u0a8f\u0a92\u0a95\u0a98\u0a9b\u0a9e\u0aa1\u0aa4\u0aa7\u0aaa\u0aad\u0ab0\u0ab3\u0ab6\u0ab9\u0abc\u0abf\u0ac2\u0ac5\u0ac8\u0acb\u0ace\u0ad1\u0ad4\u0ad7\u0ada\u0add\u0ae0\u0ae3\u0ae6\u0ae9\u0aec\u0aef\u0af2\u0af5\u0af8\u0afb\u0afe\u0b01\u0b04\u0b07\u0b0a\u0b0d\u0b10\u0b13\u0b16\u0b19\u0b1c\u0b1f\u0b22\u0b25\u0b28" +
|
||||
"\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b31\u0b32\u0b33\u0b34\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3a\u0b3b\u0b42\u0b49\u0b50\u0b57\u0b5e\u0b65\u0b6c\u0b73\u0b7a\u0b81\u0b88\u0b8f\u0b96\u0b9d\u0ba0\u0ba3\u0ba6\u0ba9\u0bac\u0baf\u0bb2\u0bb5\u0bb8\u0bbb\u0bbe\u0bc1\u0bc4\u0bc7\u0bca\u0bcd\u0bd0\u0bd3\u0bd6\u0bd9\u0bdc\u0bdf\u0be2\u0be5\u0be8\u0beb\u0bee\u0bf1\u0bf4\u0bf7\u0bfa\u0bfd\u0c00\u0c03\u0c06\u0c09\u0c0c\u0c0f\u0c12\u0c15\u0c18" +
|
||||
"\u0c1b\u0c1e\u0c21\u0c24\u0c27\u0c2a\u0c2d\u0c30\u0c33\u0c36\u0c39\u0c3c\u0c3f\u0c42\u0c45\u0c48\u0c4b\u0c4e\u0c51\u0c54\u0c57\u0c5a\u0c5d\u0c60\u0c63\u0c66\u0c69\u0c6c\u0c6f\u0c72\u0c75\u0c78\u0c7b\u0c7e\u0c81\u0c84\u0c87\u0c8a\u0c8d\u0c90\u0c93\u0c96\u0c99\u0c9c\u0c9f\u0ca2\u0ca5\u0ca8\u0cab\u0cae\u0cb1\u0cb4\u0cb7\u0cba\u0001\u0002\u09c9\u0003\u0000\u0000\u0001\u0002\u09cf\u0001\u000c\u09dc\u09dd\u09e0\u09e3\u09e6\u09e9\u09ec" +
|
||||
"\u09ef\u09f2\u09f5\u09f8\u0001\u0002\u09df\u0001\u0002\u09e2\u0001\u0002\u09e5\u0001\u0002\u09e8\u0001\u0002\u09eb\u0001\u0002\u09ee\u0001\u0002\u09f1\u0001\u0002\u09f4\u0001\u0002\u09f7\u0001\u0002\u09fa\u0001\u000c\u0a07\u0a08\u0a0b\u0a0e\u0a11\u0a14\u0a17\u0a1a\u0a1d\u0a20\u0a23\u0001\u0002\u0a0a\u0001\u0002\u0a0d\u0001\u0002\u0a10\u0001\u0002\u0a13\u0001\u0002\u0a16\u0001\u0002\u0a19\u0001\u0002\u0a1c\u0001\u0002\u0a1f\u0001" +
|
||||
"\u0002\u0a22\u0001\u0002\u0a25\u0001\u000c\u0a32\u0a33\u0a36\u0a39\u0a3c\u0a3f\u0a42\u0a45\u0a48\u0a4b\u0a4e\u0001\u0002\u0a35\u0001\u0002\u0a38\u0001\u0002\u0a3b\u0001\u0002\u0a3e\u0001\u0002\u0a41\u0001\u0002\u0a44\u0001\u0002\u0a47\u0001\u0002\u0a4a\u0001\u0002\u0a4d\u0001\u0002\u0a50\u0001\u000c\u0a5d\u0a5e\u0a61\u0a64\u0a67\u0a6a\u0a6d\u0a70\u0a73\u0a76\u0a79\u0001\u0002\u0a60\u0001\u0002\u0a63\u0001\u0002\u0a66\u0001\u0002" +
|
||||
"\u0a69\u0001\u0002\u0a6c\u0001\u0002\u0a6f\u0001\u0002\u0a72\u0001\u0002\u0a75\u0001\u0002\u0a78\u0001\u0002\u0a7b\u0001\u0003\u0a7f\u0a80\u0001\u0002\u0a82\u0001\u0002\u0a85\u0001\u0002\u0a88\u0001\u0002\u0a8b\u0001\u0002\u0a8e\u0001\u0002\u0a91\u0001\u0002\u0a94\u0001\u0002\u0a97\u0001\u0002\u0a9a\u0001\u0002\u0a9d\u0001\u0002\u0aa0\u0001\u0002\u0aa3\u0001\u0002\u0aa6\u0001\u0002\u0aa9\u0001\u0002\u0aac\u0001\u0002\u0aaf\u0001" +
|
||||
"\u0002\u0ab2\u0001\u0002\u0ab5\u0001\u0002\u0ab8\u0001\u0002\u0abb\u0001\u0002\u0abe\u0001\u0002\u0ac1\u0001\u0002\u0ac4\u0001\u0002\u0ac7\u0001\u0002\u0aca\u0001\u0002\u0acd\u0001\u0002\u0ad0\u0001\u0002\u0ad3\u0001\u0002\u0ad6\u0001\u0002\u0ad9\u0001\u0002\u0adc\u0001\u0002\u0adf\u0001\u0002\u0ae2\u0001\u0002\u0ae5\u0001\u0002\u0ae8\u0001\u0002\u0aeb\u0001\u0002\u0aee\u0001\u0002\u0af1\u0001\u0002\u0af4\u0001\u0002\u0af7\u0001" +
|
||||
"\u0002\u0afa\u0001\u0002\u0afd\u0001\u0002\u0b00\u0001\u0002\u0b03\u0001\u0002\u0b06\u0001\u0002\u0b09\u0001\u0002\u0b0c\u0001\u0002\u0b0f\u0001\u0002\u0b12\u0001\u0002\u0b15\u0001\u0002\u0b18\u0001\u0002\u0b1b\u0001\u0002\u0b1e\u0001\u0002\u0b21\u0001\u0002\u0b24\u0001\u0002\u0b27\u0001\u0002\u0b2a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0b3e\u0b3f\u0001\u0002" +
|
||||
"\u0b41\u0001\u0003\u0b45\u0b46\u0001\u0002\u0b48\u0001\u0003\u0b4c\u0b4d\u0001\u0002\u0b4f\u0001\u0003\u0b53\u0b54\u0001\u0002\u0b56\u0001\u0003\u0b5a\u0b5b\u0001\u0002\u0b5d\u0001\u0003\u0b61\u0b62\u0001\u0002\u0b64\u0001\u0003\u0b68\u0b69\u0001\u0002\u0b6b\u0001\u0003\u0b6f\u0b70\u0001\u0002\u0b72\u0001\u0003\u0b76\u0b77\u0001\u0002\u0b79\u0001\u0003\u0b7d\u0b7e\u0001\u0002\u0b80\u0001\u0003\u0b84\u0b85\u0001\u0002\u0b87\u0001" +
|
||||
"\u0003\u0b8b\u0b8c\u0001\u0002\u0b8e\u0001\u0003\u0b92\u0b93\u0001\u0002\u0b95\u0001\u0003\u0b99\u0b9a\u0001\u0002\u0b9c\u0001\u0002\u0b9f\u0001\u0002\u0ba2\u0001\u0002\u0ba5\u0001\u0002\u0ba8\u0001\u0002\u0bab\u0001\u0002\u0bae\u0001\u0002\u0bb1\u0001\u0002\u0bb4\u0001\u0002\u0bb7\u0001\u0002\u0bba\u0001\u0002\u0bbd\u0001\u0002\u0bc0\u0001\u0002\u0bc3\u0001\u0002\u0bc6\u0001\u0002\u0bc9\u0001\u0002\u0bcc\u0001\u0002\u0bcf\u0001" +
|
||||
"\u0002\u0bd2\u0001\u0002\u0bd5\u0001\u0002\u0bd8\u0001\u0002\u0bdb\u0001\u0002\u0bde\u0001\u0002\u0be1\u0001\u0002\u0be4\u0001\u0002\u0be7\u0001\u0002\u0bea\u0001\u0002\u0bed\u0001\u0002\u0bf0\u0001\u0002\u0bf3\u0001\u0002\u0bf6\u0001\u0002\u0bf9\u0001\u0002\u0bfc\u0001\u0002\u0bff\u0001\u0002\u0c02\u0001\u0002\u0c05\u0001\u0002\u0c08\u0001\u0002\u0c0b\u0001\u0002\u0c0e\u0001\u0002\u0c11\u0001\u0002\u0c14\u0001\u0002\u0c17\u0001" +
|
||||
"\u0002\u0c1a\u0001\u0002\u0c1d\u0001\u0002\u0c20\u0001\u0002\u0c23\u0001\u0002\u0c26\u0001\u0002\u0c29\u0001\u0002\u0c2c\u0001\u0002\u0c2f\u0001\u0002\u0c32\u0001\u0002\u0c35\u0001\u0002\u0c38\u0001\u0002\u0c3b\u0001\u0002\u0c3e\u0001\u0002\u0c41\u0001\u0002\u0c44\u0001\u0002\u0c47\u0001\u0002\u0c4a\u0001\u0002\u0c4d\u0001\u0002\u0c50\u0001\u0002\u0c53\u0001\u0002\u0c56\u0001\u0002\u0c59\u0001\u0002\u0c5c\u0001\u0002\u0c5f\u0001" +
|
||||
"\u0002\u0c62\u0001\u0002\u0c65\u0001\u0002\u0c68\u0001\u0002\u0c6b\u0001\u0002\u0c6e\u0001\u0002\u0c71\u0001\u0002\u0c74\u0001\u0002\u0c77\u0001\u0002\u0c7a\u0001\u0002\u0c7d\u0001\u0002\u0c80\u0001\u0002\u0c83\u0001\u0002\u0c86\u0001\u0002\u0c89\u0001\u0002\u0c8c\u0001\u0002\u0c8f\u0001\u0002\u0c92\u0001\u0002\u0c95\u0001\u0002\u0c98\u0001\u0002\u0c9b\u0001\u0002\u0c9e\u0001\u0002\u0ca1\u0001\u0002\u0ca4\u0001\u0002\u0ca7\u0001" +
|
||||
"\u0002\u0caa\u0001\u0002\u0cad\u0001\u0002\u0cb0\u0001\u0002\u0cb3\u0001\u0002\u0cb6\u0001\u0002\u0cb9\u0001\u0002\u0cbc\u0001\u0011\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd5\u0cd6\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0009\u0ce7\u0351\u0cea\u0ceb\u0362\u0363\u0579\u0cec\u0003\u08d7\u004c\u0001\u0001\u0001\u0007\u0cf4\u0cf5" +
|
||||
"\u0cf6\u0cf7\u02fd\u02fe\u0001\u0001\u0001\u0001\u0030\u0700\u0795\u0897\u0700\u0d28\u0d29\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u033f\u013e\u013f\u0340\u0141\u0142\u0341\u0143\u0144\u0145\u0342\u0146\u0147\u0148\u0343\u0d38\u0d39\u0d3a\u0d3b\u0d3c\u0d3d\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0020\u0701\u09cc\u0ccf\u0cf5\u0d28\u0d64\u0d6b\u0d74\u0d75\u0095\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0705\u0d7f\u031e\u00b7\u031f\u0321\u0d7b\u0325\u0329\u0d80\u0d81\u0d82\u0004\u0d68\u0d69\u0d6a\u0001\u0001\u0001\u0003\u0d6e\u0d71\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0050\u0dd3\u0dd4\u0dd8\u0ddc\u0ddd\u0dde\u0ddf\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df2\u0df3\u0df4\u0df5\u0ddc\u01ed\u01ee\u01ef\u01f0\u01f1\u01f2\u01f3\u01f4\u01f5\u01fd\u01fe\u01ff\u0200\u0204\u0205\u0206\u020d\u020e\u020f\u0210\u0df6\u0ddb\u0081\u0df9\u012f\u0214\u07af\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0d42\u0e00\u0e01\u0e02\u0e03" +
|
||||
"\u0e04\u0e05\u0e06\u0e07\u0d43\u0e08\u0e09\u07bb\u0dd7\u0215\u0e0a\u0001\u0003\u0dd7\u0215\u0001\u0003\u0ddb\u0081\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0ddb\u0081\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0028\u0d74\u0e33\u0e33\u0e34" +
|
||||
"\u0e35\u0e36\u0d77\u0e37\u0e38\u0e39\u0e3a\u0e3b\u0e3c\u0e3d\u0e3e\u00b4\u0e3f\u00b7\u00ba\u00bb\u00bc\u0380\u0e40\u0383\u00c6\u0e41\u0e42\u00c9\u0e43\u0e44\u0e45\u0e46\u0e47\u0e48\u00ce\u0e49\u0e4a\u0e4b\u0e4c\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0351\u0e51\u0e52\u0001\u0001\u000b\u0e5e\u0e61\u0e62\u0e63" +
|
||||
"\u0e64\u0e65\u0e66\u0e67\u0e68\u04e3\u0002\u0e60\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0e6d\u0e6e\u04e4\u0001\u0001\u0005\u0e74\u0e75\u0e76\u04e5\u0001\u0001\u0001\u0002\u0e79\u0001\u0003\u0e7d\u0e7e\u0001\u0001\u0002\u0e81\u0001\u0002\u0e84\u0001\u0006\u0e8b\u0e8c\u0d75\u0e8d\u0e8e\u0001\u0001\u0001\u0001\u0011\u0ea0\u0d29\u0d2c\u0d2f\u0d33\u0ea1\u0ea2\u0d37\u0ea3\u033f\u0340\u0341\u0342\u0ea4\u0ea5\u0343" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u001e\u0703\u0796\u089c\u0093\u0ec4\u0ec5\u0ec6\u0ec7\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0ece\u0ecf\u0ed0\u0ed1\u0ec7\u0105\u0106\u0107\u010d\u010e\u0110\u0111\u0112\u0114\u0ed2\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0021\u0e34\u0ef4\u0ef5\u0ef6\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f00\u0f01\u0ef6\u0f02\u0ef6\u026b" +
|
||||
"\u0efa\u0efb\u026c\u0f03\u0eff\u0ef6\u0efa\u0ef6\u0f04\u0efa\u0270\u0f05\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0015\u0704\u0797\u089d\u0f1b\u0ec8\u0f1c\u0f1d\u0f1e\u0f1f\u0f20\u0f21\u0f22\u0f1c\u0170\u0172\u0175\u0177\u0178\u0f23\u0f24\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0020\u0f45\u0f46\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54" +
|
||||
"\u0f55\u02e7\u0f5f\u02e8\u02e9\u02ea\u02eb\u02ec\u0f5c\u0f5d\u0f5e\u0196\u0198\u0185\u0f62\u02ed\u0f4b\u02ee\u0f4c\u02ef\u0f63\u0001\u0005\u0f4b\u0f4c\u02ee\u02ef\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u0f5c\u0f5d\u0f5e\u0196\u0198\u0185\u0001\u0001\u0001\u0003\u0f62\u02ed\u0001\u0001\u0015\u0798\u089e\u0f79\u0cea\u0d29\u0d76\u0d29\u0f1e\u0cea\u0f7a\u0f7b\u0f1e\u0d76\u0f7c\u0cea\u0f7a\u0f7b\u0f7d\u0798" +
|
||||
"\u089e\u0001\u0001\u0001\u0001\u0001\u0003\u0de3\u0f81\u0001\u000c\u089f\u0d2a\u0de4\u0e36\u0ec9\u0ef6\u0f8e\u0f8f\u0f90\u0f91\u0f92\u0001\u0001\u0001\u0001\u0001\u0007\u0d2b\u0d77\u0de5\u0eca\u0f9a\u0f9a\u0001\u0012\u0799\u08a0\u0d2c\u0d78\u0de6\u0d2c\u0ecb\u0ef6\u0f1f\u0fad\u0f1f\u0d78\u0fae\u0fad\u0faf\u0799\u08a0\u0001\u0001\u0001\u0006\u0de7\u0fb6\u0fb9\u0fbb\u0fbd\u0003\u0000\u0000\u0002\u0000\u0002\u0000\u0001\n\u0fc8" +
|
||||
"\u0d2d\u0de8\u0fc9\u0fc8\u0fca\u0fc8\u0fd0\u0fc8\u0001\u0001\u0002\u0fcc\u0004\u0000\u0000\u0000\u0001\u0002\u0d2e\u0010\u079b\u08a2\u0d2f\u0d79\u0dea\u0d2f\u0f20\u0fe3\u0f20\u0d79\u0fe4\u0fe3\u0fe5\u079b\u08a2\u0001\u0001\u0001\u0003\u08a3\u08a3\u0003\u0d30\u0d7a\n\u08a7\u0d31\u0d7b\u0e3a\u0ecc\u0ef7\u0ff6\u0ffd\u0e3e\u0002\u0ff8\u0002\u0ffa\u0003\u0000\u0000\u0001\u0002\u0deb\u000c\u08a9\u0d32\u0ecd\u0ef8\u100c\u100d\u100e" +
|
||||
"\u100c\u100d\u100e\u100f\u0001\u0001\u0001\u0001\u0016\u079c\u08aa\u0d33\u0d7c\u0e3b\u0d33\u0f21\u0a91\u0f90\u1026\u1027\u1028\u1029\u102a\u0f21\u0d7c\u102b\u1026\u102c\u079c\u08aa\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u1033\u0dee\u0efa\u1033\u1034\u0001\u0001\u0003\u1038\u1038\u0001\u0009\u08ac\u0d34\u0ece\u0efb\u1027\u1042\u1042\u1043\u0001\u0001\u000c\u1028\u08ad\u0d35\u0df0\u0ea1\u0ecf\u1050\u1028\u1051\u1051\u1052" +
|
||||
"\u0001\u0001\u0001\u000c\u0d36\u0e3c\u0df1\u0e3c\u0ea2\u0ed0\u0efc\u105f\u1060\u105f\u1061\u0001\u0001\u0002\u0000\u002d\u1090\u1096\u079d\u08ae\u0ceb\u0d37\u0d7d\u0d37\u0f22\u0f79\u109a\u0fc8\u109b\u109c\u109d\u0f22\u0d7d\u109e\u109f\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u079d\u08ae\u10a6\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u0002\u1092\u0003\u1095\u0149\u0001\u0003\u1099" +
|
||||
"\u0251\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1095\u0149\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u0e3e\u0ec9\u0ecf\u0ed1\u10bf\u0001\u0003\u0efe\u10c3\u0001\u0002\u1029\u0008\u07a0\u08b0\u0eff\u10ce\u10cf\u07a0\u08b0\u0001\u0001\u0006\u08b1\u0df5\u0ed1\u10d6\u10d6\u0001\u0002\u10d9\u0001\u0008\u0d7e\u0f54\u10e2\u10e5\u10ea" +
|
||||
"\u10ed\u10ee\u0003\u0000\u0000\u0002\u10e7\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0002\u10d9\u004e\u113f\u04de\u04df\u04e0\u04e1\u0ddc\u0e3e\u04e2\u04e3\u04e4\u04e5\u04e6\u04e7\u04e8\u04e9\u04ea\u04eb\u04ec\u0f1e\u1149\u0f1f\u114a\u114b\u0f20\u114c\u0f21\u114d\u0f22\u10c3\u10ce\u114e\u114f\u0170\u0171\u0172\u0173\u0174\u0175\u0176\u0177\u0184\u0178\u0179\u017d\u017e\u0097\u1162\u1165\u1168\u116b\u116e\u1171\u0186\u0189" +
|
||||
"\u018c\u018f\u1174\u0192\u1147\u0197\u1148\u019a\u04e1\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u0007\u1146\u1147\u1148\u0195\u0197\u019a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0013\u0352\u035a\u04f2\u04f3\u04f5\u0354\u0353\u0357\u0358\u04fd\u04fe\u04ff\u050e\u0510\u0516\u0517\u0518\u0523\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000" +
|
||||
"\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0058\u0705\u11dd\u11e4\u11ee\u0463\u0464\u0465\u0466\u11ef\u0467\u0468\u0469\u046a\u046b\u046c\u046d\u046e\u046f\u0470\u11f8\u0ec7\u0471\u0f1c\u0d76\u0d78\u11fc\u0d79\u0d7c\u0d7d\u11fd\u0705\u0705\u031e\u031f\u0320\u0321\u0325\u0329\u032a\u11fe\u11eb\u1202\u1201\u11ec\u11ed\u032b\u032c\u032d\u032e\u032f" +
|
||||
"\u11f6\u0212\u1203\u1204\u1205\u0331\u0332\u0333\u1206\u1209\u120c\u120d\u1210\u1213\u1214\u1217\u0334\u121a\u121d\u0335\u11e2\u0336\u11e3\u0337\u1220\u1221\u0466\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u0005\u11e2\u11e3\u0336\u0337\u0001\u0001\u0007\u11eb\u11ec\u11ed\u032b\u032e\u032f\u0001\u0001\u0001\u0001\u0007\u11f6\u0d6e\u11f7\u0212\u0d71\u0213\u0001\u0001\u0003\u11fb\u0330\u0001\u0001\u0001\u0003\u1201" +
|
||||
"\u032d\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0046\u1272\u1279\u1296\u12b1\u0f7c\u0fae\u0fe4\u12b8\u102b\u109e\u12b9\u12ba\u12bb\u12c5\u12cc\u02b7\u12eb\u02b8\u02b9\u02ba\u02bb\u02bf\u02c0\u02c1\u12ef\u12c2\u12c3" +
|
||||
"\u12c4\u1277\u017f\u0181\u0182\u02c5\u12ee\u02c6\u12ca\u02c7\u12cb\u02c8\u12b6\u02c9\u12b7\u02ca\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u02d1\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u12fb\u12fe\u12ff\u1300\u1303\u1306\u1309\u02d8\u02d9\u130c\u130f\u1312\u0005\u1277\u02c5\u1278\u05a9\u0001\u0001\u000f\u1288\u1289\u128a\u128b\u128c\u128d\u128e\u128f\u1290\u1291\u1292\u1293\u1294\u1295\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u000e\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u12b6\u12b7\u02c9\u02ca\u0001\u0001\u0001\u0001\u0001\u0007\u12c2\u12c3\u12c4\u017f\u0181\u0182\u0001\u0001\u0001\u0005\u12ca\u12cb\u02c7\u02c8\u0001\u0001\r\u12d9\u12dc\u12df\u12e2\u12e5\u12e8\u0285\u0288\u028b\u028e\u0291" +
|
||||
"\u0294\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u12ee\u02c6\u0001\u0005\u12ca\u12cb\u02c7\u02c8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0000\u0000\u0001\u0001\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0012\u057a\u0037\u109f\u0362\u033f\u031e\u033f\u0170\u0f7a\u0170" +
|
||||
"\u031e\u02b7\u0362\u1327\u0540\u057a\u0037\u0001\u0021\u1349\u134c\u01ee\u0f79\u109a\u0fc8\u109b\u109c\u109d\u109f\u10a0\u10a1\u10a2\u10a3\u10a4\u134f\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u0003\u1099\u0251\u0003\u1095\u0149\u0003\u1095\u0149\u002c\u137e\u0038\u013e\u01ef\u0e3f\u0105\u0ef6\u1382\u0ec9\u0eca\u0ecb\u1383\u1384\u1385\u1386\u0ecc\u0ecd\u1387\u0ece\u0ecf\u0ed0" +
|
||||
"\u1388\u0ed1\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u010d\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0e3f\u1381\u0115\u0003\u1381\u0115\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0008\u013f\u00b7\u01f0\u0106\u0f02\u1391\u1392\u0001\u0001\u0011\u057e\u0039\u0340\u031f\u01f1\u0340\u0107\u0ef6\u0172\u0172\u031f\u02b8\u13a4\u0541\u057e\u0039\u0001\u0007\u01f2\u13ac\u13ad\u13af\u13b1\u13ac\u0001\u0002\u0000\u0002" +
|
||||
"\u0000\u0002\u0000\n\u10a1\u0141\u01f3\u10a1\u13bd\u0fc9\u10a1\u13c3\u10a1\u0002\u13bf\u0004\u0000\u0000\u0000\u0003\u0000\u0000\u0002\u0142\u000f\u0580\u003b\u0341\u0321\u01f5\u0341\u0175\u0175\u0321\u02b9\u13d7\u0542\u0580\u003b\u0001\u0003\u003c\u003c\u0003\u0143\u13de\u0001\u0008\u0040\u0144\u0d7b\u0380\u010d\u0ffd\u0e3e\u0004\u01fd\u0e40\u0e41\u0006\u0042\u0145\u010e\u13f1\u0543\u0001\u001a\u0590\u0043\u0342\u0325\u0383" +
|
||||
"\u0342\u0177\u0cea\u0ceb\u0177\u0325\u02bb\u0362\u0f90\u140c\u0351\u1027\u1028\u0363\u0364\u1029\u0365\u0544\u0590\u0043\u0001\u0005\u1033\u0200\u0efa\u1412\u0002\u1414\u0003\u0000\u0000\u0005\u0045\u0146\u0110\u0efb\u000b\u1028\u0046\u0147\u0205\u0ea4\u0111\u1050\u1028\u1427\u0147\u0001\n\u0148\u00c6\u0206\u00c6\u0ea5\u0112\u1432\u1060\u1433\u0001\u0002\u0000\u001b\u0595\u0047\u0363\u0343\u0329\u0e41\u0343\u0178\u0f79\u109a" +
|
||||
"\u0fc8\u109b\u109c\u109d\u0178\u0329\u02bf\u109f\u10a0\u10a1\u10a2\u10a3\u10a4\u0545\u0595\u0047\u0024\u0e42\u1382\u0ec9\u0eca\u0ecb\u1383\u1384\u1385\u1386\u0ecd\u1387\u0ece\u0ecf\u0ed0\u1388\u0ed1\u0104\u0105\u0106\u0107\u0108\u0109\u010a\u010b\u010c\u0e3e\u010e\u010f\u0110\u0111\u0112\u0113\u0114\u0091\u0546\u0002\u0179\u0003\u1029\u1479\u0001\u0008\u0598\u0049\u0eff\u017d\u0547\u0598\u0049\u0004\u004a\u0210\u0114\u0002\u1488" +
|
||||
"\u0001\u0012\u149b\u149c\u0f81\u0f92\u0fd0\u1052\u0097\u0e3f\u0091\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u002f\u14d4\u14d8\u14ed\u1500\u0e52\u0f7d\u0faf\u0fe5\u100f\u102c\u10a5\u10bf\u10cf\u1507\u0540\u1511\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u149d\u1515\u0599\u150e\u150f\u1510\u0180\u0199\u0183\u1514\u0549\u1505\u02ff\u1506\u0300\u1516\u1517\u1518\u1519" +
|
||||
"\u151a\u151b\u151c\u151d\u0003\u14d7\u05aa\u0001\u000b\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\n\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u1505\u1506\u02ff\u0300\u0001\u0001\u0007\u150e\u150f\u1510\u0180\u0199\u0183\u0001\u0001\u0001\u0003\u1514\u0549\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0017\u0853\u0579\u0f23\u0798\u0799\u079b\u079c\u079d\u07a0\u1535\u057a\u057e\u0580\u0590\u0595\u0598\u1536\u0853\u05ab\u059a\u1537\u1538\u0001\u0001\u0001\u0001\u0037\u1570\u1575\u157c\u1581\u0d76\u0d78\u11fc\u0d79\u0d7c\u0d7d\u11fd\u031e\u031f\u0320\u0321\u0325\u0329\u032a\u1584\u11eb\u1202\u1201\u11ec\u11ed\u032b\u032c\u032d\u032e\u032f\u11f6\u0212\u1203\u1204\u1205\u0331\u0332\u0333\u1206" +
|
||||
"\u1209\u120c\u120d\u1210\u1213\u1214\u1217\u0334\u121a\u121d\u0335\u11e2\u0336\u11e3\u0337\u1587\u0005\u11e2\u11e3\u0336\u0337\u0007\u11eb\u11ec\u11ed\u032b\u032e\u032f\u0005\u11f6\u11f7\u0212\u0213\u0003\u11fb\u0330\u0003\u1201\u032d\u0001\u0077\u15ff\u1606\u1615\u1623\u1628\u162b\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u162e\u1635\u0037\u163a\u0038\u0039\u003a" +
|
||||
"\u003b\u003c\u003f\u0040\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004a\u163d\u1642\u1647\u08b9\u08d7\u08d8\u08d6\u08ba\u0854\u08bb\u08cc\u089b\u0855\u004b\u004c\u004d\u004e\u004f\u0050\u0051\u0052\u0053\u0054\u08c6\u0055\u08c1\u0059\u08c2\u005a\u08cd\u005b\u0895\\\u0896\u005d\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u005e\u005f\u0060\u0061\u0062\u0063\u08e0\u08e1\u08e4\u08e5\u08e8\u08eb\u08ec\u08ef\u08f2\u08f5" +
|
||||
"\u08f8\u08fb\u08fe\u0064\u0067\u0068\u006b\u006e\u006f\u0901\u0072\u0075\u0078\u007b\u007e\u0007\u0854\u0855\u0050\u0054\u0856\u059d\u000f\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u000e\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0005\u0895\u0896\\\u005d\u0003\u08d6\u004e\u0003\u089b\u0053\u0007\u08b9\u08ba\u08bb\u004b\u004f\u0051\u0005\u08c1\u08c2" +
|
||||
"\u0059\u005a\u0003\u08c6\u0055\u0005\u08cc\u08cd\u0052\u005b\u0005\u08c1\u08c2\u0059\u005a\u0003\u08d6\u004e\u0017\u0d2a\u0d2b\u1661\u0d2d\u0d2e\u0d30\u0d31\u0d32\u0d34\u0d35\u0d36\u013e\u013f\u0140\u0141\u0142\u0143\u0144\u0145\u0146\u0147\u0148\u0001\u0002\u1664\u0001\u0003\u0fc8\u10a1\u0002\u08d9\u0002\u08da\u0002\u08db\u0003\u07ad\u08dc\u0002\u08dd\u0003\u07ae\u08de\u0002\u08df\u0002\u005e\u0002\u005f\u0002\u0060\u000e\u059b" +
|
||||
"\u168c\u16af\u16bc\u16c9\u16ec\u170f\u1723\u1724\u1725\u1726\u1727\u1728\u0006\u1692\u169f\u16ac\u16ad\u16ae\u0007\u1699\u169a\u169b\u169c\u169d\u169e\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0006\u16cf\u16dc\u16e9\u16ea\u16eb\u0007\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u16f2\u16ff\u170c\u170d\u170e\u0007\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u0001\u0001\u0001\u0001\u0001\u0001\u0007\u1706\u1707\u1708\u1709\u170a\u170b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006" +
|
||||
"\u1715\u171c\u16ac\u16ad\u16ae\u0007\u1699\u169a\u169b\u169c\u169d\u169e\u0007\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0062\u0003\u059c\u0063\u0002\u08e0\u0007\u07af\u07af\u120c\u1737\u1737\u120c\u0001\u0005\u173d\u173d\u173f\u173f\u0002\u0000\u0002\u0000\u000f\u1750\u1751\u1752\u1753\u1755\u1750\u1751\u1752\u1753\u1755\u1756\u1756\u1752\u1752\u0001\u0001\u0001\u0002\u0000\u0001\u0002\u0000" +
|
||||
"\u000b\u0d38\u0dfa\u0e44\u0e44\u1763\u1764\u0e44\u1763\u1764\u0e44\u0001\u0001\r\u1772\u1772\u1774\u1775\u1772\u1772\u1774\u1775\u1772\u1772\u1777\u1777\u0002\u0000\u0001\u0002\u0000\u0001\u0002\u0ef6\u0002\u0dfb\u0004\u0efa\u1780\u1780\u0001\u0005\u120c\u1737\u120c\u1737\u0002\u0dfc\u000b\u0d39\u0dfd\u1763\u1793\u1764\u1793\u1763\u1793\u1764\u1793\u0001\u0005\u0d3a\u0dfe\u1799\u1799\u0001\u0002\u0dff\u0007\u0d3b\u17a3\u100e" +
|
||||
"\u17a4\u100e\u17a4\u0001\u0001\u0005\u17aa\u17ab\u17aa\u17ab\u0001\u0001\u0003\u17af\u17af\u0001\u0002\u0ef6\u0009\u17bb\u17bb\u17bc\u17bd\u17bb\u17bc\u17bd\u17bb\u0001\u0001\u0001\u000b\u1737\u10b3\u1737\u10b3\u1213\u1737\u10b3\u1213\u1737\u10b3\u0003\u0d3c\u0e00\u0002\u0e01\u0003\u17d1\u17d1\u0001\u0011\u07af\u173d\u17e3\u17e4\u17e5\u07b1\u173f\u17e6\u173d\u17e3\u17e4\u17e5\u07b1\u173f\u17e6\u07af\u0001\u0001\u0001\u0002\u0000" +
|
||||
"\u0009\u17f1\u17f2\u17f3\u1802\u1803\u1805\u1806\u1802\u0001\u0001\u000f\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0002\u0000\u0001\u0002\u0000\u0002\u180a\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0d3d\u0e02\u0e46\u182a\u182b\u0e46\u0001\u0001\u0007\u1833\u1835\u1836" +
|
||||
"\u1833\u1833\u1838\u0002\u0000\u0001\u0002\u0000\u0001\u0002\u183b\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u0e03\u0003\u0efa\u1850\u0001\u0003\u0334\u1854\u0001\u0003\u0e04\u1858\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0d3e\u0e05\u182a\u186f\u182b\u186f\u0001\u0004\u0d3f\u0e06\u1874\u0001\u0002\u0e07\u0005" +
|
||||
"\u0d40\u187c\u187d\u188d\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0004\u1892\u1893\u1894\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u1028\u18b0\u0001\u0005\u18b6\u18b7\u18b8\u18b6\u0001\u0001\u0001\u0007\u1854\u10b8\u0335\u18c0\u1854\u10b8" +
|
||||
"\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u0d41\u0e08\u18d7\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000f\u0e09\u17f3\u180a\u183b\u1858\u187d\u1894\u18c0\u18d7\u18ff\u1911\u192a\u1858\u193a\u0012\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u194c\u0001\u0002\u1911\n" +
|
||||
"\u1959\u195b\u195c\u195d\u07ba\u192a\u195e\u1960\u07bb\u0002\u0000\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0005\u07bb\u07bb\u1854\u0334\u0004\u1959\u1858\u195e\u0003\u17aa\u17aa\u0002\u1892\u0002\u193a\u0002\u1974\u0002\u0000\u0003\u1979\u197b\u0002\u0000\u0002\u0000\u0002\u197f\u0002\u0000\u0002\u1983\u0002\u0000\u0017\u199c\u199e\u19a0\u19a2\u19a4\u19a6\u19a8\u19aa\u19ac\u19ae\u19b0\u19b2\u19b4\u19b6\u19b8\u19ba\u19bc\u19be" +
|
||||
"\u19c0\u19c2\u19c4\u19c6\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0004\u19cc\u19ce\u19d0\u0002\u0000\u0002\u0000\u0002\u0000\u0004\u19d6\u19da\u19d8\u0002\u19d8\u0002\u0000\u0002\u0000\u0004\u19e0\u19e4\u19e2" +
|
||||
"\u0002\u19e2\u0002\u0000\u0002\u0000\u0008\u19ee\u1a03\u1a04\u1a05\u1a06\u1a05\u1a06\u0015\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0015\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0005\u1a20\u1a21\u1a22\u1a23\u0001\u0001\u0001\u0001\u0002\u1a26\u0001\u0002" +
|
||||
"\u1a29\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004\u1a21\u1a3d\u1a3e\u0001\u0001\u0002\u1a41\u0001\u0002\u1a22\n\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a68\u1a51\u1a52\u1a68\u0001\u0001\u0001\u0001\u0016\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0016\u0000\u0000\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u1a81\u1a81\u0001\u0002\u1a84\u0001\u0002\u1a87\u0001\u0004\u1a8c\u1a9e\u1a9e\u0012\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0006\u1a23\u1aa5\u1ab8\u1acb\u1acb\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0009\u1ae7\u1ae8\u1a4f\u1aa5\u1ae9\u1af9\u1afa\u1afa\u0001\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\n\u1b05\u1a50\u1a84\u1ab8" +
|
||||
"\u1b06\u1af9\u1a04\u1af9\u1a04\u0001\u0011\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\u1b21\u1b22\u1a51\u1acb\u1b23\u1afa\u1a05\u1afa\u1a05\u0001\u0001\u0010\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u1b35\u0001\u0007\u1a03\u1a50\u1a51\u1b3d\u1b3e\u1a4f\u0001\u0001\u0002\u1a4e\u0009\u1a06\u1a3e\u1a52\u1a9e\u1af9\u1afa" +
|
||||
"\u1b4a\u1afa\u0013\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u1a68\u1a04\u1a05\u1b63\u1a05\u001c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\n\u1b21\u1b22\u1a51\u1acb\u1afa\u1a05\u1b23\u1afa\u1a05\u0009\u1a06\u1a3e\u1a52\u1a9e\u1af9\u1afa" +
|
||||
"\u1afa\u1b4a\u0006\u1a68\u1a04\u1a05\u1a05\u1b63\u000c\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u1bb3\u1bb4\u1bb5\u0001\u0001\u0001\u0003\u1bb9\u1bba\u0001\u0001\u0007\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1bcb\u1bcc\u0001\u0001\u0004\u1bd1\u1bd2\u1bd3\u0001\u0001\u0001\u0003\u1bd7\u1bd8\u0001" +
|
||||
"\u0001\u0002\u1bdb\u0001\u0002\u1bde\u0001\u0002\u1be1\u0001\u0002\u1be4\u0001\u0004\u1be9\u1bea\u1beb\u0001\u0001\u0001\u0002\u1bee\u0001\u0003\u1bf2\u1bf3\u0001\u0001\u0002\u1bf6\u0001\u0004\u1bfb\u1bfc\u1bfd\u0001\u0001\u0001\u0002\u1c00\u0001\u0004\u1c05\u1c06\u1c07\u0001\u0001\u0001\u0002\u1c0a\u0001\u0002\u1c0d\u0001\u0002\u1c10\u0001\u0002\u1c13\u0001\u0002\u1c16\u0001\u0002\u1c19\u0001\u0002\u1c1c\u0001\u0002\u1c1f\u0001" +
|
||||
"\u0002\u1c22\u0001\u0006\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u0001\u0001\u0001\u0001\u0001\u0002\u1c30\u0001\u0002\u1c33\u0001\u0005\u1c39\u1c3a\u1c3b\u1c3c\u0001\u0001\u0001\u0001\u0003\u1c40\u1c41\u0001\u0001\u0005\u1c47\u1c48\u1c49\u1c4a\u0001\u0001\u0001\u0001\u0003\u1c4e\u1c4f\u0001\u0001\u0011\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1c6c\u0011\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1c8b\u000c\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1cb0\u1cb1" +
|
||||
"\u0001\u0001\u0003\u1cb5\u1cb6\u0001\u0001\u0003\u1cba\u1cbb\u0001\u0001\u0002\u1cbe\u0001\u0006\u1c6d\u1c6e\u1c6f\u1c66\u1c70\u0002\u1c93\u0002\u1cc9\u0001\u0002\u1ccc\u0001\u0002\u1ccf\u0001\u0002\u1cd2\u0001\u0004\u1cd7\u1cd8\u1cd9\u0001\u0001\u0001\u0004\u1cde\u1cdf\u1ce0\u0001\u0001\u0001\u0005\u1ce6\u1ce7\u1ce8\u1ce9\u0001\u0001\u0001\u0001\u0004\u1cee\u1cef\u1cf0\u0001\u0001\u0001\u0009\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff" +
|
||||
"\u1d00\u1d01\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1cfb\u0006\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u0001\u0001\u0001\u0001\u0001\u0009\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1d1f\u1d1b\u0008\u1d2b\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u1d36\u1d37\u1d38\u0001\u0001\u0001\u0002\u1d3b\u0001\u0007\u1d43\u1d44\u1d45" +
|
||||
"\u1d46\u1d47\u1d48\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u1d4e\u1d4f\u1d50\u1d51\u0001\u0001\u0001\u0001\u0005\u1d57\u1d58\u1d59\u1d5a\u0001\u0001\u0001\u0001\u0002\u1d5a\u0008\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u1d6f\u1d70\u0001\u0001\u0015\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u1d87\u0003\u1d94\u1d95\u0004\u1d8d\u1d97\u1d96\u0002\u1d8f\n\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u1dbe\u1dbf\u1dc0\u1dc1\u1dc2\u0001\u0001\u0001\u0001\u0001\u0006\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u0001\u0001\u0001\u0001\u0001\u0005\u1dd3" +
|
||||
"\u1dd4\u1dd5\u1dd6\u0001\u0001\u0001\u0001\u0003\u1dda\u1ddb\u0001\u0001\u0005\u1de1\u1de2\u1de3\u1de4\u0001\u0001\u0001\u0001\u0002\u1de7\u0001\u0002\u1db3\u0002\u1dd4\u0003\u1def\u1df0\u0001\u0001\u0003\u149e\u1df4\u0001\u0003\u0d80\u1df8\u0001\u0003\u149f\u1dfc\u0001\u0003\u0d81\u1e00\u0001\u0002\u1e03\u0001\u0004\u1220\u14a0\u1e08\u0001\u0005\u14a1\u151b\u05ab\u1e0e\u0001\u0002\u1e11\u0001\u0002\u1221\u0002\u1e16\u0001\u0008" +
|
||||
"\u1222\u059a\u1e1f\u1e20\u1e21\u1e22\u1e23\u0001\u0001\u0001\u0001\u0001\u0002\u1e26\u0001\u0002\u1e29\u0001\u0004\u151c\u0ec8\u1e2e\u0001\u0004\u151d\u0ec8\u1e33\u0001\u0002\u1e1f\u0002\u14a2\u0002\u1e3a\u0001\u0002\u1e3d\u0001\u0002\u1e40\u0001\u0002\u1e43\u0001\u0003\u1223\u1e47\u0001\u0002\u1223\u0002\u1e4c\u0001\u0002\u1e4f\u0001\u0002\u1e52\u0001\u0002\u1e55\u0001\u0002\u1e58\u0001\u0002\u1e5b\u0001\u0002\u1e5e\u0001\u0002" +
|
||||
"\u1e61\u0001\u0002\u1e64\u0001\u0002\u1e67\u0001\u0004\u1e6c\u1224\u14a3\u0001\u0003\u1e70\u1225\u0001\u0002\u1e73\u0001\u0002\u1e76\u0001\u0002\u1e79\u0001\u0002\u1e7c\u0001\u0002\u1e7f\u0001\u0005\u1537\u1587\u1e20\u1e85\u0001\u0004\u1226\u1e21\u1e85\u0002\u1e8c\u0001\u0002\u1e8f\u0001\u0002\u1e92\u0001\u0002\u1e95\u0001\u0002\u1e98\u0001\u0002\u1e9b\u0001\u0002\u1e9e\u0001\u0003\u1227\u1ea2\u0001\u0002\u1228\u0002\u1229\u0002" +
|
||||
"\u122a\u0016\u0904\u0e4c\u0e8e\u0ed2\u0f05\u0f24\u0f63\u10ed\u1664\u1df4\u1df8\u1dfc\u1e00\u1e08\u1e0e\u1e22\u1e2e\u1e33\u1e47\u1ea2\u1ebf\u0001\n\u0cec\u0d82\u0e0a\u10ee\u122b\u14a4\u1538\u1e23\u1ebf\u0002\u1ecc\u0001\u0104\u1fd1\u0735\u0795\u07cf\u0897\u0796\u0797\u0351\u0ddc\u0d6a\u0ddd\u1fd6\u1fda\u1fdd\u1fe0\u1fe3\u1fe6\u1fe9\u1fec\u1fef\u1ff2\u1ff5\u0ec6\u1515\u0f1d\u0f4d\u089c\u089d\u0d28\u0d69\u1327\u1ffa\u0e3f\u0ef6" +
|
||||
"\u1ffb\u0d7b\u140c\u0efa\u0efb\u0e41\u0eff\u1ffc\u0ddf\u0751\u0de1\u1ffd\u1033\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u200e\u200f\u2010\u2011\u2012\u2013\u1802\u182a\u1850\u1802\u2014\u2016\u2017\u0e06\u2018\u2019\u201a\u201b\u201c\u18b8\u201e\u18b6\u18b0\u201f\u2020\u2022\u2023\u1959\u1833\u2024\u10b7\u2025\u0e09\u1806\u0e05\u2026\u182b\u1835\u187c\u0e06\u203f\u204b\u2052\u2058\u205f\u2065\u206a\u2070\u207c\u2082" +
|
||||
"\u2088\u208f\u209c\u1a52\u1a68\u1b35\u20a2\u1ae9\u1a20\u20a3\u20a4\u1a3d\u1a3e\u20a5\u1a41\u20a6\u20a6\u20a7\u20a8\u1a81\u1b63\u20a9\u20aa\u20ab\u1a9e\u20ac\u20ad\u20ae\u1a84\u20af\u1af9\u1afa\u1fd6\u1fda\u1fdd\u1fe0\u1fe3\u1fe6\u1fe9\u1fec\u1fef\u1ff2\u20a7\u1b06\u20b0\u20b1\u1b4a\u1b63\u20b2\u1b23\u1fd6\u1fda\u1fdd\u1fe0\u1fe3\u1fe6\u1fe9\u1fec\u1fef\u1ff2\u20b3\u20b4\u20b5\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be" +
|
||||
"\u20bf\u20c0\u1ba4\u1ba5\u1ba6\u20c1\u20c2\u20c3\u1ba7\u20c7\u20c8\u1ba8\u1ba9\u20c9\u20ca\u20ce\u1baa\u20d2\u1bab\u20d6\u1bac\u1bad\u1bae\u20d7\u20d8\u20dc\u20e0\u20e1\u20e2\u20e3\u20e4\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u20ec\u20ed\u20ee\u20ef\u20f0\u20f2\u20f4\u20f6\u20f8\u149c\u1ffd\u1043\u0ef4\u0f00\u0ef5\u0f03\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u1e03\u1220\u05ab\u2100\u2101\u2102\u2103\u2104\u1224\u1225\u1537\u2105" +
|
||||
"\u1ea2\u14a3\u1229\u122a\u0904\u1838\u2106\u2107\u2108\u0005\u0000\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0005\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0008\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001\u0001\u0002\u0000\u0001\u0001\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0007\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000" +
|
||||
"\u0007\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0005\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u000c\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0007\u0000\u0000\u0000\u0000\u0000\u0000\r\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000" +
|
||||
"\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0004\u0000\u0000\u0000\u0001\u0001\u0001\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0001\u0001\u0004\u0000\u0000\u0000\u0004\u0000\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0002\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0019\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u000b\u212c\u212d\u212e\u212f\u2130\u2131\u2132\u2133\u2134\u2135\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2141\u2142\u2143\u2144\u2145\u2146\u2147\u2148\u2149\u214a\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2180\u2181" +
|
||||
"\u2182\u2183\u2184\u2185\u2186\u2187\u2188\u2189\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u2195\u2196\u2197\u2198\u2199\u219a\u219b\u219c\u219d\u219e\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u000b\u21aa\u21ab\u21ac\u21ad\u21ae\u21af\u21b0\u21b1\u21b2\u21b3\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0071\u1051\u0dea\u2225\u2228\u222b\u222c\u222d\u222e\u222f\u2230\u2231" +
|
||||
"\u2232\u20bb\u20bc\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u20e6\u20b5\u2245\u20b6\u2246\u2247\u20ed\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u1043\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226f\u0424\u2271\u2274\u2277" +
|
||||
"\u227a\u227b\u1043\u0ef4\u0f00\u0ef5\u0f03\u20fc\u20fd\u20fe\u20ff\u1e11\u1e16\u1e6c\u1e70\u1e73\u1e76\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0002\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0006\u228d\u2290\u2293\u2296\u2299\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000\u0000\u0003\u0000" +
|
||||
"\u0000\u0003\u0000\u0000\u03b4\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u0415\u2667\u2668\u0415\u2652\u265b\u2667\u2650\u2664\u265e\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2652\u2654\u2654\u2654\u2654\u2658\u2658\u2658\u2658\u265d\u265e\u265e\u265e\u265e\u265e\u265e\u2664\u2664\u2664\u2664\u2667\u2650\u2650\u2650\u2650\u2650\u2650" +
|
||||
"\u2650\u2652\u2654\u2654\u2654\u2654\u2658\u2658\u2658\u2658\u265d\u265e\u265e\u265e\u265e\u265e\u265e\u2664\u2664\u2664\u2664\u2667\u2667\u2650\u2650\u2650\u2650\u2652\u2652\u2652\u2652\u2652\u2652\u2652\u2652\u2653\u2653\u2653\u2653\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2656\u2656\u2656\u2656\u2656\u2656\u2657\u2657\u2657\u2657\u2658\u2658\u2658\u2658\u2658\u2658\u2658\u2658\u2659\u2659\u265a\u265a\u265b\u265b\u265b" +
|
||||
"\u265b\u265b\u265b\u265b\u265b\u265d\u265d\u265d\u265d\u265d\u265d\u265e\u265e\u265e\u265e\u265e\u265e\u2661\u2661\u2661\u2661\u2661\u2661\u2662\u2662\u0184\u0184\u2662\u2662\u2662\u2662\u2663\u2663\u2663\u2663\u2663\u2663\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2666\u2666\u2667\u2667\u2667\u2668\u2668\u2668\u2668\u2668\u2668\u2651\u2669\u2658\u265b\u265e\u265e\u265e\u2661\u2664\u2664\u2668\u2668\u2650\u2650" +
|
||||
"\u2658\u2658\u265e\u265e\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2650\u2650\u2650\u2650\u2650\u2650\u2656\u2656\u2656\u2656\u265a\u265a\u265e\u265e\u265e\u265e\u266a\u266a\u2659\u2656\u2656\u265d\u265d\u2650\u2650\u2650\u2650\u265e\u265e\u2650\u2650\u2654\u2654\u2658\u2658\u265e\u265e\u2661\u2661\u2664\u2664\u2657\u2657\u2650\u2650\u2654\u2654\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u2667\u2667\u2659" +
|
||||
"\u2650\u2652\u2652\u265b\u2663\u2651\u2664\u2654\u2654\u2659\u2659\u2661\u2661\u2667\u2667\u2659\u2656\u2669\u2658\u2658\u265d\u265e\u265e\u2661\u2664\u2667\u2651\u266b\u2657\u265b\u2657\u266c\u2659\u2661\u266d\u266e\u042a\u2666\u2667\u2669\u265b\u2662\u0415\u266f\u2670\u2671\u2672\u2673\u2674\u2675\u2670\u2676\u2677\u2664\u2678\u2679\u267a\u267b\u2672\u2674\u266f\u2670\u2671\u2672\u2674\u267a\u2672\u2674\u2673\u2674\u2675\u2676" +
|
||||
"\u2674\u2674\u267b\u2678\u2677\u2679\u2676\u2670\u267c\u1833\u267d\u267e\u267f\u2024\u10b7\u2025\u2680\u2024\u0e09\u2016\u2017\u18b6\u201f\u2020\u2022\u1959\u2016\u2017\u18b6\u201f\u2020\u2022\u1959\u267c\u1833\u267d\u267e\u267f\u2024\u10b7\u2025\u2680\u2024\u0e09\u267c\u267c\u2681\u2681\u2682\u2682\u2683\u2683\u2684\u2684\u1854\u1854\u2685\u2685\u0e05\u0e05\u201f\u201f\u267d\u267d\u2686\u2686\u182b\u182b\u2687\u2687\u1835\u1835" +
|
||||
"\u2688\u2688\u2689\u2689\u1805\u1805\u268a\u268a\u268b\u268b\u2024\u2024\u2024\u2024\u2685\u2685\u2685\u2685\u2685\u2685\u267e\u267e\u268c\u268c\u268c\u268c\u268c\u268c\u2025\u2025\u2106\u2106\u2688\u2688\u0d40\u0d40\u268d\u268d\u268e\u268f\u2690\u2691\u2691\u2692\u2693\u2694\u2695\u20ab\u20ae\u20af\u2696\u1a22\u2697\u2693\u2698\u2699\u20b0\u2694\u269a\u269b\u20b1\u269c\u20b2\u269d\u269e\u269f\u26a0\u26a1\u26a2\u26a2\u26a2\u269f" +
|
||||
"\u26a3\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26aa\u26ad\u26ae\u26af\u26af\u26b0\u26a4\u26a4\u26a5\u26a7\u26a9\u26a9\u26b1\u26b2\u26b3\u26b4\u269f\u26a0\u26a0\u26a3\u26a1\u26a1\u26a8\u26a7\u26b5\u26a5\u26a6\u26b6\u26aa\u26ad\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u2650\u2650\u2651\u2652\u2653\u1391\u2654\u2659\u265a\u265b\u265c" +
|
||||
"\u13f1\u265e\u26cb\u26cc\u265f\u2107\u266d\u2663\u2664\u2665\u2666\u2668\u266a\u2669\u26cd\u2678\u2679\u26ce\u268d\u2650\u26cf\u26d0\u2651\u2653\u2654\u13a4\u26d1\u26d2\u2656\u26d3\u265a\u265c\u13f1\u265e\u26cb\u265f\u2663\u2664\u26d4\u26d5\u2665\u2675\u26d6\u267b\u26d7\u2658\u2661\u2664\u2665\u2675\u2669\u2679\u267b\u26d7\u2658\u265f\u2664\u26d8\u2652\u26d9\u26da\u2655\u2659\u26db\u2658\u2672\u26dc\u26dd\u265e\u26de\u26df\u2664" +
|
||||
"\u26e0\u26e1\u2668\u266a\u2676\u2651\u2651\u2651\u2651\u2652\u2652\u2653\u2653\u2653\u2653\u2653\u2653\u2654\u2654\u2654\u2654\u10a0\u10a0\u2655\u2655\u2656\u2656\u2657\u2657\u2657\u2657\u2657\u2657\u2657\u2657\u2658\u2658\u265a\u265a\u265a\u265a\u265b\u265b\u265b\u265b\u265c\u265c\u265c\u265c\u265c\u265c\u265d\u265d\u265d\u265d\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265f\u265f\u265f\u265f\u2661\u2661\u2661\u2661\u2661" +
|
||||
"\u2661\u2662\u2662\u2662\u2662\u2662\u2662\u2662\u2662\u2662\u2662\u2663\u2663\u2663\u2663\u2664\u2664\u2665\u2665\u2665\u2665\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u2666\u0415\u0415\u0415\u0415\u2667\u2667\u2668\u2668\u2668\u2668\u2663\u2666\u2667\u13ac\u1427\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u2650\u109f\u109f\u109f\u109f\u109f\u109f\u109f\u109f\u109f\u109f\u2654\u2654" +
|
||||
"\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2654\u2658\u2658\u2658\u2658\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u265e\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2664\u2667\u2667\u2667\u2667\u2667\u2667\u2667\u2667\u266f\u2670\u2671\u2672\u2673\u2674\u26e2\u266f" +
|
||||
"\u266f\u266f\u2670\u2671\u2672\u2672\u2672\u2674\u2674\u1519\u2674\u2674\u2673\u26e2\u2658\u265d\u2650\u2654\u265e\u0415\u13a4\u2657\u265a\u265b\u265c\u265d\u265f\u2662\u2663\u2654\u2651\u2657\u2661\u265f\u2668\u2678\u267a\u2651\u265f\u2650\u2663\u26e3\u2659\u268b\u268b\u1838\u1838\u268c\u268c\u17f2\u17f2\u2106\u2106\u2107\u2107\u2655\u2662\u265a\u265a\u265a\u265a\u2660\u2660\u2665\u2665\u26e4\u2652\u2652\u2655\u2655\u2656\u2656" +
|
||||
"\u265d\u265d\u2661\u2661\u2662\u2662\u2658\u2660\u2664\u2664\u2660\u265e\u26d5\u26cb\u26e2\u26e6\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001" +
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001").toCharArray();
|
||||
"\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0002\u0000\u0001").toCharArray();
|
||||
|
||||
public static final int accent_aigu = 1;
|
||||
public static final int accent_arrows = 130;
|
||||
@@ -311,14 +311,14 @@ public final class ComposeKeyData
|
||||
public static final int accent_tilde = 1326;
|
||||
public static final int accent_trema = 1354;
|
||||
public static final int compose = 1452;
|
||||
public static final int fn = 7889;
|
||||
public static final int numpad_bengali = 8480;
|
||||
public static final int numpad_devanagari = 8501;
|
||||
public static final int numpad_gujarati = 8522;
|
||||
public static final int numpad_hindu = 8543;
|
||||
public static final int numpad_kannada = 8564;
|
||||
public static final int numpad_persian = 8585;
|
||||
public static final int numpad_tamil = 8606;
|
||||
public static final int shift = 8627;
|
||||
public static final int substitutions = 8859;
|
||||
public static final int fn = 7885;
|
||||
public static final int numpad_bengali = 8481;
|
||||
public static final int numpad_devanagari = 8502;
|
||||
public static final int numpad_gujarati = 8523;
|
||||
public static final int numpad_hindu = 8544;
|
||||
public static final int numpad_kannada = 8565;
|
||||
public static final int numpad_persian = 8586;
|
||||
public static final int numpad_tamil = 8607;
|
||||
public static final int shift = 8628;
|
||||
public static final int substitutions = 8860;
|
||||
}
|
||||
|
||||
@@ -76,6 +76,9 @@ public final class Config
|
||||
public int clipboard_history_duration;
|
||||
public boolean space_bar_auto_complete;
|
||||
public boolean physical_keyboard_hide;
|
||||
public boolean usageGaugeEnabled;
|
||||
public String usageGaugeUrl;
|
||||
public String usageGaugeToken;
|
||||
|
||||
// Dynamically set
|
||||
/** Configuration options implied by the connected editor. */
|
||||
@@ -161,8 +164,8 @@ public final class Config
|
||||
longPressInterval = _prefs.getInt("longpress_interval", 65);
|
||||
keyrepeat_enabled = _prefs.getBoolean("keyrepeat_enabled", true);
|
||||
margin_bottom = get_dip_pref_oriented(dm, "margin_bottom", 7, 3);
|
||||
key_vertical_margin = get_dip_pref(dm, "key_vertical_margin", 1.5f) / 100;
|
||||
key_horizontal_margin = get_dip_pref(dm, "key_horizontal_margin", 2) / 100;
|
||||
key_vertical_margin = get_dip_pref(dm, "key_vertical_margin", 6f) / 100;
|
||||
key_horizontal_margin = get_dip_pref(dm, "key_horizontal_margin", 13) / 100;
|
||||
// Label brightness is used as the alpha channel
|
||||
labelBrightness = _prefs.getInt("label_brightness", 100) * 255 / 100;
|
||||
// Keyboard opacity
|
||||
@@ -199,6 +202,9 @@ public final class Config
|
||||
clipboard_history_duration = Integer.parseInt(_prefs.getString("clipboard_history_duration", "5"));
|
||||
space_bar_auto_complete = _prefs.getBoolean("space_bar_auto_complete", false);
|
||||
physical_keyboard_hide = _prefs.getString("physical_keyboard_behavior", "hide").equals("hide");
|
||||
usageGaugeEnabled = _prefs.getBoolean("usage_gauge_enabled", true);
|
||||
usageGaugeUrl = _prefs.getString("usage_gauge_url", "http://10.10.10.1:8771/usage");
|
||||
usageGaugeToken = _prefs.getString("usage_gauge_token", "");
|
||||
float screen_width_dp = dm.widthPixels / dm.density;
|
||||
wide_screen = screen_width_dp >= WIDE_DEVICE_THRESHOLD;
|
||||
split_layout = get_split_layout();
|
||||
@@ -276,6 +282,7 @@ public final class Config
|
||||
case "everforestlight": return R.style.EverforestLight;
|
||||
case "cobalt": return R.style.Cobalt;
|
||||
case "pine": return R.style.Pine;
|
||||
case "orchid": return R.style.Orchid;
|
||||
case "epaperblack": return R.style.ePaperBlack;
|
||||
default:
|
||||
case "system":
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DeepSeekPeak
|
||||
{
|
||||
/**
|
||||
* Peak is Monday through Friday from 01:00 to 04:00 UTC or from 06:00 to
|
||||
* 10:00 UTC. DeepSeek additionally excludes Chinese public holidays; this
|
||||
* helper deliberately ignores that exception.
|
||||
*/
|
||||
public static boolean isPeak(long epochMillis)
|
||||
{
|
||||
Calendar utc = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||
utc.setTimeInMillis(epochMillis);
|
||||
int day = utc.get(Calendar.DAY_OF_WEEK);
|
||||
if (day == Calendar.SATURDAY || day == Calendar.SUNDAY)
|
||||
return false;
|
||||
|
||||
int hour = utc.get(Calendar.HOUR_OF_DAY);
|
||||
return (hour >= 1 && hour < 4) || (hour >= 6 && hour < 10);
|
||||
}
|
||||
|
||||
public static int colorFor(long epochMillis)
|
||||
{
|
||||
return isPeak(epochMillis) ? 0xFFFF2D95 : 0xFF00E5FF;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
long[] samples = {
|
||||
1704675540000L, // Monday 00:59 UTC
|
||||
1704675600000L, // Monday 01:00 UTC
|
||||
1704700800000L, // Monday 08:00 UTC
|
||||
1704711600000L // Monday 11:00 UTC
|
||||
};
|
||||
for (long sample : samples)
|
||||
System.out.println(sample + ": " + (isPeak(sample) ? "peak" : "off-peak"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/** Host load history behind the space bar ribbon.
|
||||
*
|
||||
* Two rules, both Harley's, and they are the whole design:
|
||||
* - failure is quiet. Offline, VPN down, Utumno off, junk payload or a timeout all end as
|
||||
* "no data" - never a crash, and never a stale trace wearing a live face.
|
||||
* - parsing happens on the fetch thread, so the draw path can neither block nor throw.
|
||||
*/
|
||||
public final class HostGraph
|
||||
{
|
||||
/** Payload series order. io leads: a stalled disk is the thing worth seeing. */
|
||||
public static final String[] SERIES = { "io", "cpu", "util" };
|
||||
/** A sample older than this is not worth drawing as if it were now. */
|
||||
/** Safety net only: with the 1 Hz re-arm this never trips, but if fetches stop the ribbon
|
||||
blanks rather than freezing mid-thought. */
|
||||
public static final long MAX_AGE_MS = 15000L;
|
||||
private static final int CONNECT_TIMEOUT_MS = 2500;
|
||||
private static final int READ_TIMEOUT_MS = 2500;
|
||||
/** Matches the server's 1 Hz sampling; only ever called while the keyboard is visible. */
|
||||
private static final long MIN_INTERVAL_MS = 900L;
|
||||
private static final int DEFAULT_N = 60;
|
||||
|
||||
private static final Object _lock = new Object();
|
||||
private static float[][] _values = new float[SERIES.length][];
|
||||
private static long _fetchedAt = 0L;
|
||||
private static long _lastAttempt = 0L;
|
||||
private static boolean _inFlight = false;
|
||||
|
||||
private HostGraph() {}
|
||||
|
||||
/** True only when there is something honest to draw: data, and data that is still current. */
|
||||
public static boolean hasData()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_values[0] == null || _values[0].length == 0)
|
||||
return false;
|
||||
return System.currentTimeMillis() - _fetchedAt < MAX_AGE_MS;
|
||||
}
|
||||
}
|
||||
|
||||
/** Copy of one series; empty when there is nothing to draw. Safe on the draw path. */
|
||||
public static float[] series(int index)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
float[] v = (index >= 0 && index < _values.length) ? _values[index] : null;
|
||||
return v == null ? new float[0] : v.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/** The server root, so the ribbon rides the same preference as the usage endpoint. */
|
||||
static String baseOf(String url)
|
||||
{
|
||||
if (url == null)
|
||||
return "";
|
||||
int scheme = url.indexOf("://");
|
||||
int from = scheme < 0 ? 0 : scheme + 3;
|
||||
int slash = url.indexOf('/', from);
|
||||
return slash < 0 ? url : url.substring(0, slash);
|
||||
}
|
||||
|
||||
/** Fetch at most every couple of seconds; call freely while the keyboard is visible. */
|
||||
public static void fetchIfNeeded(String baseUrl, String token, Runnable onDone)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_inFlight || now - _lastAttempt < MIN_INTERVAL_MS)
|
||||
return;
|
||||
_inFlight = true;
|
||||
_lastAttempt = now;
|
||||
}
|
||||
Thread worker = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
run(baseUrl, token);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
forget();
|
||||
}
|
||||
finally
|
||||
{
|
||||
synchronized (_lock) { _inFlight = false; }
|
||||
if (onDone != null)
|
||||
{
|
||||
try { onDone.run(); }
|
||||
catch (Throwable t) { /* a repaint request must never take the keyboard down */ }
|
||||
}
|
||||
}
|
||||
}, "host-graph");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private static void forget()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
_values = new float[SERIES.length][];
|
||||
_fetchedAt = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private static void run(String baseUrl, String token)
|
||||
{
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
String url = baseUrl + "/graph?n=" + DEFAULT_N;
|
||||
connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
connection.setReadTimeout(READ_TIMEOUT_MS);
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
if (token != null && !token.isEmpty())
|
||||
connection.setRequestProperty("Authorization", "Bearer " + token);
|
||||
StringBuilder body = new StringBuilder();
|
||||
try (InputStream input = connection.getInputStream();
|
||||
InputStreamReader reader = new InputStreamReader(input, "UTF-8"))
|
||||
{
|
||||
char[] buf = new char[4096];
|
||||
int n;
|
||||
while ((n = reader.read(buf)) > 0)
|
||||
body.append(buf, 0, n);
|
||||
}
|
||||
float[][] parsed = parse(body.toString());
|
||||
if (parsed == null)
|
||||
{
|
||||
forget();
|
||||
return;
|
||||
}
|
||||
synchronized (_lock)
|
||||
{
|
||||
_values = parsed;
|
||||
_fetchedAt = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// No VPN, no route, host off, refused, unreadable: one answer for all of them.
|
||||
forget();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/** Parses the payload, or returns null when it cannot be trusted. Pure, therefore tested. */
|
||||
static float[][] parse(String json)
|
||||
{
|
||||
if (json == null)
|
||||
return null;
|
||||
float[][] out = new float[SERIES.length][];
|
||||
for (int i = 0; i < SERIES.length; i++)
|
||||
{
|
||||
int nameAt = json.indexOf("\"" + SERIES[i] + "\"");
|
||||
if (nameAt < 0)
|
||||
return null; // a series we cannot read is no data at all
|
||||
int open = json.indexOf('[', nameAt);
|
||||
int close = open < 0 ? -1 : json.indexOf(']', open);
|
||||
if (open < 0 || close < 0)
|
||||
return null;
|
||||
String[] parts = json.substring(open + 1, close).split(",");
|
||||
float[] values = new float[parts.length];
|
||||
for (int j = 0; j < parts.length; j++)
|
||||
{
|
||||
try
|
||||
{
|
||||
values[j] = Float.parseFloat(parts[j].trim());
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (values.length == 0)
|
||||
return null;
|
||||
out[i] = values;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,13 @@ public final class KeyModifier
|
||||
return KeyValue.CHANGE_METHOD;
|
||||
case SWITCH_VOICE_TYPING:
|
||||
return KeyValue.VOICE_TYPING_CHOOSER;
|
||||
/* The Roku page's volume-down key: held, it is the TV's mute instead of
|
||||
volume-down. This is the same path the two keys above take, so [Pointers]
|
||||
replaces the pointer's value while the finger is still down and the single
|
||||
[key_up] on release carries the mute - the short press is not sent as well.
|
||||
Mute therefore lives on the page without taking a slot of its own. */
|
||||
case ROKU_VOLDOWN:
|
||||
return KeyValue.ROKU_VOLUME_MUTE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
{
|
||||
CONFIG,
|
||||
SWITCH_TEXT,
|
||||
SWITCH_FN,
|
||||
SWITCH_NUMERIC,
|
||||
SWITCH_EMOJI,
|
||||
SWITCH_BACK_EMOJI,
|
||||
@@ -25,6 +26,40 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
SWITCH_VOICE_TYPING,
|
||||
SWITCH_VOICE_TYPING_CHOOSER,
|
||||
HIDE_SELF,
|
||||
/* The Roku remote page and the keys on it. These are appended: the ordinals are
|
||||
in-process only, so adding at the end never renumbers an existing key. */
|
||||
SWITCH_ROKU,
|
||||
ROKU_HOME,
|
||||
ROKU_BACK,
|
||||
ROKU_UP,
|
||||
ROKU_DOWN,
|
||||
ROKU_LEFT,
|
||||
ROKU_RIGHT,
|
||||
ROKU_SELECT,
|
||||
ROKU_PLAY,
|
||||
ROKU_REWIND,
|
||||
ROKU_FORWARD,
|
||||
ROKU_POWER,
|
||||
ROKU_VOLUP,
|
||||
ROKU_VOLDOWN,
|
||||
ROKU_VOLMUTE,
|
||||
/* The lights page (res/xml/lights.xml) and its keys. Appended for the same reason as the Roku
|
||||
events above: the ordinals only mean anything in-process. LIGHT_1..LIGHT_12 are the page's
|
||||
slots - one per light, in the order the proxy lists them - and SWITCH_LIGHTS is the door to
|
||||
the page itself. */
|
||||
SWITCH_LIGHTS,
|
||||
LIGHT_1,
|
||||
LIGHT_2,
|
||||
LIGHT_3,
|
||||
LIGHT_4,
|
||||
LIGHT_5,
|
||||
LIGHT_6,
|
||||
LIGHT_7,
|
||||
LIGHT_8,
|
||||
LIGHT_9,
|
||||
LIGHT_10,
|
||||
LIGHT_11,
|
||||
LIGHT_12,
|
||||
}
|
||||
|
||||
// Must be evaluated in the reverse order of their values.
|
||||
@@ -493,6 +528,26 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
return eventKey(symbol, Event.ACTION, FLAG_SMALLER_FONT);
|
||||
}
|
||||
|
||||
/** A key on the lights page: one light, labelled with the name the proxy reported for it, already
|
||||
shortened to fit the key (see [LightsPage.displayName]). The label travels in the key value,
|
||||
so the page shows exactly the names the box gave us, and the state colour is applied when the
|
||||
key is drawn ([LightsPage.colorFor]). */
|
||||
public static KeyValue lightKey(String label, Event ev)
|
||||
{
|
||||
return eventKey(label, ev, FLAG_SMALLER_FONT);
|
||||
}
|
||||
|
||||
/** The state glyph of a lights key, drawn in the corner above the light's name. It carries
|
||||
FLAG_KEY_FONT - it is a glyph, not text - and that is what tells it apart from the name on the
|
||||
same key, so [LightsPage.isStateGlyph] can pick it out. Which of the two lamps is drawn is
|
||||
decided when the key is drawn, not here: a press or a poll changes the state under the key
|
||||
without the page being rebuilt. The value stored is the unlit lamp, so that anything that
|
||||
draws this key without asking for the state (a fallback, a preview) never shows a light on. */
|
||||
public static KeyValue lightGlyphKey(Event ev)
|
||||
{
|
||||
return eventKey(0xE026, ev, FLAG_KEY_FONT);
|
||||
}
|
||||
|
||||
/** Make a key that types a string. A char key is returned for a string of
|
||||
length 1. */
|
||||
public static KeyValue makeStringKey(String str, int flags)
|
||||
@@ -543,6 +598,10 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
public static final KeyValue CHANGE_METHOD_PREV = eventKey(0xE009, Event.CHANGE_METHOD_PREV, FLAG_SMALLER_FONT);
|
||||
public static final KeyValue CHANGE_METHOD_NEXT = eventKey(0xE009, Event.CHANGE_METHOD_NEXT, FLAG_SMALLER_FONT);
|
||||
public static final KeyValue VOICE_TYPING_CHOOSER = eventKey(0xE015, Event.SWITCH_VOICE_TYPING_CHOOSER, FLAG_SMALLER_FONT);
|
||||
/** The Roku page's mute key. It is also what a long press on the page's volume-down key
|
||||
sends (see [KeyModifier.modify_long_press]), so mute lives on the page without
|
||||
spending a slot on it; both uses share this one key. */
|
||||
public static final KeyValue ROKU_VOLUME_MUTE = eventKey("Mute", Event.ROKU_VOLMUTE, FLAG_SMALLER_FONT);
|
||||
public static final KeyValue COMPOSE_CANCEL = placeholderKey(0xE01A, Placeholder.COMPOSE_CANCEL, FLAG_SECONDARY);
|
||||
|
||||
public static KeyValue getSpecialKeyByName(String name)
|
||||
@@ -642,14 +701,51 @@ public final class KeyValue implements Comparable<KeyValue>
|
||||
/* Special event keys */
|
||||
case "config": return CONFIG;
|
||||
case "switch_text": return eventKey("ABC", Event.SWITCH_TEXT, FLAG_SMALLER_FONT);
|
||||
case "switch_fn": return eventKey("Fn", Event.SWITCH_FN, FLAG_SMALLER_FONT);
|
||||
case "switch_numeric": return eventKey("123+", Event.SWITCH_NUMERIC, FLAG_SMALLER_FONT);
|
||||
case "switch_emoji": return eventKey(0xE001, Event.SWITCH_EMOJI, FLAG_SMALLER_FONT);
|
||||
case "switch_emoji": return eventKey(0xE01D, Event.SWITCH_EMOJI, FLAG_SMALLER_FONT);
|
||||
case "switch_back_emoji": return eventKey("ABC", Event.SWITCH_BACK_EMOJI, 0);
|
||||
case "switch_clipboard": return eventKey(0xE017, Event.SWITCH_CLIPBOARD, 0);
|
||||
case "switch_back_clipboard": return eventKey("ABC", Event.SWITCH_BACK_CLIPBOARD, 0);
|
||||
case "switch_forward": return eventKey(0xE013, Event.SWITCH_FORWARD, FLAG_SMALLER_FONT);
|
||||
case "switch_backward": return eventKey(0xE014, Event.SWITCH_BACKWARD, FLAG_SMALLER_FONT);
|
||||
case "switch_greekmath": return eventKey("πλ∇¬", Event.SWITCH_GREEKMATH, FLAG_SMALLER_FONT);
|
||||
/* Opens the Roku remote page (res/xml/roku.xml). The glyph is the remote drawn
|
||||
at 0xE024 in the special font. */
|
||||
case "switch_roku": return eventKey(0xE024, Event.SWITCH_ROKU, FLAG_SMALLER_FONT);
|
||||
/* The remote's own keys. Labels only: the key sends an HTTP request instead of
|
||||
typing (see RokuRemote). */
|
||||
case "roku_home": return eventKey("Home", Event.ROKU_HOME, FLAG_SMALLER_FONT);
|
||||
case "roku_back": return eventKey("Back", Event.ROKU_BACK, FLAG_SMALLER_FONT);
|
||||
case "roku_up": return eventKey(0xE005, Event.ROKU_UP, FLAG_SMALLER_FONT);
|
||||
case "roku_down": return eventKey(0xE007, Event.ROKU_DOWN, FLAG_SMALLER_FONT);
|
||||
case "roku_left": return eventKey(0xE008, Event.ROKU_LEFT, FLAG_SMALLER_FONT);
|
||||
case "roku_right": return eventKey(0xE006, Event.ROKU_RIGHT, FLAG_SMALLER_FONT);
|
||||
case "roku_select": return eventKey("OK", Event.ROKU_SELECT, FLAG_SMALLER_FONT);
|
||||
case "roku_play": return eventKey("Play", Event.ROKU_PLAY, FLAG_SMALLER_FONT);
|
||||
case "roku_rewind": return eventKey("Rewind", Event.ROKU_REWIND, FLAG_SMALLER_FONT);
|
||||
case "roku_forward": return eventKey("Forward", Event.ROKU_FORWARD, FLAG_SMALLER_FONT);
|
||||
case "roku_power": return eventKey("Power", Event.ROKU_POWER, FLAG_SMALLER_FONT);
|
||||
/* The TV's volume, same ECP path as the rest of the page (RokuRemote sends the
|
||||
VolumeUp/VolumeDown/VolumeMute keypresses Harley's own web remote uses). */
|
||||
case "roku_volup": return eventKey("Vol+", Event.ROKU_VOLUP, FLAG_SMALLER_FONT);
|
||||
case "roku_voldown": return eventKey("Vol-", Event.ROKU_VOLDOWN, FLAG_SMALLER_FONT);
|
||||
case "roku_volmute": return ROKU_VOLUME_MUTE;
|
||||
/* The way back from the Roku page. The same event as "switch_text", which is
|
||||
unchanged, but labelled: the page has no config key, so this is the only exit
|
||||
and "ABC" does not read as one on a page full of remote keys. */
|
||||
case "roku_close": return eventKey("Keyboard", Event.SWITCH_TEXT, FLAG_SMALLER_FONT);
|
||||
/* The lights page (res/xml/lights.xml). The door is the lamp drawn at 0xE025 in the special
|
||||
font, in a corner slot of the Alt key. The three "light_N" names are the template's slot
|
||||
row: [LightsPage.build] repeats that row once per row of lights and replaces both values of
|
||||
each slot - the label with a light's name and the corner with its state glyph - so the
|
||||
value here only has to exist for the XML to parse. Which lights those slots are is decided
|
||||
by the proxy's answer, never by a name written down here. */
|
||||
case "switch_lights": return eventKey(0xE025, Event.SWITCH_LIGHTS, FLAG_SMALLER_FONT);
|
||||
case "lights_close": return eventKey("Keyboard", Event.SWITCH_TEXT, FLAG_SMALLER_FONT);
|
||||
case "light_1": return eventKey(LightsPage.PLACEHOLDER, Event.LIGHT_1, FLAG_SMALLER_FONT);
|
||||
case "light_2": return eventKey(LightsPage.PLACEHOLDER, Event.LIGHT_2, FLAG_SMALLER_FONT);
|
||||
case "light_3": return eventKey(LightsPage.PLACEHOLDER, Event.LIGHT_3, FLAG_SMALLER_FONT);
|
||||
case "change_method": return CHANGE_METHOD;
|
||||
case "change_method_prev": return CHANGE_METHOD_PREV;
|
||||
case "change_method_next": return CHANGE_METHOD_NEXT;
|
||||
|
||||
@@ -35,6 +35,58 @@ import juloo.keyboard2.suggestions.Suggestions;
|
||||
public class Keyboard2 extends InputMethodService
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
/** The ribbon follows the server's 1 Hz sampling, so it crawls instead of jumping. */
|
||||
private static final long HOST_GRAPH_REFRESH_MS = 1000L;
|
||||
|
||||
/** Re-armed only while the keyboard is on screen, so hiding it stops the polling dead. */
|
||||
private final Runnable _hostGraphRefresh = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
if (!_keyboard_layout_view.isShown())
|
||||
return;
|
||||
if (_config.usageGaugeEnabled)
|
||||
HostGraph.fetchIfNeeded(HostGraph.baseOf(_config.usageGaugeUrl), _config.usageGaugeToken,
|
||||
() -> _handler.post(() -> _keyboard_layout_view.invalidate()));
|
||||
_handler.postDelayed(this, HOST_GRAPH_REFRESH_MS);
|
||||
}
|
||||
};
|
||||
/** The lights page (res/xml/lights.xml): a page of its own, opened by the switch_lights key.
|
||||
Non-null means it is the page on screen, and only then is [LightsProxy] asked for anything -
|
||||
once per [LightsProxy.POLL_MS], because a light changes rarely and the usage gauge's 1 Hz
|
||||
would be absurd here. */
|
||||
private KeyboardData _lightsLayout = null;
|
||||
/** The [LightsProxy.version] the open lights page was built from. A different version means a
|
||||
light was added, removed or renamed under the page, so it has to be rebuilt. */
|
||||
private int _lightsVersion = -1;
|
||||
|
||||
/** Re-armed only while the lights page is on screen, so closing it stops the polling dead. */
|
||||
private final Runnable _lightsRefresh = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
if (_lightsLayout == null || !_keyboard_layout_view.isShown())
|
||||
return;
|
||||
LightsProxy.fetchIfNeeded(false, () -> _handler.post(Keyboard2.this::onLightsLoaded));
|
||||
_handler.postDelayed(this, LightsProxy.POLL_MS);
|
||||
}
|
||||
};
|
||||
|
||||
/** Called on the main thread when a lights read or a press has finished: repaint, and rebuild
|
||||
the page if the light list itself changed. */
|
||||
void onLightsLoaded()
|
||||
{
|
||||
if (_keyboard_layout_view == null)
|
||||
return;
|
||||
if (_lightsLayout != null && _lightsVersion != LightsProxy.version())
|
||||
{
|
||||
_lightsVersion = LightsProxy.version();
|
||||
_lightsLayout = loadLightsLayout();
|
||||
_keyboard_layout_view.setKeyboard(_lightsLayout);
|
||||
}
|
||||
_keyboard_layout_view.invalidate();
|
||||
}
|
||||
|
||||
/** The view containing the keyboard and candidates view. */
|
||||
private ViewGroup _keyboard_container_view;
|
||||
private Keyboard2View _keyboard_layout_view;
|
||||
@@ -50,6 +102,16 @@ public class Keyboard2 extends InputMethodService
|
||||
private ViewGroup _emojiPane = null;
|
||||
private ViewGroup _clipboard_pane = null;
|
||||
private Handler _handler;
|
||||
private final Runnable _peakIndicatorRefresh = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (_keyboard_layout_view != null)
|
||||
_keyboard_layout_view.invalidate();
|
||||
_handler.postDelayed(this, 60000);
|
||||
}
|
||||
};
|
||||
|
||||
private Config _config;
|
||||
|
||||
@@ -83,6 +145,7 @@ public class Keyboard2 extends InputMethodService
|
||||
{
|
||||
_config.set_current_layout(l);
|
||||
_currentSpecialLayout = null;
|
||||
_lightsLayout = null; // the lights page is not the page any more
|
||||
_keyboard_layout_view.setKeyboard(current_layout());
|
||||
}
|
||||
|
||||
@@ -94,6 +157,9 @@ public class Keyboard2 extends InputMethodService
|
||||
|
||||
void setSpecialLayout(KeyboardData l)
|
||||
{
|
||||
/* Any other page replaces the lights page, and only the lights page polls. */
|
||||
if (l != _lightsLayout)
|
||||
_lightsLayout = null;
|
||||
_currentSpecialLayout = l;
|
||||
_keyboard_layout_view.setKeyboard(l);
|
||||
}
|
||||
@@ -116,6 +182,33 @@ public class Keyboard2 extends InputMethodService
|
||||
R.xml.numeric_landscape : R.xml.numeric);
|
||||
}
|
||||
|
||||
KeyboardData loadFnLayout()
|
||||
{
|
||||
return LayoutModifier.modify_layout(
|
||||
KeyboardData.load(getResources(), R.xml.fn));
|
||||
}
|
||||
|
||||
/** The Roku remote: a whole page of keys that send commands instead of typing.
|
||||
Loaded as a plain layout ([loadLayout]), deliberately NOT through
|
||||
[LayoutModifier.modify_layout]: the page is nothing but remote keys, so the
|
||||
config key the modifier force-adds and the extra keys it drops into the free
|
||||
corner slots must stay off it. The page's own 'roku_close' key
|
||||
(Event.SWITCH_TEXT, labelled 'Keyboard') is the way back to the text layout. */
|
||||
KeyboardData loadRokuLayout()
|
||||
{
|
||||
return loadLayout(R.xml.roku);
|
||||
}
|
||||
|
||||
/** The lights page: the skeleton in res/xml/lights.xml with one key per light the proxy last
|
||||
reported (see [LightsPage.build]). Loaded as a plain layout for the same reason as the Roku
|
||||
page: the page is its own keys and the way back, so the config key and the user's extra keys
|
||||
must stay off it. It is rebuilt - not modified - when the light list changes, which is why
|
||||
each light's name and slot live in its key values. */
|
||||
KeyboardData loadLightsLayout()
|
||||
{
|
||||
return LightsPage.build(loadLayout(R.xml.lights), LightsProxy.lights());
|
||||
}
|
||||
|
||||
KeyboardData loadPinentry(int layout_id)
|
||||
{
|
||||
return LayoutModifier.modify_pinentry(KeyboardData.load(getResources(), layout_id),
|
||||
@@ -148,6 +241,7 @@ public class Keyboard2 extends InputMethodService
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
_handler.removeCallbacks(_peakIndicatorRefresh);
|
||||
super.onDestroy();
|
||||
|
||||
_foldStateTracker.close();
|
||||
@@ -254,10 +348,21 @@ public class Keyboard2 extends InputMethodService
|
||||
_config.editor_config.refresh(info, getResources());
|
||||
refresh_config();
|
||||
_currentSpecialLayout = refresh_special_layout();
|
||||
_lightsLayout = null; // a fresh input session starts on a text layout
|
||||
_keyboard_layout_view.setKeyboard(current_layout());
|
||||
_keyeventhandler.started(_config);
|
||||
setInputView(_keyboard_container_view);
|
||||
Logs.debug_startup_input_view(info, _config);
|
||||
_handler.removeCallbacks(_peakIndicatorRefresh);
|
||||
_handler.postDelayed(_peakIndicatorRefresh, 60000);
|
||||
if (_config.usageGaugeEnabled)
|
||||
UsageGauge.fetchIfNeeded(_config.usageGaugeUrl, _config.usageGaugeToken,
|
||||
() -> _handler.post(() -> _keyboard_layout_view.invalidate()));
|
||||
if (_config.usageGaugeEnabled)
|
||||
HostGraph.fetchIfNeeded(HostGraph.baseOf(_config.usageGaugeUrl), _config.usageGaugeToken,
|
||||
() -> _handler.post(() -> _keyboard_layout_view.invalidate()));
|
||||
_handler.removeCallbacks(_hostGraphRefresh);
|
||||
_handler.postDelayed(_hostGraphRefresh, HOST_GRAPH_REFRESH_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -357,6 +462,7 @@ public class Keyboard2 extends InputMethodService
|
||||
@Override
|
||||
public void onFinishInputView(boolean finishingInput)
|
||||
{
|
||||
_handler.removeCallbacks(_peakIndicatorRefresh);
|
||||
super.onFinishInputView(finishingInput);
|
||||
_keyboard_layout_view.reset();
|
||||
}
|
||||
@@ -418,6 +524,7 @@ public class Keyboard2 extends InputMethodService
|
||||
|
||||
case SWITCH_TEXT:
|
||||
_currentSpecialLayout = null;
|
||||
_lightsLayout = null; // the way back from the lights page: stop its polling
|
||||
_keyboard_layout_view.setKeyboard(current_layout());
|
||||
break;
|
||||
|
||||
@@ -425,6 +532,47 @@ public class Keyboard2 extends InputMethodService
|
||||
setSpecialLayout(loadNumericLayout());
|
||||
break;
|
||||
|
||||
case SWITCH_FN:
|
||||
setSpecialLayout(loadFnLayout());
|
||||
break;
|
||||
|
||||
case SWITCH_ROKU:
|
||||
setSpecialLayout(loadRokuLayout());
|
||||
break;
|
||||
|
||||
case SWITCH_LIGHTS:
|
||||
/* Opening the page asks at once - its keys are drawn from the last snapshot and this is
|
||||
the moment to make it current - and then re-arms the slow poll. */
|
||||
_lightsVersion = LightsProxy.version();
|
||||
_lightsLayout = loadLightsLayout();
|
||||
setSpecialLayout(_lightsLayout);
|
||||
LightsProxy.fetchIfNeeded(true, () -> _handler.post(() -> onLightsLoaded()));
|
||||
_handler.removeCallbacks(_lightsRefresh);
|
||||
_handler.postDelayed(_lightsRefresh, LightsProxy.POLL_MS);
|
||||
break;
|
||||
|
||||
/* One key per light. Like the Roku keys, a press only hands the work to a worker thread
|
||||
and returns: the POST waits for Home Assistant to settle, which is seconds, and
|
||||
[handle_event_key] runs on the main thread. A press is never retried (see
|
||||
[LightsProxy.toggle]), and a second press on a light whose toggle is still in flight is
|
||||
refused - that would flip the light back exactly as a retry would. */
|
||||
case LIGHT_1: case LIGHT_2: case LIGHT_3: case LIGHT_4:
|
||||
case LIGHT_5: case LIGHT_6: case LIGHT_7: case LIGHT_8:
|
||||
case LIGHT_9: case LIGHT_10: case LIGHT_11: case LIGHT_12:
|
||||
LightsProxy.toggle(LightsPage.slotOf(ev), () -> _handler.post(() -> onLightsLoaded()));
|
||||
break;
|
||||
|
||||
/* Roku remote commands. handle_event_key runs on the main thread, so these
|
||||
only hand the key to RokuRemote and return: the POST happens on a worker
|
||||
thread and a failure there is nobody's problem but its own. */
|
||||
case ROKU_HOME: case ROKU_BACK:
|
||||
case ROKU_UP: case ROKU_DOWN: case ROKU_LEFT: case ROKU_RIGHT:
|
||||
case ROKU_SELECT: case ROKU_PLAY: case ROKU_REWIND: case ROKU_FORWARD:
|
||||
case ROKU_VOLUP: case ROKU_VOLDOWN: case ROKU_VOLMUTE:
|
||||
case ROKU_POWER:
|
||||
RokuRemote.press(ev);
|
||||
break;
|
||||
|
||||
case SWITCH_EMOJI:
|
||||
if (_emojiPane == null)
|
||||
_emojiPane = (ViewGroup)inflate_view(R.layout.emoji_pane);
|
||||
|
||||
@@ -2,13 +2,20 @@ package juloo.keyboard2;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.net.Uri;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.MotionEvent;
|
||||
@@ -19,6 +26,7 @@ import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Keyboard2View extends View
|
||||
implements View.OnTouchListener, Pointers.IPointerEventHandler
|
||||
@@ -36,6 +44,13 @@ public class Keyboard2View extends View
|
||||
|
||||
private Pointers.Modifiers _mods;
|
||||
|
||||
/** Long-pressing the usage gauge key opens the usage page; a tap stays inert. */
|
||||
private final Handler _gaugeHandler = new Handler(Looper.getMainLooper());
|
||||
private Runnable _gaugeLongPress;
|
||||
/** Its own paint, so the ribbon cannot disturb whatever the key drawing left configured. */
|
||||
private final Paint _ribbonPaint = new Paint();
|
||||
private boolean _gaugePressed;
|
||||
|
||||
private static int _currentWhat = 0;
|
||||
|
||||
private Config _config;
|
||||
@@ -54,6 +69,13 @@ public class Keyboard2View extends View
|
||||
private Theme.Computed _tc;
|
||||
|
||||
private static RectF _tmpRect = new RectF();
|
||||
private static final Paint GAUGE_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
/** The ring around the spend number gets its own paint, so the number's own settings and its
|
||||
halo cannot be disturbed by whatever the ring leaves configured. */
|
||||
private static final Paint GAUGE_RING_PAINT = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private static final Path GAUGE_PATH = new Path();
|
||||
/** Reused for measuring the number. Nothing on the draw path allocates. */
|
||||
private static final Rect _gaugeTextBounds = new Rect();
|
||||
|
||||
enum Vertical
|
||||
{
|
||||
@@ -199,6 +221,7 @@ public class Keyboard2View extends View
|
||||
{
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
cancelGaugePress();
|
||||
_pointers.onTouchUp(event.getPointerId(event.getActionIndex()));
|
||||
break;
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
@@ -207,14 +230,23 @@ public class Keyboard2View extends View
|
||||
float tx = event.getX(p);
|
||||
float ty = event.getY(p);
|
||||
KeyboardData.Key key = getKeyAtPosition(tx, ty);
|
||||
if (key != null)
|
||||
if (key != null && UsageGauge.isGaugeKey(key))
|
||||
startGaugePress();
|
||||
else if (key != null)
|
||||
_pointers.onTouchDown(tx, ty, event.getPointerId(p), key);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (_gaugePressed)
|
||||
{
|
||||
KeyboardData.Key moved = getKeyAtPosition(event.getX(0), event.getY(0));
|
||||
if (moved == null || !UsageGauge.isGaugeKey(moved))
|
||||
cancelGaugePress(); // slid off the gauge: no longer a press on it
|
||||
}
|
||||
for (p = 0; p < event.getPointerCount(); p++)
|
||||
_pointers.onTouchMove(event.getX(p), event.getY(p), event.getPointerId(p));
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
cancelGaugePress();
|
||||
_pointers.onTouchCancel();
|
||||
break;
|
||||
default:
|
||||
@@ -351,8 +383,13 @@ public class Keyboard2View extends View
|
||||
x += k.shift * _keyWidth;
|
||||
float keyW = _keyWidth * k.width - _tc.horizontal_margin;
|
||||
boolean isKeyDown = _pointers.isKeyDown(k);
|
||||
// A key whose Roku command has been sent wears the pressed look until the TV
|
||||
// answers, so a slow command does not look like a dead key. Same colours as a
|
||||
// real press (key_activated + the activated label), which is the only pair the
|
||||
// theme guarantees to be readable together.
|
||||
boolean isSent = RokuRemote.isLit(k.keys[0], System.currentTimeMillis());
|
||||
Theme.Computed.Key tc_key;
|
||||
if (isKeyDown)
|
||||
if (isKeyDown || isSent)
|
||||
tc_key = _tc.key_activated;
|
||||
else
|
||||
switch (k.role)
|
||||
@@ -364,20 +401,320 @@ public class Keyboard2View extends View
|
||||
case Normal: tc_key = _tc.key; break;
|
||||
}
|
||||
drawKeyFrame(canvas, x, y, keyW, keyH, tc_key);
|
||||
if (k.keys[0] != null)
|
||||
if (k.role == KeyboardData.Key.Role.Space_bar)
|
||||
drawHostRibbon(canvas, x, y, keyW, keyH);
|
||||
if (UsageGauge.isGaugeKey(k))
|
||||
drawUsageGauge(canvas, x, y, keyW, keyH, tc_key);
|
||||
else if (k.keys[0] != null)
|
||||
drawLabel(canvas, k.keys[0], keyW / 2f + x, y, keyH, isKeyDown, tc_key);
|
||||
for (int i = 1; i < 9; i++)
|
||||
{
|
||||
if (k.keys[i] != null)
|
||||
drawSubLabel(canvas, k.keys[i], x, y, keyW, keyH, i, isKeyDown, tc_key);
|
||||
}
|
||||
drawIndication(canvas, k, x, y, keyW, keyH, _tc);
|
||||
if (!UsageGauge.isGaugeKey(k))
|
||||
drawIndication(canvas, k, x, y, keyW, keyH, _tc);
|
||||
x += _keyWidth * k.width;
|
||||
}
|
||||
y += row.height * _tc.row_height;
|
||||
}
|
||||
}
|
||||
|
||||
/** Holding the gauge key opens the usage page. Short taps do nothing: the key never types. */
|
||||
private void startGaugePress()
|
||||
{
|
||||
cancelGaugePress();
|
||||
_gaugePressed = true;
|
||||
_gaugeLongPress = new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
if (!_gaugePressed)
|
||||
return;
|
||||
_gaugePressed = false;
|
||||
_gaugeLongPress = null;
|
||||
openUsagePage();
|
||||
}
|
||||
};
|
||||
// Same delay the keyboard uses for every other long press, so it feels like the rest of it.
|
||||
_gaugeHandler.postDelayed(_gaugeLongPress, _config != null ? _config.longPressTimeout : 600L);
|
||||
}
|
||||
|
||||
private void cancelGaugePress()
|
||||
{
|
||||
_gaugePressed = false;
|
||||
if (_gaugeLongPress != null)
|
||||
{
|
||||
_gaugeHandler.removeCallbacks(_gaugeLongPress);
|
||||
_gaugeLongPress = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void openUsagePage()
|
||||
{
|
||||
try
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(UsageGauge.USAGE_PAGE_URL));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// No browser, or the launcher refused: the keyboard must never crash over a long press.
|
||||
}
|
||||
}
|
||||
|
||||
/** Two smooth lanes along the bottom of the space bar: cpu utilisation as a filled neon wave,
|
||||
the disk stall as a cyan line riding the same curve. Columns became a curve because a row
|
||||
of separate bars read as a bar chart; the ask was a colour field, like a picker.
|
||||
*
|
||||
* Nothing at all is drawn without data: a missing ribbon is the honest "cannot reach Utumno",
|
||||
* while a live-but-idle host still draws its baseline, so blank is never read as zero. */
|
||||
/** Cached so the draw path allocates nothing: it only changes when the key geometry does. */
|
||||
/** Where the ribbon reaches the theme's pink; lower starts the colour change sooner. */
|
||||
private static final float RIBBON_PINK_AT = 0.3f;
|
||||
|
||||
/** Reused every frame so the smooth wave never allocates on the draw path. */
|
||||
private final android.graphics.Path _ribbonPath = new android.graphics.Path();
|
||||
private android.graphics.Shader _ribbonShader;
|
||||
private float _ribbonShaderBottom = -1f;
|
||||
private float _ribbonShaderHeight = -1f;
|
||||
|
||||
/** The ribbon's own ramp in the keyboard's colours: orchid violet at rest, through the
|
||||
theme's pink at half, to the hot pink that peaks the space bar glyph when the box is flat out. */
|
||||
private static int ribbonColor(float level)
|
||||
{
|
||||
level = Math.max(0f, Math.min(1f, level));
|
||||
if (level < RIBBON_PINK_AT)
|
||||
// 0xFFC67DFF is the theme's own alternate violet: same family, but its luma is up with
|
||||
// the cyan stall line, so a low-utilisation wave still reads instead of looking dim.
|
||||
return ribbonLerp(0xFFC67DFF, 0xFFFF2D95, level / RIBBON_PINK_AT);
|
||||
return ribbonLerp(0xFFFF2D95, 0xFFFF2FF0, (level - RIBBON_PINK_AT) / (1f - RIBBON_PINK_AT));
|
||||
}
|
||||
|
||||
private static int ribbonLerp(int from, int to, float t)
|
||||
{
|
||||
int r = (int) ((from >> 16 & 0xFF) + (((to >> 16 & 0xFF) - (from >> 16 & 0xFF)) * t));
|
||||
int g = (int) ((from >> 8 & 0xFF) + (((to >> 8 & 0xFF) - (from >> 8 & 0xFF)) * t));
|
||||
int b = (int) ((from & 0xFF) + (((to & 0xFF) - (from & 0xFF)) * t));
|
||||
return 0xFF000000 | r << 16 | g << 8 | b;
|
||||
}
|
||||
|
||||
private void drawHostRibbon(Canvas canvas, float x, float y, float keyW, float keyH)
|
||||
{
|
||||
if (!HostGraph.hasData())
|
||||
return;
|
||||
float[] util = HostGraph.series(2);
|
||||
if (util.length == 0)
|
||||
return;
|
||||
float left = x + _tc.horizontal_margin;
|
||||
float right = x + keyW - _tc.horizontal_margin;
|
||||
float width = right - left;
|
||||
if (width <= 4f)
|
||||
return;
|
||||
float bottom = y + keyH - 1f;
|
||||
// One gradient the whole way up, keyed to *height* rather than to each sample's own value, so
|
||||
// a taller wave reveals more of the ramp. Built once per key geometry.
|
||||
if (_ribbonShader == null || _ribbonShaderBottom != bottom || _ribbonShaderHeight != keyH)
|
||||
{
|
||||
_ribbonShader = new android.graphics.LinearGradient(0f, bottom, 0f, bottom - keyH,
|
||||
new int[] { ribbonColor(0f), ribbonColor(RIBBON_PINK_AT), ribbonColor(1f) },
|
||||
new float[] { 0f, RIBBON_PINK_AT, 1f }, android.graphics.Shader.TileMode.CLAMP);
|
||||
_ribbonShaderBottom = bottom;
|
||||
_ribbonShaderHeight = keyH;
|
||||
}
|
||||
_ribbonPaint.setAntiAlias(true);
|
||||
_ribbonPaint.setShader(_ribbonShader);
|
||||
_ribbonPaint.setStyle(android.graphics.Paint.Style.FILL);
|
||||
buildWave(_ribbonPath, util, left, width, bottom, keyH, true);
|
||||
canvas.drawPath(_ribbonPath, _ribbonPaint);
|
||||
// Second lane: how much of each second was spent *waiting* on the disk. Utilisation says
|
||||
// "busy"; this says "stuck", and only this one tells the two apart.
|
||||
float[] stall = HostGraph.series(0);
|
||||
if (stall.length > 0)
|
||||
{
|
||||
_ribbonPaint.setShader(null);
|
||||
_ribbonPaint.setColor(0xFF00E5FF);
|
||||
_ribbonPaint.setStyle(android.graphics.Paint.Style.STROKE);
|
||||
_ribbonPaint.setStrokeWidth(Math.max(2f, keyH * 0.03f));
|
||||
_ribbonPaint.setStrokeCap(android.graphics.Paint.Cap.ROUND);
|
||||
_ribbonPaint.setStrokeJoin(android.graphics.Paint.Join.ROUND);
|
||||
buildWave(_ribbonPath, stall, left, width, bottom, keyH, false);
|
||||
canvas.drawPath(_ribbonPath, _ribbonPaint);
|
||||
_ribbonPaint.setStyle(android.graphics.Paint.Style.FILL);
|
||||
}
|
||||
// The baseline says "connected". Without it a blank space bar would read as zero load.
|
||||
_ribbonPaint.setStrokeWidth(1f);
|
||||
_ribbonPaint.setColor(0x55FFFFFF);
|
||||
canvas.drawRect(left, bottom - 1f, right, bottom, _ribbonPaint);
|
||||
}
|
||||
|
||||
/** A smooth curve through the samples, filled to the bottom when `area`.
|
||||
*
|
||||
* Quadratic segments whose control points are the samples and whose ends are the midpoints:
|
||||
* that is what removes the corner at each sample and turns a row of columns into one wave. */
|
||||
private static void buildWave(android.graphics.Path path, float[] series, float left, float width,
|
||||
float bottom, float full, boolean area)
|
||||
{
|
||||
int n = series.length;
|
||||
if (n == 0)
|
||||
return;
|
||||
path.reset();
|
||||
float dx = n > 1 ? width / (n - 1) : width;
|
||||
float px = left;
|
||||
float py = bottom - ribbonLevel(series[0]) * full;
|
||||
path.moveTo(px, py);
|
||||
if (area)
|
||||
path.lineTo(px, bottom);
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
float cx = left + i * dx;
|
||||
float cy = bottom - ribbonLevel(series[i]) * full;
|
||||
if (i < n - 1)
|
||||
path.quadTo(px, py, (px + cx) / 2f, (py + cy) / 2f);
|
||||
else
|
||||
path.quadTo(px, py, cx, cy);
|
||||
px = cx;
|
||||
py = cy;
|
||||
}
|
||||
if (area)
|
||||
{
|
||||
path.lineTo(left + width, bottom);
|
||||
path.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static float ribbonLevel(float v)
|
||||
{
|
||||
return Math.max(0f, Math.min(100f, v)) / 100f;
|
||||
}
|
||||
|
||||
private void drawUsageGauge(Canvas canvas, float x, float y, float keyW,
|
||||
float keyH, Theme.Computed.Key tc)
|
||||
{
|
||||
UsageGauge.Reading reading = UsageGauge.cachedReading();
|
||||
boolean stale = !_config.usageGaugeEnabled
|
||||
|| UsageGauge.isStaleNow(_config.usageGaugeToken);
|
||||
float inset = Math.max(1.f, tc.border_width);
|
||||
RectF bounds = new RectF(x + inset, y + inset, x + keyW - inset, y + keyH - inset);
|
||||
GAUGE_PATH.reset();
|
||||
GAUGE_PATH.addRoundRect(bounds, tc.border_radius, tc.border_radius, Path.Direction.CW);
|
||||
canvas.save();
|
||||
canvas.clipPath(GAUGE_PATH);
|
||||
if (stale || reading == null)
|
||||
{
|
||||
GAUGE_PAINT.setShader(null);
|
||||
GAUGE_PAINT.setColor(0xff55585e);
|
||||
canvas.drawRect(bounds, GAUGE_PAINT);
|
||||
}
|
||||
else
|
||||
{
|
||||
float level = UsageGauge.fillFraction(reading);
|
||||
int hue = UsageGauge.hueColor(level);
|
||||
GAUGE_PAINT.setColor(tc.bg_paint.getColor());
|
||||
canvas.drawRect(bounds, GAUGE_PAINT);
|
||||
if (level > 0)
|
||||
{
|
||||
float fillTop = bounds.bottom - bounds.height() * level;
|
||||
int darker = blendColor(hue, 0xff000000, 0.22f);
|
||||
GAUGE_PAINT.setShader(new LinearGradient(0, bounds.bottom, 0, fillTop,
|
||||
darker, hue, Shader.TileMode.CLAMP));
|
||||
canvas.drawRect(bounds.left, fillTop, bounds.right, bounds.bottom, GAUGE_PAINT);
|
||||
GAUGE_PAINT.setShader(null);
|
||||
}
|
||||
}
|
||||
String label = reading == null ? "—" : String.format(Locale.US, "%.1f", reading.spentTodayUsd);
|
||||
float size = Math.min(_subLabelSize, keyW * 0.40f);
|
||||
GAUGE_PAINT.setTextSize(size);
|
||||
GAUGE_PAINT.setTypeface(android.graphics.Typeface.DEFAULT);
|
||||
GAUGE_PAINT.setTextAlign(Paint.Align.CENTER);
|
||||
// Stale hides the number behind a dash; a partial day keeps the number but dims it, so a
|
||||
// floor can never be read as the day's total. A low balance stays red: that reading is live.
|
||||
int labelColor = stale ? 0xff92949a :
|
||||
(UsageGauge.isLowBalance(reading) ? 0xffe53935 :
|
||||
(reading.partial ? 0xff92949a : _theme.labelColor));
|
||||
if (!stale)
|
||||
{
|
||||
// The ring is the only part of this key that can carry the low-balance warning as a
|
||||
// colour: the number itself is a stroked outline whose colour comes from luminance
|
||||
// so it stays legible over the ramped fill, which means a red label has nowhere
|
||||
// else to show and only flips that outline white.
|
||||
drawGaugeRing(canvas, label, bounds, size, y, keyH,
|
||||
UsageGauge.isLowBalance(reading) ? 0xffe53935 : _theme.subLabelColor);
|
||||
// The outline is derived from the theme's own label colour and deliberately NOT from
|
||||
// labelColor: otherwise a low balance would flip this outline white, which changes how
|
||||
// the number looks. The ring carries the red; the number keeps its normal appearance.
|
||||
int outlineBase = stale || reading.partial ? 0xff92949a : _theme.labelColor;
|
||||
int luminance = (android.graphics.Color.red(outlineBase) * 299
|
||||
+ android.graphics.Color.green(outlineBase) * 587
|
||||
+ android.graphics.Color.blue(outlineBase) * 114) / 1000;
|
||||
GAUGE_PAINT.setStyle(Paint.Style.STROKE);
|
||||
GAUGE_PAINT.setStrokeWidth(Math.max(1.f, size * 0.12f));
|
||||
GAUGE_PAINT.setColor(luminance > 140 ? 0xff202020 : 0xfff5f5f5);
|
||||
canvas.drawText(label, bounds.centerX(),
|
||||
(keyH - GAUGE_PAINT.ascent() - GAUGE_PAINT.descent()) / 2f + y, GAUGE_PAINT);
|
||||
GAUGE_PAINT.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
GAUGE_PAINT.setColor(labelColor);
|
||||
canvas.drawText(label, bounds.centerX(),
|
||||
(keyH - GAUGE_PAINT.ascent() - GAUGE_PAINT.descent()) / 2f + y, GAUGE_PAINT);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/** A ring around the spend number, in the theme's alternate violet.
|
||||
*
|
||||
* [GAUGE_PAINT] must already carry the label's size and typeface, and [bounds], [y], [keyH] the
|
||||
* key's own geometry: the radius, the centre and the baseline are all measured from those rather
|
||||
* than assumed, so the ring wraps the glyphs the device's font actually draws.
|
||||
*
|
||||
* The radius is the ink box's half-diagonal plus the gap, so the gap is exact at the ink's
|
||||
* corners - the furthest ink from the centre - and larger everywhere else. Centring on the ink
|
||||
* box's own middle rather than on the line box matters for digits, which have no descender: the
|
||||
* line box would pull the ring down by half a descent and eat the gap above the number.
|
||||
*
|
||||
* The dark casing under the coloured stroke is the trick the number's own halo already uses, and
|
||||
* for the same reason: the fill behind it runs from cyan or violet through amber to red, and a
|
||||
* lone violet stroke would disappear into part of that ramp. */
|
||||
private void drawGaugeRing(Canvas canvas, String label, RectF bounds, float size,
|
||||
float y, float keyH, int ringColor)
|
||||
{
|
||||
GAUGE_PAINT.getTextBounds(label, 0, label.length(), _gaugeTextBounds);
|
||||
float halfW = _gaugeTextBounds.width() / 2f;
|
||||
float halfH = _gaugeTextBounds.height() / 2f;
|
||||
// Align.CENTER centres the advance, not the ink: recentre on the ink box's own middle.
|
||||
float cx = bounds.centerX() - GAUGE_PAINT.measureText(label) / 2f
|
||||
+ (_gaugeTextBounds.left + _gaugeTextBounds.right) / 2f;
|
||||
// The number's own baseline, plus the ink box's own middle: see the comment above.
|
||||
float cy = (keyH - GAUGE_PAINT.ascent() - GAUGE_PAINT.descent()) / 2f + y
|
||||
+ (_gaugeTextBounds.top + _gaugeTextBounds.bottom) / 2f;
|
||||
float gap = Math.max(1.5f, size * 0.08f);
|
||||
float stroke = Math.max(1.5f, size * 0.07f);
|
||||
float casing = Math.max(1f, size * 0.02f);
|
||||
float radius = (float)Math.sqrt(halfW * halfW + halfH * halfH) + gap + casing + stroke / 2f;
|
||||
// A ring squeezed inwards would cut across the digits it frames, which is worse than no ring,
|
||||
// so refuse it rather than clamp it; either way keep a margin off the key's own edge. A circle
|
||||
// wide enough to enclose a five-character total needs more width than the key has.
|
||||
float room = Math.min(Math.min(cx - bounds.left, bounds.right - cx),
|
||||
Math.min(cy - bounds.top, bounds.bottom - cy)) - Math.max(1f, size * 0.03f);
|
||||
if (radius + stroke / 2f + casing > room)
|
||||
return;
|
||||
GAUGE_RING_PAINT.setStyle(Paint.Style.STROKE);
|
||||
GAUGE_RING_PAINT.setStrokeWidth(stroke + 2f * casing);
|
||||
GAUGE_RING_PAINT.setColor(0xff202020);
|
||||
canvas.drawCircle(cx, cy, radius, GAUGE_RING_PAINT);
|
||||
GAUGE_RING_PAINT.setStrokeWidth(stroke);
|
||||
GAUGE_RING_PAINT.setColor(ringColor);
|
||||
canvas.drawCircle(cx, cy, radius, GAUGE_RING_PAINT);
|
||||
}
|
||||
|
||||
private static int blendColor(int from, int to, float amount)
|
||||
{
|
||||
return android.graphics.Color.rgb(
|
||||
(int)(android.graphics.Color.red(from) * (1 - amount) + android.graphics.Color.red(to) * amount),
|
||||
(int)(android.graphics.Color.green(from) * (1 - amount) + android.graphics.Color.green(to) * amount),
|
||||
(int)(android.graphics.Color.blue(from) * (1 - amount) + android.graphics.Color.blue(to) * amount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow()
|
||||
{
|
||||
@@ -415,6 +752,22 @@ public class Keyboard2View extends View
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/** True for the four direction keys of the Roku page (res/xml/roku.xml), matched by
|
||||
EVENT and never by glyph: 0xE005-0xE008 are ordinary arrow glyphs that the default
|
||||
layouts and other pages also use, and those must keep their own colours.
|
||||
[KeyValue.getEvent()] is only valid for its own kind, so the kind is checked first,
|
||||
the same guard [RokuRemote.isLit] uses. */
|
||||
private static boolean isRokuDirection(KeyValue k)
|
||||
{
|
||||
if (k.getKind() != KeyValue.Kind.Event)
|
||||
return false;
|
||||
switch (k.getEvent())
|
||||
{
|
||||
case ROKU_UP: case ROKU_DOWN: case ROKU_LEFT: case ROKU_RIGHT: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
private int labelColor(KeyValue k, boolean isKeyDown, boolean sublabel)
|
||||
{
|
||||
if (isKeyDown)
|
||||
@@ -428,6 +781,21 @@ public class Keyboard2View extends View
|
||||
}
|
||||
return _theme.pressedColor;
|
||||
}
|
||||
/* The Roku page's four direction keys are event keys, and every event key carries
|
||||
FLAG_SECONDARY, which dims them to secondaryLabelColor - a muted pink on the
|
||||
Orchid theme, which reads as "disabled" on the one page where they are the main
|
||||
controls. They get the theme's blue accent instead. Only these four events are
|
||||
affected: no other key on any page or layout changes colour. A press is answered
|
||||
above, and [drawLabel]'s RokuRemote.isLit() still overrides this, so neither the
|
||||
pressed look nor the sent acknowledgement changes. */
|
||||
if (isRokuDirection(k))
|
||||
return _theme.rokuArrowColor;
|
||||
/* The lights page's keys are event keys too, and every event key carries FLAG_SECONDARY, which
|
||||
would dim them all to the same secondary grey - useless on the one page whose whole point is
|
||||
telling one light's state from another's. They get the state's colour instead, from the one
|
||||
mapping in [LightsProxy.colorFor]. */
|
||||
if (LightsPage.isLightKey(k))
|
||||
return LightsPage.colorFor(k, _theme);
|
||||
if (k.hasFlagsAny(KeyValue.FLAG_SECONDARY | KeyValue.FLAG_GREYED))
|
||||
{
|
||||
if (k.hasFlagsAny(KeyValue.FLAG_GREYED))
|
||||
@@ -444,8 +812,36 @@ public class Keyboard2View extends View
|
||||
if (kv == null)
|
||||
return;
|
||||
float textSize = scaleTextSize(kv, true);
|
||||
Paint p = tc.label_paint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT), labelColor(kv, isKeyDown, false), textSize);
|
||||
canvas.drawText(kv.getString(), x, (keyH - p.ascent() - p.descent()) / 2f + y, p);
|
||||
// DeepSeek peak/off-peak: the space bar's joystick glyph doubles as the rate
|
||||
// indicator (neon blue = off-peak, neon pink = peak).
|
||||
int color = kv.getKind() == KeyValue.Kind.Editing
|
||||
&& KeyValue.Editing.SPACE_BAR.equals(kv.getEditing())
|
||||
? DeepSeekPeak.colorFor(System.currentTimeMillis())
|
||||
: labelColor(kv, isKeyDown, false);
|
||||
// The emoji badge (0xE01D, Alt's top-right corner) sits in a corner slot,
|
||||
// which labelColor() would dim to secondaryLabelColor. Draw it in labelColor
|
||||
// so it matches the rest of the keyboard's labels.
|
||||
String glyph = kv.getString();
|
||||
if (glyph != null && glyph.length() == 1 && glyph.charAt(0) == 0xE01D)
|
||||
color = _theme.labelColor;
|
||||
/* A lights key shows the light's name until a press has something to say. The answer to that
|
||||
press - a refusal, or that Home Assistant could not be read back - is then the honest thing
|
||||
to put on the key, in the stale colour, until the box reports a state for that light again. */
|
||||
String note = LightsPage.noteFor(kv, System.currentTimeMillis());
|
||||
if (note != null)
|
||||
{
|
||||
glyph = note;
|
||||
color = _theme.lightStaleColor;
|
||||
}
|
||||
// A Roku key wears the theme's activated colours for a moment after it is pressed:
|
||||
// the command goes out on a worker thread and a press against a sleeping TV takes
|
||||
// ~5 s to be answered, so without this a slow press looks like a dead key and gets
|
||||
// pressed twice. The state is one enum reference and one long in RokuRemote, written
|
||||
// by the worker thread and only read from the draw path.
|
||||
if (RokuRemote.isLit(kv, System.currentTimeMillis()))
|
||||
color = _theme.activatedColor;
|
||||
Paint p = tc.label_paint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT), color, textSize);
|
||||
canvas.drawText(glyph, x, (keyH - p.ascent() - p.descent()) / 2f + y, p);
|
||||
}
|
||||
|
||||
private void drawSubLabel(Canvas canvas, KeyValue kv, float x, float y,
|
||||
@@ -458,7 +854,23 @@ public class Keyboard2View extends View
|
||||
if (kv == null)
|
||||
return;
|
||||
float textSize = scaleTextSize(kv, false);
|
||||
Paint p = tc.sublabel_paint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT), labelColor(kv, isKeyDown, true), textSize, a);
|
||||
int color = labelColor(kv, isKeyDown, true);
|
||||
// Corner badges drawn with an icon glyph. labelColor() dims every key carrying
|
||||
// FLAG_SECONDARY to secondaryLabelColor, and every eventKey carries it, so the
|
||||
// emoji badge (0xE01D, Alt's top-right corner), the Roku badge (0xE024) and the
|
||||
// lights badge (0xE025, Alt's bottom-right corner) would all come out dim next to
|
||||
// the theme's own labels. drawLabel() has the same test for 0xE01D, but drawLabel()
|
||||
// is only ever called with k.keys[0]: a corner badge lives in k.keys[2] and is drawn
|
||||
// here, so that copy never runs.
|
||||
if (kv.getKind() == KeyValue.Kind.Event)
|
||||
{
|
||||
String badge = kv.getString();
|
||||
if (badge != null && badge.length() == 1
|
||||
&& (badge.charAt(0) == 0xE01D || badge.charAt(0) == 0xE024
|
||||
|| badge.charAt(0) == 0xE025))
|
||||
color = _theme.labelColor;
|
||||
}
|
||||
Paint p = tc.sublabel_paint(kv.hasFlagsAny(KeyValue.FLAG_KEY_FONT), color, textSize, a);
|
||||
float subPadding = _config.keyPadding;
|
||||
if (v == Vertical.CENTER)
|
||||
y += (keyH - p.ascent() - p.descent()) / 2f;
|
||||
@@ -468,7 +880,13 @@ public class Keyboard2View extends View
|
||||
x += keyW / 2f;
|
||||
else
|
||||
x += (a == Paint.Align.LEFT) ? subPadding : keyW - subPadding;
|
||||
String label = kv.getString();
|
||||
/* A lights key's state glyph is chosen as it is drawn, not when the page was built: a press
|
||||
or a poll changes the state under the key without the page being rebuilt, so what is stored
|
||||
in the key value is only a marker. [LightsPage.glyphFor] returns shared Strings, so this
|
||||
allocates nothing. */
|
||||
String label = LightsPage.isStateGlyph(kv)
|
||||
? LightsPage.glyphFor(LightsProxy.stateAt(LightsPage.slotOf(kv.getEvent())))
|
||||
: kv.getString();
|
||||
int label_len = label.length();
|
||||
// Limit the label of string keys to 3 characters
|
||||
if (label_len > 3 && kv.getKind() == KeyValue.Kind.String)
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** The lights page (res/xml/lights.xml): one key per light, plus the way back.
|
||||
*
|
||||
* The page is opened by the switch_lights key (the lamp glyph at 0xE025, in the free bottom-right
|
||||
* corner of the Alt key) and left by lights_close, which fires the same Event.SWITCH_TEXT as
|
||||
* roku_close. Like the Roku page it is loaded as a plain layout (Keyboard2.loadLightsLayout), NOT
|
||||
* through LayoutModifier.modify_layout, so the forced config key and the user's extra keys never
|
||||
* land on it: this page is its own keys and nothing else.
|
||||
*
|
||||
* The skeleton - a row of three slots and the full-width 'Keyboard' key - lives in the XML. This
|
||||
* class turns that skeleton into the live page: one key per light the proxy reported, three to a
|
||||
* row, with a short last row stretched to the full width. Each key carries two values:
|
||||
*
|
||||
* key0 the light's name, exactly as the registry gave it (shortened only when it would run
|
||||
* over the key), coloured by the light's state;
|
||||
* key7 the state glyph, in the special font: the lit lamp (0xE025) for a light that is on, the
|
||||
* unlit one (0xE026) for everything else. The glyph is chosen when the key is *drawn*, not
|
||||
* when the page is built, because a press and a poll change the state under the key
|
||||
* without the page being rebuilt.
|
||||
*
|
||||
* Before the proxy has answered, the template's three slots stand with a dash and the unlit lamp in
|
||||
* the stale look: nobody has said which lights exist, and an unlit lamp never claims otherwise.
|
||||
*
|
||||
* [build], [displayName], [glyphFor] and [colorFor] are pure, so the unit tests drive the real page
|
||||
* logic.
|
||||
*/
|
||||
public final class LightsPage
|
||||
{
|
||||
/** The most lights the page can show: the page's events are LIGHT_1..LIGHT_12. A whitelist
|
||||
longer than this is reported on the last key rather than silently dropped. */
|
||||
public static final int MAX_LIGHTS = 12;
|
||||
/** The template's slot row holds three keys; the page repeats it. */
|
||||
public static final int SLOTS_PER_ROW = 3;
|
||||
/** The template's corner slot for the state glyph. */
|
||||
public static final int GLYPH_SUB_INDEX = 7;
|
||||
/** A key is a third of the keyboard wide; a longer name would run over its neighbours. The name
|
||||
itself is whatever the registry says, never a mapping kept here. */
|
||||
public static final int MAX_NAME_CHARS = 16;
|
||||
/** Drawn on a slot before the proxy has named a light for it. */
|
||||
public static final String PLACEHOLDER = "—";
|
||||
private static final String ELLIPSIS = "…";
|
||||
/** The two glyphs, as the Strings the draw path hands to [android.graphics.Canvas]: kept here so
|
||||
drawing a key allocates nothing. */
|
||||
private static final String LIT_GLYPH = String.valueOf((char)0xE025);
|
||||
private static final String UNLIT_GLYPH = String.valueOf((char)0xE026);
|
||||
|
||||
private LightsPage() {}
|
||||
|
||||
// ------------------------------------------------------------------ the page
|
||||
|
||||
/** The live page: the template's row of slots repeated once per row of lights, then the
|
||||
template's 'Keyboard' row, untouched. [lights] may be null or empty, in which case the slots
|
||||
stand as placeholders. Never mutates the template (KeyboardData.load caches it). */
|
||||
public static KeyboardData build(KeyboardData template, LightsProxy.Light[] lights)
|
||||
{
|
||||
if (template == null || template.rows.size() < 2)
|
||||
return template;
|
||||
LightsProxy.Light[] list = lights == null ? new LightsProxy.Light[0] : lights;
|
||||
int shown = Math.min(list.length, MAX_LIGHTS);
|
||||
List<KeyboardData.Row> rows = new ArrayList<KeyboardData.Row>();
|
||||
if (shown == 0)
|
||||
rows.add(slotRow(template, list, 0, 0));
|
||||
else
|
||||
for (int first = 0; first < shown; first += SLOTS_PER_ROW)
|
||||
rows.add(slotRow(template, list, first, shown));
|
||||
rows.add(template.rows.get(template.rows.size() - 1)); // the 'Keyboard' key
|
||||
return template.with_rows(rows);
|
||||
}
|
||||
|
||||
/** One row of up to three light keys, built from the template's slot keys so the geometry lives
|
||||
in the XML. A short row is stretched to the template row's width. */
|
||||
private static KeyboardData.Row slotRow(KeyboardData template,
|
||||
LightsProxy.Light[] lights, int first, int shown)
|
||||
{
|
||||
KeyboardData.Row tpl = template.rows.get(0);
|
||||
ArrayList<KeyboardData.Key> keys = new ArrayList<KeyboardData.Key>();
|
||||
for (int i = 0; i < tpl.keys.size(); i++)
|
||||
{
|
||||
int slot = first + i + 1; // 1-based: LIGHT_<slot>
|
||||
if (shown != 0 && slot > shown)
|
||||
break;
|
||||
String label = PLACEHOLDER;
|
||||
if (shown != 0)
|
||||
{
|
||||
LightsProxy.Light light = lights[slot - 1];
|
||||
label = displayName(light.name);
|
||||
if (slot == shown && lights.length > MAX_LIGHTS)
|
||||
{
|
||||
// Say what did not fit rather than dropping it silently, and make room for it: the count
|
||||
// is the last thing on the key and must never be the part that gets cut.
|
||||
String more = " +" + (lights.length - MAX_LIGHTS);
|
||||
label = ellipsize(label, MAX_NAME_CHARS - more.length()) + more;
|
||||
}
|
||||
}
|
||||
KeyValue.Event ev = eventFor(slot);
|
||||
keys.add(tpl.keys.get(i)
|
||||
.withKeyValue(0, KeyValue.lightKey(label, ev))
|
||||
.withKeyValue(GLYPH_SUB_INDEX, KeyValue.lightGlyphKey(ev)));
|
||||
}
|
||||
KeyboardData.Row row = new KeyboardData.Row(keys, tpl.height, tpl.shift);
|
||||
return keys.size() == tpl.keys.size() ? row : row.updateWidth(tpl.keysWidth);
|
||||
}
|
||||
|
||||
/** The name as it fits on a key. The label is the registry's own name - there is deliberately no
|
||||
mapping from a light to a shorter label anywhere in the keyboard - cut with a visible ellipsis
|
||||
only when it would run over the neighbouring keys. Pure. */
|
||||
public static String displayName(String name)
|
||||
{
|
||||
return ellipsize(name, MAX_NAME_CHARS);
|
||||
}
|
||||
|
||||
/** Cut a line to [max] characters, marking the cut. Pure. */
|
||||
public static String ellipsize(String text, int max)
|
||||
{
|
||||
if (text == null)
|
||||
return "";
|
||||
String s = text.trim();
|
||||
if (s.length() <= max)
|
||||
return s;
|
||||
return s.substring(0, max - 1).trim() + ELLIPSIS;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ the keys
|
||||
|
||||
/** True for a key of the lights page. [KeyValue.getEvent()] is only valid for its own kind, so
|
||||
the kind is checked first - the same guard RokuRemote.isLit uses. */
|
||||
public static boolean isLightKey(KeyValue k)
|
||||
{
|
||||
return k != null && k.getKind() == KeyValue.Kind.Event && slotOf(k.getEvent()) > 0;
|
||||
}
|
||||
|
||||
/** True for the state-glyph value of a lights key. It is the value drawn in the special font
|
||||
(FLAG_KEY_FONT), which is exactly what tells it apart from the light's name. */
|
||||
public static boolean isStateGlyph(KeyValue k)
|
||||
{
|
||||
return isLightKey(k) && k.hasFlagsAny(KeyValue.FLAG_KEY_FONT);
|
||||
}
|
||||
|
||||
/** The glyph for a state: the lamp with its beams for a light that is on, the unlit lamp for
|
||||
everything else - 'off', 'unavailable', 'unknown' and no data at all. Only ON is ever drawn
|
||||
lit, so an unreachable light cannot look switched on. Pure, and the returned Strings are
|
||||
shared, so the draw path allocates nothing. */
|
||||
public static String glyphFor(LightsProxy.State state)
|
||||
{
|
||||
return state == LightsProxy.State.ON ? LIT_GLYPH : UNLIT_GLYPH;
|
||||
}
|
||||
|
||||
/** 1..MAX_LIGHTS for a lights key's event, 0 for anything else. */
|
||||
public static int slotOf(KeyValue.Event ev)
|
||||
{
|
||||
if (ev == null)
|
||||
return 0;
|
||||
switch (ev)
|
||||
{
|
||||
case LIGHT_1: return 1;
|
||||
case LIGHT_2: return 2;
|
||||
case LIGHT_3: return 3;
|
||||
case LIGHT_4: return 4;
|
||||
case LIGHT_5: return 5;
|
||||
case LIGHT_6: return 6;
|
||||
case LIGHT_7: return 7;
|
||||
case LIGHT_8: return 8;
|
||||
case LIGHT_9: return 9;
|
||||
case LIGHT_10: return 10;
|
||||
case LIGHT_11: return 11;
|
||||
case LIGHT_12: return 12;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** The event of slot [slot] (1-based), or null when the page has no such slot. */
|
||||
public static KeyValue.Event eventFor(int slot)
|
||||
{
|
||||
switch (slot)
|
||||
{
|
||||
case 1: return KeyValue.Event.LIGHT_1;
|
||||
case 2: return KeyValue.Event.LIGHT_2;
|
||||
case 3: return KeyValue.Event.LIGHT_3;
|
||||
case 4: return KeyValue.Event.LIGHT_4;
|
||||
case 5: return KeyValue.Event.LIGHT_5;
|
||||
case 6: return KeyValue.Event.LIGHT_6;
|
||||
case 7: return KeyValue.Event.LIGHT_7;
|
||||
case 8: return KeyValue.Event.LIGHT_8;
|
||||
case 9: return KeyValue.Event.LIGHT_9;
|
||||
case 10: return KeyValue.Event.LIGHT_10;
|
||||
case 11: return KeyValue.Event.LIGHT_11;
|
||||
case 12: return KeyValue.Event.LIGHT_12;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The colour a lights key's label and glyph are drawn in. The rule itself is in one place
|
||||
([LightsProxy.colorFor]); all this does is fetch the three theme colours and the state. */
|
||||
public static int colorFor(KeyValue k, Theme theme)
|
||||
{
|
||||
return LightsProxy.colorFor(LightsProxy.stateAt(slotOf(k.getEvent())), theme.lightOnColor,
|
||||
theme.lightOffColor, theme.lightStaleColor);
|
||||
}
|
||||
|
||||
/** What a lights key says: the answer to the last press on this light while that answer is still
|
||||
fresh (a refusal, or that the state could not be read back), and null otherwise - in which
|
||||
case the key shows the light's name in its state colour. */
|
||||
public static String noteFor(KeyValue k, long now)
|
||||
{
|
||||
if (!isLightKey(k))
|
||||
return null;
|
||||
LightsProxy.Light light = LightsProxy.lightAt(slotOf(k.getEvent()));
|
||||
return light == null ? null : light.noteOrNull(now);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,747 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** The lights page's data: the whitelisted lights, their states, and one press at a time.
|
||||
*
|
||||
* The endpoint is Harley's lights-proxy on this host's wg0 address - the same
|
||||
* 10.10.10.1 that RokuRemote uses, already allowed by res/xml/network_security_config.xml. It is
|
||||
* the only thing that talks to Home Assistant, so the whitelist, the polarity rules and the
|
||||
* honesty rules live there and this class only has to report what it was told.
|
||||
*
|
||||
* Three rules, and they are the whole design:
|
||||
* - every fetch happens on a worker thread with a short timeout, and failure is quiet: no VPN,
|
||||
* no proxy, a non-2xx or a junk payload all end as "no data", which the page draws as the
|
||||
* stale look. Never a crash, and never a guessed state.
|
||||
* - a light is 'on', 'off', 'unavailable' or 'unknown', and only the first two are states the
|
||||
* page may draw as a state. The other two - and no data at all - draw as stale, because a
|
||||
* light nobody can reach must never look like a light that is switched off.
|
||||
* - a toggle is sent once and never retried, and the answer is believed rather than a
|
||||
* prediction. The proxy reports what Home Assistant said after the light settled as 'state'
|
||||
* (null when it could not be read back) and what it expects as 'optimistic_state'; the
|
||||
* prediction is never displayed.
|
||||
*
|
||||
* A light changes rarely, so the page reads at most once per POLL_MS while it is open. That is
|
||||
* deliberately nothing like the usage gauge's 1 Hz: polling a lamp harder buys no truth.
|
||||
*/
|
||||
public final class LightsProxy
|
||||
{
|
||||
/** Harley's lights-proxy, on the WireGuard address. */
|
||||
static final String ENDPOINT = "http://10.10.10.1:8772";
|
||||
|
||||
/** A light changes rarely: how often the page re-reads while it is visible. */
|
||||
static final long POLL_MS = 15000L;
|
||||
/** Opening the page or pressing a light may ask sooner than the poll, but not twice in a moment. */
|
||||
static final long FORCE_FLOOR_MS = 2000L;
|
||||
/** A snapshot older than this is never drawn as a live state, however the fetch went. */
|
||||
static final long MAX_AGE_MS = 120000L;
|
||||
/** How long the answer to a press stays on its key: long enough to read, short enough that an
|
||||
old sentence cannot pass for the current state. */
|
||||
static final long NOTE_MS = 60000L;
|
||||
private static final int CONNECT_TIMEOUT_MS = 4000;
|
||||
private static final int READ_TIMEOUT_MS = 5000;
|
||||
/** A toggle waits for Home Assistant to settle (the proxy sleeps ~2.5 s, then reads back with
|
||||
its own 4 s timeout); timing out sooner would drop an answer that was still on its way. */
|
||||
private static final int TOGGLE_READ_TIMEOUT_MS = 12000;
|
||||
/** A burst of presses must not spawn unbounded threads. The same bound RokuRemote keeps. */
|
||||
private static final int MAX_IN_FLIGHT = 4;
|
||||
|
||||
/** What the page may show for a light. 'unavailable', 'unknown', a state we do not recognise
|
||||
and no data at all are all UNKNOWN: nobody can see this light. */
|
||||
public enum State { ON, OFF, UNKNOWN }
|
||||
|
||||
/** One light as the proxy last described it, with the answer to the last press on it. All of it
|
||||
is immutable, so the worker thread can publish a whole new array and the draw path needs no
|
||||
lock: it reads one volatile reference and some final fields. */
|
||||
public static final class Light
|
||||
{
|
||||
public final String id;
|
||||
public final String name;
|
||||
public final String entityId;
|
||||
public final State state;
|
||||
/** What the last press on this light said - a refusal code, or that the state could not be
|
||||
read back - or null. Drawn instead of the name while it is fresh. */
|
||||
public final String note;
|
||||
public final long noteAt;
|
||||
|
||||
Light(String id_, String name_, String entityId_, State state_, String note_, long noteAt_)
|
||||
{
|
||||
id = id_;
|
||||
name = name_;
|
||||
entityId = entityId_;
|
||||
state = state_;
|
||||
note = note_;
|
||||
noteAt = noteAt_;
|
||||
}
|
||||
|
||||
/** The note, or null when there is nothing to say or it has gone stale. */
|
||||
public String noteOrNull(long now)
|
||||
{
|
||||
if (note == null)
|
||||
return null;
|
||||
return (now - noteAt) < NOTE_MS ? note : null;
|
||||
}
|
||||
}
|
||||
|
||||
/** The object the proxy answers /lights with, reduced to what the page needs. */
|
||||
private static final Object _lock = new Object();
|
||||
private static volatile Light[] _lights = new Light[0];
|
||||
private static volatile long _fetchedAt = 0L;
|
||||
/** True only after a read that succeeded. A failed read says so at once instead of leaving the
|
||||
last numbers wearing a live face. */
|
||||
private static volatile boolean _fresh = false;
|
||||
/** Bumped when the *list* changes (a light added, removed or renamed), so the open page knows
|
||||
it has to be rebuilt. States changing never bump it: those are just a repaint. */
|
||||
private static volatile int _version = 0;
|
||||
private static boolean _inFlight = false;
|
||||
private static long _lastAttempt = 0L;
|
||||
/** Bit per slot with a toggle in flight: a second press before the answer would flip the light
|
||||
back, which is the same trap as a retry, so it is refused. */
|
||||
private static int _pendingSlots = 0;
|
||||
private static final AtomicInteger _inFlightCount = new AtomicInteger();
|
||||
|
||||
private LightsProxy() {}
|
||||
|
||||
// ------------------------------------------------------------------ what the page reads
|
||||
|
||||
/** The lights as last read. Never null; possibly empty, which means nobody has answered yet. */
|
||||
public static Light[] lights()
|
||||
{
|
||||
return _lights;
|
||||
}
|
||||
|
||||
/** The slot's light (LIGHT_<slot>), or null when the page has fewer lights than that. */
|
||||
public static Light lightAt(int slot)
|
||||
{
|
||||
Light[] lights = _lights;
|
||||
int i = slot - 1;
|
||||
return (i < 0 || i >= lights.length) ? null : lights[i];
|
||||
}
|
||||
|
||||
/** The state to draw for a slot: UNKNOWN whenever the page has no *current* answer, which is
|
||||
what makes 'unavailable' and a dead proxy look the same - not a state. */
|
||||
public static State stateAt(int slot)
|
||||
{
|
||||
Light light = lightAt(slot);
|
||||
if (light == null || isStaleNow())
|
||||
return State.UNKNOWN;
|
||||
return light.state;
|
||||
}
|
||||
|
||||
/** True when there is nothing to draw a state from: no read has succeeded, it failed, or what
|
||||
we have is too old to call current. */
|
||||
public static boolean isStaleNow()
|
||||
{
|
||||
return !_fresh || (System.currentTimeMillis() - _fetchedAt) >= MAX_AGE_MS;
|
||||
}
|
||||
|
||||
/** Changes whenever the light *list* does. The open page compares it to know when to rebuild. */
|
||||
public static int version()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
/** THE colour rule, and the only place it is written: ON is blue, OFF is pink, and everything
|
||||
else - unavailable, unknown, no data - is the stale grey. The three colours themselves come
|
||||
from the theme (res/values/themes.xml), so swapping on and off later is a one-line change
|
||||
there and no key holds a colour of its own. */
|
||||
public static int colorFor(State state, int onColor, int offColor, int staleColor)
|
||||
{
|
||||
if (state == null)
|
||||
return staleColor; // no light at all: the same look as one nobody can see
|
||||
switch (state)
|
||||
{
|
||||
case ON: return onColor;
|
||||
case OFF: return offColor;
|
||||
default: return staleColor;
|
||||
}
|
||||
}
|
||||
|
||||
/** 'on' and 'off' are states; everything else - including 'unavailable', 'unknown', a missing
|
||||
field and anything we do not recognise - is UNKNOWN, never OFF. Pure, so it is tested. */
|
||||
public static State stateOf(String raw)
|
||||
{
|
||||
if (raw == null)
|
||||
return State.UNKNOWN;
|
||||
String text = raw.trim().toLowerCase(Locale.US);
|
||||
if (text.equals("on"))
|
||||
return State.ON;
|
||||
if (text.equals("off"))
|
||||
return State.OFF;
|
||||
return State.UNKNOWN;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ fetching
|
||||
|
||||
/** Read the lights, at most once per POLL_MS; [force] is for opening the page and for the press
|
||||
that follows, which are allowed to ask sooner but not in a burst. Never blocks the caller and
|
||||
never throws: when a read is actually made, [onDone] runs on that worker thread afterwards. */
|
||||
public static void fetchIfNeeded(boolean force, Runnable onDone)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
synchronized (_lock)
|
||||
{
|
||||
long floor = force ? FORCE_FLOOR_MS : POLL_MS;
|
||||
if (_inFlight || (now - _lastAttempt) < floor)
|
||||
return;
|
||||
_inFlight = true;
|
||||
_lastAttempt = now;
|
||||
}
|
||||
Thread worker = new Thread(() -> {
|
||||
try
|
||||
{
|
||||
read(ENDPOINT + "/lights");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// One answer for every way a read can go wrong.
|
||||
fail();
|
||||
}
|
||||
finally
|
||||
{
|
||||
synchronized (_lock) { _inFlight = false; }
|
||||
runQuietly(onDone);
|
||||
}
|
||||
}, "lights-fetch");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private static void read(String url)
|
||||
{
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
connection = (HttpURLConnection)new URL(url).openConnection();
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
connection.setReadTimeout(READ_TIMEOUT_MS);
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
StringBuilder body = new StringBuilder();
|
||||
try (InputStream input = connection.getInputStream();
|
||||
InputStreamReader reader = new InputStreamReader(input, "UTF-8"))
|
||||
{
|
||||
char[] buf = new char[4096];
|
||||
int n;
|
||||
while ((n = reader.read(buf)) > 0)
|
||||
body.append(buf, 0, n);
|
||||
}
|
||||
Light[] parsed = parseLights(body.toString());
|
||||
if (parsed == null)
|
||||
{
|
||||
fail(); // a payload we cannot read is no data at all
|
||||
return;
|
||||
}
|
||||
publish(parsed);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// No VPN, no route, proxy down, refused, a non-2xx like the no-token 200-with-nothing and
|
||||
// an unreadable stream: one answer for all of them.
|
||||
fail();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static void fail()
|
||||
{
|
||||
_fresh = false;
|
||||
}
|
||||
|
||||
private static void publish(Light[] lights)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (!sameList(_lights, lights))
|
||||
_version++;
|
||||
/* The notes travel with the lights so a rebuild keeps them, but a real state from the box
|
||||
supersedes the answer to a press: once we know what the light is doing, the last
|
||||
sentence about it is no longer the best thing to show. */
|
||||
Light[] merged = new Light[lights.length];
|
||||
for (int i = 0; i < lights.length; i++)
|
||||
{
|
||||
merged[i] = mergeNote(lights[i], _lights);
|
||||
}
|
||||
_lights = merged;
|
||||
_fetchedAt = System.currentTimeMillis();
|
||||
_fresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** A fresh reading of the same light keeps a still-useful note; anything else replaces it. */
|
||||
static Light mergeNote(Light fresh, Light[] previous)
|
||||
{
|
||||
if (fresh.note != null || fresh.state != State.UNKNOWN)
|
||||
return fresh;
|
||||
for (int i = 0; i < previous.length; i++)
|
||||
{
|
||||
Light old = previous[i];
|
||||
if (old.id.equals(fresh.id) && old.note != null)
|
||||
return new Light(fresh.id, fresh.name, fresh.entityId, fresh.state, old.note, old.noteAt);
|
||||
}
|
||||
return fresh;
|
||||
}
|
||||
|
||||
private static boolean sameList(Light[] a, Light[] b)
|
||||
{
|
||||
if (a.length != b.length)
|
||||
return false;
|
||||
for (int i = 0; i < a.length; i++)
|
||||
if (!a[i].id.equals(b[i].id) || !a[i].name.equals(b[i].name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ pressing a light
|
||||
|
||||
/** Toggle one light (1-based slot). Sent once, never retried: a retried toggle on a light that
|
||||
did change flips it straight back. [onDone] runs on the worker thread, twice at most - once
|
||||
with the answer to this press, once when the follow-up read lands. */
|
||||
public static void toggle(int slot, Runnable onDone)
|
||||
{
|
||||
Light light = lightAt(slot);
|
||||
if (light == null || !claimSlot(slot))
|
||||
return;
|
||||
final String id = light.id;
|
||||
Thread worker = new Thread(() -> {
|
||||
ToggleResult result = null;
|
||||
try
|
||||
{
|
||||
result = sendToggle(id);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
result = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
releaseSlot(slot);
|
||||
/* What the press said, painted at once: a refusal or an unreadable read-back is the
|
||||
honest thing to show, and waiting for a poll would hide it for seconds. */
|
||||
reply(slot, result);
|
||||
runQuietly(onDone);
|
||||
/* And then a read: the answer above is about this one light and the page should not keep
|
||||
a stale view of the rest. Floored like a page open, so a burst of presses cannot become
|
||||
a burst of reads, and the proxy caches reads for 9 s anyway. */
|
||||
fetchIfNeeded(true, onDone);
|
||||
}
|
||||
}, "lights-toggle");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
/** Reserves the slot and one in-flight allowance, or refuses. Bounded like RokuRemote, and one
|
||||
press per light at a time: a second press before the answer would flip it back. */
|
||||
private static boolean claimSlot(int slot)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
int bit = 1 << (slot - 1);
|
||||
if ((_pendingSlots & bit) != 0)
|
||||
return false;
|
||||
if (_inFlightCount.incrementAndGet() > MAX_IN_FLIGHT)
|
||||
{
|
||||
_inFlightCount.decrementAndGet();
|
||||
return false;
|
||||
}
|
||||
_pendingSlots |= bit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void releaseSlot(int slot)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
_pendingSlots &= ~(1 << (slot - 1));
|
||||
}
|
||||
_inFlightCount.decrementAndGet();
|
||||
}
|
||||
|
||||
private static ToggleResult sendToggle(String id) throws Exception
|
||||
{
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
String url = ENDPOINT + "/lights/" + URLEncoder.encode(id, "UTF-8") + "/toggle";
|
||||
connection = (HttpURLConnection)new URL(url).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
connection.setReadTimeout(TOGGLE_READ_TIMEOUT_MS);
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
connection.setDoOutput(true);
|
||||
connection.setFixedLengthStreamingMode(0); // a POST with no body: the proxy ignores it
|
||||
int code = connection.getResponseCode();
|
||||
boolean ok = code >= 200 && code < 300;
|
||||
String body = readAll(ok ? connection.getInputStream() : connection.getErrorStream());
|
||||
/* Both halves matter: a 503 carries the reason (no token yet, a light Home Assistant cannot
|
||||
read) and a 200 carries the read-back. An unreadable body is still an answer. */
|
||||
ToggleResult result = parseToggle(body);
|
||||
return result != null ? result : ToggleResult.unreadable();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static String readAll(InputStream input) throws Exception
|
||||
{
|
||||
if (input == null)
|
||||
return null;
|
||||
StringBuilder body = new StringBuilder();
|
||||
try (InputStreamReader reader = new InputStreamReader(input, "UTF-8"))
|
||||
{
|
||||
char[] buf = new char[2048];
|
||||
int n;
|
||||
while ((n = reader.read(buf)) > 0)
|
||||
body.append(buf, 0, n);
|
||||
}
|
||||
return body.toString();
|
||||
}
|
||||
|
||||
/** What the press did to the light: the read-back, which is the only thing shown, and what the
|
||||
proxy said when it could not give one. */
|
||||
static void reply(int slot, ToggleResult result)
|
||||
{
|
||||
if (result == null)
|
||||
result = ToggleResult.unreadable();
|
||||
synchronized (_lock)
|
||||
{
|
||||
Light[] current = _lights;
|
||||
int i = slot - 1;
|
||||
if (i < 0 || i >= current.length)
|
||||
return;
|
||||
Light old = current[i];
|
||||
State state = old.state;
|
||||
if (result.accepted)
|
||||
state = result.state == null ? State.UNKNOWN : stateOf(result.state);
|
||||
String note = result.note();
|
||||
if (note != null)
|
||||
note = LightsPage.ellipsize(note, LightsPage.MAX_NAME_CHARS);
|
||||
Light[] next = current.clone();
|
||||
next[i] = new Light(old.id, old.name, old.entityId, state, note,
|
||||
System.currentTimeMillis());
|
||||
_lights = next; // the list itself is unchanged: no rebuild, the view just repaints
|
||||
}
|
||||
}
|
||||
|
||||
private static void runQuietly(Runnable r)
|
||||
{
|
||||
if (r == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// A repaint request must never take the keyboard down.
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ parsing
|
||||
|
||||
/** The lights of a GET /lights payload, or null when it cannot be read: a page that invents an
|
||||
empty list would be claiming there are no lights. Pure, so it is tested. */
|
||||
public static Light[] parseLights(String json)
|
||||
{
|
||||
Object root = Json.parse(json);
|
||||
if (!(root instanceof Map))
|
||||
return null;
|
||||
Object raw = ((Map<?, ?>)root).get("lights");
|
||||
if (!(raw instanceof List))
|
||||
return null;
|
||||
ArrayList<Light> out = new ArrayList<Light>();
|
||||
for (Object item : (List<?>)raw)
|
||||
{
|
||||
if (!(item instanceof Map))
|
||||
continue;
|
||||
Map<?, ?> m = (Map<?, ?>)item;
|
||||
String id = asString(m.get("id"));
|
||||
String name = asString(m.get("name"));
|
||||
if (id == null || name == null || name.trim().isEmpty())
|
||||
continue; // a light with no name has no key on the page
|
||||
String entity = asString(m.get("entity_id"));
|
||||
out.add(new Light(id, name, entity == null ? "" : entity,
|
||||
stateOf(asString(m.get("state"))), null, 0L));
|
||||
}
|
||||
return out.toArray(new Light[out.size()]);
|
||||
}
|
||||
|
||||
/** What one toggle said. [state] is Home Assistant's read-back after the light settled, and the
|
||||
only field the page may display as a state; [optimisticState] is the proxy's prediction and
|
||||
is deliberately not kept here. Pure, so it is tested. */
|
||||
public static final class ToggleResult
|
||||
{
|
||||
public final boolean accepted;
|
||||
public final String state;
|
||||
public final boolean confirmed;
|
||||
public final Boolean changed;
|
||||
public final String warning;
|
||||
public final String code;
|
||||
|
||||
ToggleResult(boolean accepted_, String state_, boolean confirmed_, Boolean changed_,
|
||||
String warning_, String code_)
|
||||
{
|
||||
accepted = accepted_;
|
||||
state = state_;
|
||||
confirmed = confirmed_;
|
||||
changed = changed_;
|
||||
warning = warning_;
|
||||
code = code_;
|
||||
}
|
||||
|
||||
static ToggleResult unreadable()
|
||||
{
|
||||
return new ToggleResult(false, null, false, null, null, "no_answer");
|
||||
}
|
||||
|
||||
/** The one short line the key shows instead of the name, or null when the answer was a state
|
||||
the page can show. It is the proxy's own vocabulary - its error code - or the fact that
|
||||
the state could not be read back; the long prose warning does not fit a key. */
|
||||
public String note()
|
||||
{
|
||||
if (!accepted)
|
||||
return code == null ? "refused" : code;
|
||||
if (state == null || !confirmed)
|
||||
return "unconfirmed";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** What a POST /lights/<id>/toggle answered, or null when the body cannot be read. Pure. */
|
||||
public static ToggleResult parseToggle(String json)
|
||||
{
|
||||
Object root = Json.parse(json);
|
||||
if (!(root instanceof Map))
|
||||
return null;
|
||||
Map<?, ?> m = (Map<?, ?>)root;
|
||||
Object accepted = m.get("accepted");
|
||||
String state = asString(m.get("state"));
|
||||
String code = error_code(m);
|
||||
if (accepted == null && state == null && code == null)
|
||||
return null; // a body that says neither yes, nor no, nor what it saw is no answer
|
||||
return new ToggleResult(asBool(accepted), state, asBool(m.get("confirmed")),
|
||||
asBoolean(m.get("changed")), asString(m.get("warning")), code);
|
||||
}
|
||||
|
||||
/** Why a toggle was refused: the service's own error code - from the nested error object its
|
||||
toggle answer documents, or the flat "code" a refusal carries - and only the prose when there
|
||||
is no code at all. A key has room for the vocabulary, not for a sentence. Null when the
|
||||
service did not refuse. */
|
||||
private static String error_code(Map<?, ?> m)
|
||||
{
|
||||
Object err = m.get("error");
|
||||
if (err instanceof Map)
|
||||
{
|
||||
String nested = asString(((Map<?, ?>)err).get("code"));
|
||||
if (nested != null)
|
||||
return nested;
|
||||
}
|
||||
String code = asString(m.get("code"));
|
||||
return code != null ? code : asString(err);
|
||||
}
|
||||
|
||||
static String asString(Object v)
|
||||
{
|
||||
return (v instanceof String) ? (String)v : null;
|
||||
}
|
||||
|
||||
static boolean asBool(Object v)
|
||||
{
|
||||
return (v instanceof Boolean) && ((Boolean)v).booleanValue();
|
||||
}
|
||||
|
||||
static Boolean asBoolean(Object v)
|
||||
{
|
||||
return (v instanceof Boolean) ? (Boolean)v : null;
|
||||
}
|
||||
|
||||
/** A deliberately small JSON reader: objects, arrays, strings, numbers, booleans and null.
|
||||
*
|
||||
* Android's org.json is a stub under the local JVM test runner, which is why HostGraph and this
|
||||
* class parse their own payloads: pure Java, so the tests can drive the real parser over the
|
||||
* real payloads. Only what the lights endpoints answer with is supported, and anything it
|
||||
* cannot read becomes "no data" rather than a guess. */
|
||||
static final class Json
|
||||
{
|
||||
private final String _s;
|
||||
private int _i = 0;
|
||||
|
||||
private Json(String s) { _s = s; }
|
||||
|
||||
/** Map/List/String/Double/Boolean/null, or null when the text cannot be read. */
|
||||
static Object parse(String text)
|
||||
{
|
||||
if (text == null)
|
||||
return null;
|
||||
Json p = new Json(text);
|
||||
try
|
||||
{
|
||||
Object v = p.value();
|
||||
p.ws();
|
||||
return p._i == p._s.length() ? v : null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ws()
|
||||
{
|
||||
while (_i < _s.length() && Character.isWhitespace(_s.charAt(_i)))
|
||||
_i++;
|
||||
}
|
||||
|
||||
private Object value() throws Exception
|
||||
{
|
||||
ws();
|
||||
if (_i >= _s.length())
|
||||
throw new Exception("eof");
|
||||
char c = _s.charAt(_i);
|
||||
switch (c)
|
||||
{
|
||||
case '{': return object();
|
||||
case '[': return array();
|
||||
case '"': return string();
|
||||
case 't': expect("true"); return Boolean.TRUE;
|
||||
case 'f': expect("false"); return Boolean.FALSE;
|
||||
case 'n': expect("null"); return null;
|
||||
default: return number();
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> object() throws Exception
|
||||
{
|
||||
Map<String, Object> out = new java.util.LinkedHashMap<String, Object>();
|
||||
_i++; // '{'
|
||||
ws();
|
||||
if (peek() == '}')
|
||||
{
|
||||
_i++;
|
||||
return out;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
ws();
|
||||
if (peek() != '"')
|
||||
throw new Exception("object key");
|
||||
String key = string();
|
||||
ws();
|
||||
if (peek() != ':')
|
||||
throw new Exception("object colon");
|
||||
_i++;
|
||||
out.put(key, value());
|
||||
ws();
|
||||
char c = peek();
|
||||
_i++;
|
||||
if (c == '}')
|
||||
return out;
|
||||
if (c != ',')
|
||||
throw new Exception("object comma");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> array() throws Exception
|
||||
{
|
||||
List<Object> out = new ArrayList<Object>();
|
||||
_i++; // '['
|
||||
ws();
|
||||
if (peek() == ']')
|
||||
{
|
||||
_i++;
|
||||
return out;
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
out.add(value());
|
||||
ws();
|
||||
char c = peek();
|
||||
_i++;
|
||||
if (c == ']')
|
||||
return out;
|
||||
if (c != ',')
|
||||
throw new Exception("array comma");
|
||||
}
|
||||
}
|
||||
|
||||
private String string() throws Exception
|
||||
{
|
||||
StringBuilder b = new StringBuilder();
|
||||
_i++; // '"'
|
||||
while (true)
|
||||
{
|
||||
if (_i >= _s.length())
|
||||
throw new Exception("string eof");
|
||||
char c = _s.charAt(_i++);
|
||||
if (c == '"')
|
||||
return b.toString();
|
||||
if (c != '\\')
|
||||
{
|
||||
b.append(c);
|
||||
continue;
|
||||
}
|
||||
if (_i >= _s.length())
|
||||
throw new Exception("escape eof");
|
||||
char e = _s.charAt(_i++);
|
||||
switch (e)
|
||||
{
|
||||
case '"': b.append('"'); break;
|
||||
case '\\': b.append('\\'); break;
|
||||
case '/': b.append('/'); break;
|
||||
case 'b': b.append('\b'); break;
|
||||
case 'f': b.append('\f'); break;
|
||||
case 'n': b.append('\n'); break;
|
||||
case 'r': b.append('\r'); break;
|
||||
case 't': b.append('\t'); break;
|
||||
case 'u':
|
||||
if (_i + 4 > _s.length())
|
||||
throw new Exception("unicode escape eof");
|
||||
b.append((char)Integer.parseInt(_s.substring(_i, _i + 4), 16));
|
||||
_i += 4;
|
||||
break;
|
||||
default: throw new Exception("escape");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Double number() throws Exception
|
||||
{
|
||||
int start = _i;
|
||||
while (_i < _s.length() && "+-.eE0123456789".indexOf(_s.charAt(_i)) >= 0)
|
||||
_i++;
|
||||
if (_i == start)
|
||||
throw new Exception("number");
|
||||
return Double.valueOf(_s.substring(start, _i));
|
||||
}
|
||||
|
||||
private char peek() throws Exception
|
||||
{
|
||||
if (_i >= _s.length())
|
||||
throw new Exception("eof");
|
||||
return _s.charAt(_i);
|
||||
}
|
||||
|
||||
private void expect(String word) throws Exception
|
||||
{
|
||||
if (!_s.startsWith(word, _i))
|
||||
throw new Exception("expected " + word);
|
||||
_i += word.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** Sends Roku remote commands from the keyboard.
|
||||
*
|
||||
* Three rules, and they are the whole design:
|
||||
* - the request happens on a worker thread. [Keyboard2.Receiver.handle_event_key] runs
|
||||
* on the main thread, so it must return immediately: doing the network work inline
|
||||
* would freeze the keyboard for as long as the TV takes to answer.
|
||||
* - failure is quiet, exactly like [HostGraph]. No TV, no VPN, a refused connection, a
|
||||
* non-2xx from the proxy or a timeout all end as "nothing happened" - never a crash
|
||||
* and never a wedged key.
|
||||
* - the pressed key stays lit for a moment, from two plain fields that only the draw
|
||||
* path reads. A command against a sleeping Roku legitimately takes ~5 s to be
|
||||
* answered (202 Accepted), and a key that looks dead gets pressed twice.
|
||||
*
|
||||
* The endpoint is Harley's proxy on this host's wg0 address: it passes
|
||||
* /roku/keypress/<Key> straight through to the TV's ECP listener on port 8060.
|
||||
* Cleartext for 10.10.10.1 is already allowed by res/xml/network_security_config.xml.
|
||||
*/
|
||||
public final class RokuRemote
|
||||
{
|
||||
private static final String ENDPOINT = "http://10.10.10.1:7777/roku/keypress/";
|
||||
/** The proxy gives the TV 15 s before it gives up; timing out sooner than the
|
||||
proxy would drop an answer that was still on its way. */
|
||||
private static final int CONNECT_TIMEOUT_MS = 15000;
|
||||
private static final int READ_TIMEOUT_MS = 15000;
|
||||
/** How long the key is lit from the press until an answer arrives. */
|
||||
private static final long PRESSED_MS = 700L;
|
||||
/** ... and how long an accepted command keeps it lit. */
|
||||
private static final long SENT_MS = 1300L;
|
||||
/** A burst of presses must not spawn unbounded threads; past this a press is dropped. */
|
||||
private static final int MAX_IN_FLIGHT = 8;
|
||||
|
||||
/** Which key is lit, and when its light goes out. Plain fields, written by the
|
||||
worker thread and read by the draw path, which allocates nothing for them. */
|
||||
private static volatile KeyValue.Event _litEvent = null;
|
||||
private static volatile long _litUntil = 0L;
|
||||
private static final AtomicInteger _inFlight = new AtomicInteger();
|
||||
|
||||
private RokuRemote() {}
|
||||
|
||||
/** True while [ev] should be drawn as just-pressed. Safe on the draw path. */
|
||||
public static boolean isLit(KeyValue.Event ev, long now)
|
||||
{
|
||||
return ev != null && ev == _litEvent && now < _litUntil;
|
||||
}
|
||||
|
||||
/** The same test for a whole key. [KeyValue.getEvent()] is only valid for its own
|
||||
kind, so this is the single place that reads a key's value and it checks the kind
|
||||
first. Allocates nothing: one enum reference and one long. */
|
||||
public static boolean isLit(KeyValue kv, long now)
|
||||
{
|
||||
return kv != null && kv.getKind() == KeyValue.Kind.Event && isLit(kv.getEvent(), now);
|
||||
}
|
||||
|
||||
/** Send one command. Never blocks the caller and never throws. */
|
||||
public static void press(KeyValue.Event ev)
|
||||
{
|
||||
final String key = ecpKey(ev);
|
||||
if (key == null)
|
||||
return;
|
||||
if (_inFlight.incrementAndGet() > MAX_IN_FLIGHT)
|
||||
{
|
||||
_inFlight.decrementAndGet();
|
||||
return; // fail-soft: a dropped press beats unbounded threads
|
||||
}
|
||||
_litEvent = ev;
|
||||
_litUntil = System.currentTimeMillis() + PRESSED_MS;
|
||||
Thread worker = new Thread(() -> send(ev, key), "roku-remote");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private static void send(KeyValue.Event ev, String key)
|
||||
{
|
||||
boolean accepted = false;
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
connection = (HttpURLConnection)new URL(ENDPOINT + key).openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
|
||||
connection.setReadTimeout(READ_TIMEOUT_MS);
|
||||
int code = connection.getResponseCode();
|
||||
accepted = code >= 200 && code < 300; // 202 once the TV has taken the keypress
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// No VPN, no route, TV off, proxy down, unreadable: one answer for all of them.
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
_inFlight.decrementAndGet();
|
||||
// Only light up if this key is still the one on screen: the user may have moved on.
|
||||
if (accepted && ev == _litEvent)
|
||||
_litUntil = System.currentTimeMillis() + SENT_MS;
|
||||
}
|
||||
}
|
||||
|
||||
/** The ECP key names, i.e. what goes after /keypress/. */
|
||||
private static String ecpKey(KeyValue.Event ev)
|
||||
{
|
||||
switch (ev)
|
||||
{
|
||||
case ROKU_HOME: return "Home";
|
||||
case ROKU_BACK: return "Back";
|
||||
case ROKU_UP: return "Up";
|
||||
case ROKU_DOWN: return "Down";
|
||||
case ROKU_LEFT: return "Left";
|
||||
case ROKU_RIGHT: return "Right";
|
||||
case ROKU_SELECT: return "Select";
|
||||
case ROKU_PLAY: return "Play";
|
||||
case ROKU_REWIND: return "Rev"; // the ECP's name for rewind
|
||||
case ROKU_FORWARD: return "Fwd"; // ... and for fast forward
|
||||
case ROKU_POWER: return "Power"; // the ECP's power toggle
|
||||
/* Volume: the same names Harley's own web remote posts to /keypress/. They only
|
||||
do something on a Roku TV (the TV's volume keys), which is what this is. */
|
||||
case ROKU_VOLUP: return "VolumeUp";
|
||||
case ROKU_VOLDOWN: return "VolumeDown";
|
||||
case ROKU_VOLMUTE: return "VolumeMute";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,15 @@ public class Theme
|
||||
public final int subLabelColor;
|
||||
public final int secondaryLabelColor;
|
||||
public final int greyedLabelColor;
|
||||
/** Label colour for the Roku page's direction keys (res/values/themes.xml). */
|
||||
public final int rokuArrowColor;
|
||||
/** The lights page's three colours (res/values/themes.xml): blue for a light that is on, pink
|
||||
for one that is off, and the grey that stands for a light nobody can see. The rule that maps
|
||||
a state to one of them is a single function, [LightsProxy.colorFor]; these are the values it
|
||||
is handed. */
|
||||
public final int lightOnColor;
|
||||
public final int lightOffColor;
|
||||
public final int lightStaleColor;
|
||||
|
||||
// Key borders
|
||||
public final float keyBorderRadius;
|
||||
@@ -58,6 +67,10 @@ public class Theme
|
||||
s.getFloat(R.styleable.keyboard_secondaryDimming, 0.25f));
|
||||
greyedLabelColor = adjustLight(labelColor,
|
||||
s.getFloat(R.styleable.keyboard_greyedDimming, 0.5f));
|
||||
rokuArrowColor = s.getColor(R.styleable.keyboard_colorRokuArrow, labelColor);
|
||||
lightOnColor = s.getColor(R.styleable.keyboard_colorLightOn, rokuArrowColor);
|
||||
lightOffColor = s.getColor(R.styleable.keyboard_colorLightOff, 0xFFFF2D95);
|
||||
lightStaleColor = s.getColor(R.styleable.keyboard_colorLightStale, greyedLabelColor);
|
||||
keyBorderRadius = s.getDimension(R.styleable.keyboard_keyBorderRadius, 0);
|
||||
keyBorderWidth = s.getDimension(R.styleable.keyboard_keyBorderWidth, 0);
|
||||
keyBorderWidthActivated = s.getDimension(R.styleable.keyboard_keyBorderWidthActivated, 0);
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import android.graphics.Color;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/** Data and rendering logic for the display-only usage gauge. */
|
||||
public final class UsageGauge
|
||||
{
|
||||
// The layout stores this marker in 'indication', so it identifies the key
|
||||
// without adding a character to the generated compose table.
|
||||
public static final String KEY_MARKER = "usage_gauge";
|
||||
/** Long-pressing the gauge key opens this in the browser. */
|
||||
public static final String USAGE_PAGE_URL = "https://platform.deepseek.com/usage";
|
||||
public static final long REFRESH_AFTER_MS = 5 * 60 * 1000L;
|
||||
// The sampler publishes every 15 minutes, so one missed tick must not read as stale: three
|
||||
// do. A threshold equal to the interval makes the gauge blink to '--' on every cycle.
|
||||
public static final long STALE_AFTER_MS = 45 * 60 * 1000L;
|
||||
|
||||
public static final class Reading
|
||||
{
|
||||
public final double h5Pct;
|
||||
public final double weeklyPct;
|
||||
public final double spentTodayUsd;
|
||||
public final double balance;
|
||||
/** True when the server's spend window opened mid-day: the total is a floor, not the day. */
|
||||
public final boolean partial;
|
||||
/** True when the server says the reading it sent is too old to present as current. */
|
||||
public final boolean serverStale;
|
||||
|
||||
Reading(double h5, double weekly, double spent, double balance_)
|
||||
{
|
||||
this(h5, weekly, spent, balance_, false, false);
|
||||
}
|
||||
|
||||
Reading(double h5, double weekly, double spent, double balance_, boolean partial_,
|
||||
boolean serverStale_)
|
||||
{
|
||||
h5Pct = h5;
|
||||
weeklyPct = weekly;
|
||||
spentTodayUsd = spent;
|
||||
balance = balance_;
|
||||
partial = partial_;
|
||||
serverStale = serverStale_;
|
||||
}
|
||||
}
|
||||
|
||||
private static volatile Reading _cached;
|
||||
private static volatile long _cachedAt;
|
||||
private static volatile boolean _failed;
|
||||
private static boolean _inFlight;
|
||||
|
||||
private UsageGauge() {}
|
||||
|
||||
public static boolean isGaugeKey(KeyboardData.Key key)
|
||||
{
|
||||
return key != null && KEY_MARKER.equals(key.indication);
|
||||
}
|
||||
|
||||
/** Returns null when any value needed for a trustworthy display is absent or invalid. */
|
||||
public static Reading parse(String json)
|
||||
{
|
||||
try
|
||||
{
|
||||
JSONObject root = new JSONObject(json);
|
||||
JSONObject codex = root.getJSONObject("codex");
|
||||
JSONObject deepseek = root.getJSONObject("deepseek");
|
||||
double h5 = requiredNumber(codex.getJSONObject("h5"), "pct");
|
||||
double weekly = requiredNumber(codex.getJSONObject("weekly"), "pct");
|
||||
double spent = requiredNumber(deepseek, "spent_today_usd");
|
||||
double balance = requiredNumber(deepseek, "balance");
|
||||
if (!finite(h5) || !finite(weekly) || !finite(spent) || !finite(balance)
|
||||
|| h5 < 0 || weekly < 0 || spent < 0)
|
||||
return null;
|
||||
boolean partial = deepseek.optBoolean("partial", false);
|
||||
boolean serverStale = codex.optBoolean("stale", false)
|
||||
|| deepseek.optBoolean("stale", false);
|
||||
return new Reading(h5, weekly, spent, balance, partial, serverStale);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
// The local JVM test runner replaces Android's org.json methods with
|
||||
// throwing stubs. Keep the same defensive behavior testable there.
|
||||
return parseJsonFallback(json);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Reading parseJsonFallback(String json)
|
||||
{
|
||||
try
|
||||
{
|
||||
String codex = objectForKey(json, "codex");
|
||||
String deepseek = objectForKey(json, "deepseek");
|
||||
double h5 = numberForKey(objectForKey(codex, "h5"), "pct");
|
||||
double weekly = numberForKey(objectForKey(codex, "weekly"), "pct");
|
||||
double spent = numberForKey(deepseek, "spent_today_usd");
|
||||
double balance = numberForKey(deepseek, "balance");
|
||||
if (!finite(h5) || !finite(weekly) || !finite(spent) || !finite(balance)
|
||||
|| h5 < 0 || weekly < 0 || spent < 0)
|
||||
return null;
|
||||
boolean partial = booleanForKey(deepseek, "partial");
|
||||
boolean serverStale = booleanForKey(codex, "stale") || booleanForKey(deepseek, "stale");
|
||||
return new Reading(h5, weekly, spent, balance, partial, serverStale);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String objectForKey(String json, String key) throws Exception
|
||||
{
|
||||
Matcher matcher = Pattern.compile("\\\"" + Pattern.quote(key)
|
||||
+ "\\\"\\s*:\\s*\\{").matcher(json);
|
||||
if (!matcher.find())
|
||||
throw new Exception("Missing object " + key);
|
||||
int start = matcher.end() - 1;
|
||||
int depth = 0;
|
||||
boolean quoted = false;
|
||||
boolean escaped = false;
|
||||
for (int i = start; i < json.length(); i++)
|
||||
{
|
||||
char c = json.charAt(i);
|
||||
if (quoted)
|
||||
{
|
||||
if (escaped) escaped = false;
|
||||
else if (c == '\\') escaped = true;
|
||||
else if (c == '"') quoted = false;
|
||||
}
|
||||
else if (c == '"') quoted = true;
|
||||
else if (c == '{') depth++;
|
||||
else if (c == '}' && --depth == 0)
|
||||
return json.substring(start, i + 1);
|
||||
}
|
||||
throw new Exception("Unclosed object " + key);
|
||||
}
|
||||
|
||||
private static double numberForKey(String json, String key) throws Exception
|
||||
{
|
||||
Matcher matcher = Pattern.compile("\\\"" + Pattern.quote(key)
|
||||
+ "\\\"\\s*:\\s*(-?(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?)")
|
||||
.matcher(json);
|
||||
if (!matcher.find())
|
||||
throw new Exception("Missing numeric field " + key);
|
||||
return Double.parseDouble(matcher.group(1));
|
||||
}
|
||||
|
||||
/** Only a literal true counts: an absent or malformed flag must never claim confidence. */
|
||||
private static boolean booleanForKey(String json, String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
int at = json.indexOf("\"" + key + "\"");
|
||||
if (at < 0)
|
||||
return false;
|
||||
int colon = json.indexOf(':', at);
|
||||
if (colon < 0)
|
||||
return false;
|
||||
int i = colon + 1;
|
||||
while (i < json.length() && Character.isWhitespace(json.charAt(i)))
|
||||
i++;
|
||||
return json.startsWith("true", i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static double requiredNumber(JSONObject object, String key) throws Exception
|
||||
{
|
||||
Object value = object.get(key);
|
||||
if (!(value instanceof Number))
|
||||
throw new Exception("Missing numeric field " + key);
|
||||
return ((Number)value).doubleValue();
|
||||
}
|
||||
|
||||
private static boolean finite(double value)
|
||||
{
|
||||
return !Double.isNaN(value) && !Double.isInfinite(value);
|
||||
}
|
||||
|
||||
public static float fillFraction(Reading reading)
|
||||
{
|
||||
if (reading == null)
|
||||
return 0;
|
||||
return (float)(Math.max(0, Math.min(100,
|
||||
Math.max(reading.h5Pct, reading.weeklyPct))) / 100.0);
|
||||
}
|
||||
|
||||
public static boolean isStale(long fetchedAt, long now, boolean failed,
|
||||
String token)
|
||||
{
|
||||
return token == null || token.trim().isEmpty() || failed || fetchedAt <= 0
|
||||
|| now < fetchedAt || now - fetchedAt > STALE_AFTER_MS;
|
||||
}
|
||||
|
||||
public static boolean isLowBalance(Reading reading)
|
||||
{
|
||||
// $1.00 or less is low: the balance sitting exactly on the threshold is
|
||||
// already worth flagging, and the API reports it to two decimals so a
|
||||
// strict < would make "1.00" indistinguishable from "just topped up"
|
||||
// for as long as the balance happens to rest on the dollar.
|
||||
return reading == null || reading.balance <= 1.00;
|
||||
}
|
||||
|
||||
public static synchronized void fetchIfNeeded(String url, String token,
|
||||
Runnable onComplete)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
if (token == null || token.trim().isEmpty() || _inFlight
|
||||
|| (_cached != null && now - _cachedAt <= REFRESH_AFTER_MS))
|
||||
return;
|
||||
_inFlight = true;
|
||||
Thread worker = new Thread(() -> {
|
||||
Reading reading = fetch(url, token);
|
||||
synchronized (UsageGauge.class)
|
||||
{
|
||||
// A reading the server itself flags as too old is as unusable as a failed fetch:
|
||||
// presenting it as current is the exact lie the gauge is built to avoid.
|
||||
if (reading == null || reading.serverStale)
|
||||
_failed = true;
|
||||
else
|
||||
{
|
||||
_cached = reading;
|
||||
_cachedAt = System.currentTimeMillis();
|
||||
_failed = false;
|
||||
}
|
||||
_inFlight = false;
|
||||
}
|
||||
if (onComplete != null)
|
||||
onComplete.run();
|
||||
}, "UsageGauge");
|
||||
worker.setDaemon(true);
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private static Reading fetch(String url, String token)
|
||||
{
|
||||
HttpURLConnection connection = null;
|
||||
try
|
||||
{
|
||||
connection = (HttpURLConnection)new URL(url).openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setConnectTimeout(3000);
|
||||
connection.setReadTimeout(5000);
|
||||
connection.setRequestProperty("Authorization", "Bearer " + token);
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
|
||||
return null;
|
||||
try (InputStream input = connection.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input,
|
||||
StandardCharsets.UTF_8)))
|
||||
{
|
||||
StringBuilder body = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
body.append(line);
|
||||
return parse(body.toString());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection != null)
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static Reading cachedReading()
|
||||
{
|
||||
return _cached;
|
||||
}
|
||||
|
||||
public static boolean isStaleNow(String token)
|
||||
{
|
||||
return isStale(_cachedAt, System.currentTimeMillis(), _failed, token);
|
||||
}
|
||||
|
||||
public static long cachedAt()
|
||||
{
|
||||
return _cachedAt;
|
||||
}
|
||||
|
||||
public static int hueColor(float fraction)
|
||||
{
|
||||
float level = Math.max(0, Math.min(1, fraction));
|
||||
if (level < 0.5f)
|
||||
// Low usage borrows the space bar button's own colour rather than a hand-matched shade, so
|
||||
// the gauge and the button can never drift apart. Amber and red still mark high usage: the
|
||||
// height carries the level, the colour carries the alarm.
|
||||
return blend(DeepSeekPeak.colorFor(System.currentTimeMillis()), Color.rgb(245, 178, 45), level * 2);
|
||||
return blend(Color.rgb(245, 178, 45), Color.rgb(235, 55, 55), (level - 0.5f) * 2);
|
||||
}
|
||||
|
||||
private static int blend(int a, int b, float t)
|
||||
{
|
||||
return Color.rgb((int)(Color.red(a) + (Color.red(b) - Color.red(a)) * t),
|
||||
(int)(Color.green(a) + (Color.green(b) - Color.green(a)) * t),
|
||||
(int)(Color.blue(a) + (Color.blue(b) - Color.blue(a)) * t));
|
||||
}
|
||||
}
|
||||
@@ -101,10 +101,10 @@ public class CandidatesView extends LinearLayout
|
||||
float row_height = config.keyboard_rows_height_pixels * (1 - config.key_vertical_margin);
|
||||
ViewGroup.MarginLayoutParams p =
|
||||
(ViewGroup.MarginLayoutParams)getLayoutParams();
|
||||
p.height = (int)(row_height * 0.6f);
|
||||
p.height = (int)(row_height * 0.637f);
|
||||
setLayoutParams(p);
|
||||
// Match the size of labels on the keyboard.
|
||||
float text_size = row_height * config.characterSize * config.labelTextSize;
|
||||
float text_size = row_height * config.characterSize * config.labelTextSize * 0.85f;
|
||||
for (int i = 0; i < NUM_CANDIDATES; i++)
|
||||
{
|
||||
TextView v = _item_views[i];
|
||||
|
||||
@@ -39,38 +39,40 @@ Useful links.
|
||||
-->
|
||||
<keyboard name="QWERTY (US)" script="latin">
|
||||
<row>
|
||||
<key c="q" nw="1" sw="!" se="loc esc"/>
|
||||
<key c="w" nw="2" ne="~" sw="\@"/>
|
||||
<key c="q" nw="1" sw="!"/>
|
||||
<key c="w" nw="2" sw="\@"/>
|
||||
<key c="e" nw="3" sw="\#" se="loc €"/>
|
||||
<key c="r" nw="4" sw="$"/>
|
||||
<key c="t" nw="5" sw="%"/>
|
||||
<key c="y" nw="6" sw="^"/>
|
||||
<key c="u" nw="7" sw="&"/>
|
||||
<key c="i" nw="8" sw="*"/>
|
||||
<key c="o" nw="9" sw="-" se="_"/>
|
||||
<key c="p" nw="0"/>
|
||||
<key c="o" nw="9" sw="(" se=")"/>
|
||||
<key c="p" nw="0" sw="-" se="_"/>
|
||||
</row>
|
||||
<row>
|
||||
<key shift="0.5" c="a" nw="loc tab" ne="`"/>
|
||||
<key c="s"/>
|
||||
<key shift="0.5" c="a" nw="loc tab"/>
|
||||
<key c="s" sw="~" se="`"/>
|
||||
<key c="d"/>
|
||||
<key c="f"/>
|
||||
<key c="g" ne="-" sw="_"/>
|
||||
<key c="h" ne="=" sw="+"/>
|
||||
<key c="j" se="}" sw="{"/>
|
||||
<key c="k" sw="[" se="]"/>
|
||||
<key c="l" ne="|" sw=":"/>
|
||||
<key c="f" sw="=" se="+"/>
|
||||
<key c="g"/>
|
||||
<key c="h" sw="{" se="}"/>
|
||||
<key c="j" sw="[" se="]"/>
|
||||
<key c="k" sw=""" se="'"/>
|
||||
<key c="l" sw=";" se=":"/>
|
||||
<!-- The indication is the stable UsageGauge.KEY_MARKER; it adds no key character. -->
|
||||
<key indication="usage_gauge"/>
|
||||
</row>
|
||||
<row>
|
||||
<key role="action" c="shift" ne="loc capslock"/>
|
||||
<key role="action" c="shift" nw="loc esc" ne="loc capslock"/>
|
||||
<key c="z"/>
|
||||
<key c="x" ne="loc †"/>
|
||||
<key c="c" ne="<" sw="."/>
|
||||
<key c="v" ne=">"/>
|
||||
<key c="b" ne="\?" sw="/"/>
|
||||
<key c="n" ne=":" sw=";"/>
|
||||
<key c="m" ne=""" sw="'"/>
|
||||
<key c="." key3=","/>
|
||||
<key c="c"/>
|
||||
<key c="v"/>
|
||||
<key c="b" sw="<" se=">"/>
|
||||
<key c="n" se="|"/>
|
||||
<key c="m" sw="\\" se="/"/>
|
||||
<key c="." key3="," se="?"/>
|
||||
<key role="action" c="backspace" ne="delete"/>
|
||||
</row>
|
||||
</keyboard>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M256 352C293.2 352 319.2 334.5 334.4 318.1C343.3 308.4 358.5 307.7 368.3 316.7C378 325.7 378.6 340.9 369.6 350.6C347.7 374.5 309.7 400 256 400C202.3 400 164.3 374.5 142.4 350.6C133.4 340.9 133.1 325.7 143.7 316.7C153.5 307.7 168.7 308.4 177.6 318.1C192.8 334.5 218.8 352 256 352zM208.4 208C208.4 225.7 194 240 176.4 240C158.7 240 144.4 225.7 144.4 208C144.4 190.3 158.7 176 176.4 176C194 176 208.4 190.3 208.4 208zM304.4 208C304.4 190.3 318.7 176 336.4 176C354 176 368.4 190.3 368.4 208C368.4 225.7 354 240 336.4 240C318.7 240 304.4 225.7 304.4 208zM512 256C512 397.4 397.4 512 256 512C114.6 512 0 397.4 0 256C0 114.6 114.6 0 256 0C397.4 0 512 114.6 512 256zM256 48C141.1 48 48 141.1 48 256C48 370.9 141.1 464 256 464C370.9 464 464 370.9 464 256C464 141.1 370.9 48 256 48z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Material Design Icons "remote-tv" - https://materialdesignicons.com/icon/remote-tv - Apache License 2.0, Copyright Pictogrammers; geometry scaled to fill the 512x512 box --><path d="M179.2,0C150.784,0 128,22.784 128,51.2V460.8C128,489.216 150.784,512 179.2,512H332.8C361.216,512 384,489.216 384,460.8V51.2C384,22.784 361.216,0 332.8,0H281.6V51.2H230.4V0H179.2M230.4,102.4H281.6V153.6H332.8V204.8H281.6V256H230.4V204.8H179.2V153.6H230.4V102.4M179.2,307.2H230.4V358.4H179.2V307.2M281.6,307.2H332.8V358.4H281.6V307.2M179.2,409.6H230.4V460.8H179.2V409.6M281.6,409.6H332.8V460.8H281.6V409.6Z"/></svg>
|
||||
|
After Width: | Height: | Size: 662 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Hand-drawn ceiling light with three beams, for the lights page key (0xE025). Same style as the other committed glyphs: one path, several solidsubpaths, solid fill, and ink that fills the 512x512 document box so the importer maps it to the whole em square (a glyph whose ink sits in a small part of its viewBox renders small). Mount plate, tapered shade, then three beams fanning down: left, straight, right. --><path d="M128,4 L384,4 L384,44 L128,44 Z M16,80 L496,80 L400,272 L112,272 Z M168,272 L224,272 L152,500 L80,500 Z M236,272 L276,272 L276,500 L236,500 Z M288,272 L344,272 L432,500 L360,500 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 676 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- The unlit twin of the lamp at 0xE025 (srcs/special_font/025.svg): the same mount plate and the same tapered shade, drawn from exactly the same subpaths, with no beams below - the lit/unlit pair differs only in the light coming out. Single path, solid fill, in the style of the other committed glyphs. --><path d="M128,4 L384,4 L384,44 L128,44 Z M16,80 L496,80 L400,272 L112,272 Z"/></svg>
|
||||
|
After Width: | Height: | Size: 456 B |
@@ -0,0 +1,103 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/** The ribbon must fail to nothing, never to a lie or a crash. */
|
||||
public class HostGraphTest
|
||||
{
|
||||
private static final String GOOD = "{\"ts\": \"2026-09-27T18:30:32+00:00\", \"host\": \"utumno\", "
|
||||
+ "\"version\": \"0.1.0\", \"window_s\": 6, \"series\": ["
|
||||
+ "{\"name\": \"io\", \"unit\": \"%\", \"values\": [0.0, 0.0, 13.6, 2.9, 6.0]}, "
|
||||
+ "{\"name\": \"cpu\", \"unit\": \"%\", \"values\": [0.0, 0.0, 0.1, 0.9, 0.2]}, "
|
||||
+ "{\"name\": \"util\", \"unit\": \"%\", \"values\": [0.0, 50.0, 4.7, 4.7, 4.3]}]}";
|
||||
|
||||
@Test
|
||||
public void realPayloadParses()
|
||||
{
|
||||
float[][] parsed = HostGraph.parse(GOOD);
|
||||
assertNotNull(parsed);
|
||||
assertEquals(3, parsed.length);
|
||||
assertEquals("util", HostGraph.SERIES[2]);
|
||||
assertEquals(5, parsed[0].length);
|
||||
assertEquals(13.6f, parsed[0][2], 0.001f);
|
||||
assertEquals(0.2f, parsed[1][4], 0.001f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void garbageIsNoData()
|
||||
{
|
||||
assertNull(HostGraph.parse("not json at all"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nullIsNoData()
|
||||
{
|
||||
assertNull(HostGraph.parse(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aMissingSeriesIsNoData()
|
||||
{
|
||||
assertNull(HostGraph.parse("{\"series\": [{\"name\": \"io\", \"values\": [1.0]}]}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyValuesAreNoData()
|
||||
{
|
||||
assertNull(HostGraph.parse("{\"series\": [{\"name\": \"io\", \"values\": []}, "
|
||||
+ "{\"name\": \"cpu\", \"values\": [1.0]}, "
|
||||
+ "{\"name\": \"util\", \"values\": [1.0]}]}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aJunkNumberIsNoData()
|
||||
{
|
||||
assertNull(HostGraph.parse("{\"series\": [{\"name\": \"io\", \"values\": [1.0, abc]}, "
|
||||
+ "{\"name\": \"cpu\", \"values\": [1.0]}, "
|
||||
+ "{\"name\": \"util\", \"values\": [1.0]}]}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aShortWindowStillParses()
|
||||
{
|
||||
float[][] parsed = HostGraph.parse("{\"series\": [{\"name\": \"io\", \"values\": [3.5]}, "
|
||||
+ "{\"name\": \"cpu\", \"values\": [0.5]}, "
|
||||
+ "{\"name\": \"util\", \"values\": [12.5]}]}");
|
||||
assertNotNull(parsed);
|
||||
assertEquals(1, parsed[0].length);
|
||||
assertEquals(3.5f, parsed[0][0], 0.001f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theGraphRidesTheSamePreferenceAsUsage()
|
||||
{
|
||||
assertEquals("http://10.10.10.1:8771", HostGraph.baseOf("http://10.10.10.1:8771/usage"));
|
||||
assertEquals("http://10.10.10.1:8771", HostGraph.baseOf("http://10.10.10.1:8771"));
|
||||
assertEquals("https://example.test:9000", HostGraph.baseOf("https://example.test:9000/usage"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void baseOfSurvivesNonsense()
|
||||
{
|
||||
assertEquals("", HostGraph.baseOf(null));
|
||||
assertEquals("garbage", HostGraph.baseOf("garbage"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aServerTooOldToSendUtilisationIsNoData()
|
||||
{
|
||||
// Fails safe rather than loud: a payload missing a series we draw is not trusted.
|
||||
assertNull(HostGraph.parse("{\"series\": [{\"name\": \"io\", \"values\": [1.0]}, "
|
||||
+ "{\"name\": \"cpu\", \"values\": [1.0]}]}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nothingIsDrawnBeforeAnyDataArrives()
|
||||
{
|
||||
assertEquals(0, HostGraph.series(0).length);
|
||||
assertEquals(false, HostGraph.hasData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/** The lights page: the state parsing the page is driven by ([LightsProxy]), and the page itself
|
||||
([LightsPage]). Android classes cannot be constructed in a plain unit test, so the drawing is
|
||||
covered where it is pure - the colour mapping, the glyph choice, the label - and the theme
|
||||
wiring is checked by reading res/values/themes.xml and the built APK instead. */
|
||||
public class LightsProxyTest
|
||||
{
|
||||
// ------------------------------------------------------------------ parsing a /lights answer
|
||||
|
||||
@Test
|
||||
public void theAnswerIsParsedIntoOneLightPerEntry()
|
||||
{
|
||||
LightsProxy.Light[] lights = LightsProxy.parseLights(
|
||||
"{\"ok\": true, \"lights\": ["
|
||||
+ "{\"id\": \"bedroom\", \"name\": \"Bedroom\", \"entity_id\": \"light.bedroom\","
|
||||
+ " \"state\": \"on\", \"optimistic_state\": null, \"warning\": null},"
|
||||
+ "{\"id\": \"stairs\", \"name\": \"Stairs\", \"entity_id\": \"light.stairs\","
|
||||
+ " \"state\": \"off\", \"optimistic_state\": null, \"warning\": null}"
|
||||
+ "]}");
|
||||
assertNotNull(lights);
|
||||
assertEquals(2, lights.length);
|
||||
assertEquals("bedroom", lights[0].id);
|
||||
assertEquals("Bedroom", lights[0].name);
|
||||
assertEquals("light.bedroom", lights[0].entityId);
|
||||
assertEquals(LightsProxy.State.ON, lights[0].state);
|
||||
assertEquals(LightsProxy.State.OFF, lights[1].state);
|
||||
assertNull("a read-back state needs no note", lights[0].note);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unavailableAndUnknownAreTheirOwnStatesAndNeverOff()
|
||||
{
|
||||
LightsProxy.Light[] lights = LightsProxy.parseLights(
|
||||
"{\"lights\": ["
|
||||
+ "{\"id\": \"a\", \"name\": \"A\", \"state\": \"unavailable\"},"
|
||||
+ "{\"id\": \"b\", \"name\": \"B\", \"state\": \"unknown\"},"
|
||||
+ "{\"id\": \"c\", \"name\": \"C\", \"state\": \" On \"},"
|
||||
+ "{\"id\": \"d\", \"name\": \"D\", \"state\": \"nonsense\"},"
|
||||
+ "{\"id\": \"e\", \"name\": \"E\"},"
|
||||
+ "{\"id\": \"f\", \"name\": \"F\", \"state\": \"OFF\"}"
|
||||
+ "]}");
|
||||
assertNotNull(lights);
|
||||
assertEquals(LightsProxy.State.UNKNOWN, lights[0].state);
|
||||
assertEquals(LightsProxy.State.UNKNOWN, lights[1].state);
|
||||
assertEquals(LightsProxy.State.ON, lights[2].state);
|
||||
assertEquals(LightsProxy.State.UNKNOWN, lights[3].state);
|
||||
assertEquals(LightsProxy.State.UNKNOWN, lights[4].state);
|
||||
assertEquals(LightsProxy.State.OFF, lights[5].state);
|
||||
for (LightsProxy.Light l : lights)
|
||||
assertFalse("nothing unreadable may be reported as switched off",
|
||||
l.state == LightsProxy.State.OFF && !l.id.equals("f"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fieldsTheKeyDoesNotShowNeverBecomeItsLabel()
|
||||
{
|
||||
// A registry entry carries more than the page uses - when it last changed, a read age, and the
|
||||
// service's error code. None of it may take the place of the light's name: while no token is in
|
||||
// place every light is unavailable, and the page would otherwise say "no_token" twelve times
|
||||
// instead of naming the lights. The stale colour already says nobody can see them.
|
||||
LightsProxy.Light[] lights = LightsProxy.parseLights(
|
||||
"{\"lights\": ["
|
||||
+ "{\"id\": \"bedroom\", \"name\": \"Bedroom\", \"state\": \"unavailable\","
|
||||
+ " \"last_changed\": \"2026-09-28T02:00:00Z\", \"age_s\": 42,"
|
||||
+ " \"error\": {\"code\": \"no_token\", \"detail\": \"no ha token configured\"},"
|
||||
+ " \"warning\": \"state could not be confirmed\"}"
|
||||
+ "]}");
|
||||
assertNotNull(lights);
|
||||
assertEquals("Bedroom", lights[0].name);
|
||||
assertNull("the answer to a read is not a note on the key", lights[0].note);
|
||||
assertEquals("an unreachable light is the stale look, never off",
|
||||
LightsProxy.State.UNKNOWN, lights[0].state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anEntryForALightTheBoxCannotSeeKeepsItsName()
|
||||
{
|
||||
// A light whose entity is unreachable comes back with an error object instead of state.
|
||||
LightsProxy.Light[] lights = LightsProxy.parseLights(
|
||||
"{\"lights\": ["
|
||||
+ "{\"id\": \"tv-room\", \"name\": \"TV room\", \"entity_id\": \"light.tv_room\","
|
||||
+ " \"state\": \"unavailable\", \"error\": {\"code\": \"unreachable\", \"detail\": \"http\"}}"
|
||||
+ "]}");
|
||||
assertNotNull(lights);
|
||||
assertEquals("TV room", lights[0].name);
|
||||
assertEquals(LightsProxy.State.UNKNOWN, lights[0].state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theAnswerIsReadWhateverTheLightWeightAndEscaping()
|
||||
{
|
||||
// Whitespace and escaped quotes inside a name must not derail the reader; the fields the page
|
||||
// does not use (numbers, booleans, nested objects) are skipped harmlessly.
|
||||
LightsProxy.Light[] lights = LightsProxy.parseLights(
|
||||
"{ \"lights\" : [ {\n \"id\":\"a\",\n \"name\": \"Say \\\"hi\\\"\",\n \"brightness\": 128,\n"
|
||||
+ " \"on\": true,\n \"attrs\": {\"a\": [1, 2, {\"b\": null}]},\n \"state\": \"on\" } ] }");
|
||||
assertNotNull(lights);
|
||||
assertEquals(1, lights.length);
|
||||
assertEquals("Say \"hi\"", lights[0].name);
|
||||
assertEquals(LightsProxy.State.ON, lights[0].state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anEmptyWhitelistIsNoLightsNotNoAnswer()
|
||||
{
|
||||
LightsProxy.Light[] none = LightsProxy.parseLights("{\"lights\": []}");
|
||||
assertNotNull("an empty list is an answer: the page has nothing to show", none);
|
||||
assertEquals(0, none.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aBrokenOrUnexpectedAnswerIsNoDataNotZeroLights()
|
||||
{
|
||||
assertNull(LightsProxy.parseLights(null));
|
||||
assertNull(LightsProxy.parseLights(""));
|
||||
assertNull(LightsProxy.parseLights("<html>404</html>"));
|
||||
assertNull(LightsProxy.parseLights("[1, 2, 3]"));
|
||||
assertNull(LightsProxy.parseLights("{\"lights\": [{\"id\": \"a\""));
|
||||
assertNull(LightsProxy.parseLights("{\"logs\": []}"));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ parsing a toggle answer
|
||||
|
||||
@Test
|
||||
public void aToggleThatWasReadBackReportsTheStateTheBoxGave()
|
||||
{
|
||||
LightsProxy.ToggleResult r = LightsProxy.parseToggle(
|
||||
"{\"ok\": true, \"id\": \"bedroom\", \"accepted\": true, \"state\": \"off\","
|
||||
+ " \"confirmed\": true, \"optimistic_state\": \"off\", \"warning\": null}");
|
||||
assertNotNull(r);
|
||||
assertTrue(r.accepted);
|
||||
assertTrue(r.confirmed);
|
||||
assertEquals("the read-back, as Home Assistant gave it", "off", r.state);
|
||||
assertEquals(LightsProxy.State.OFF, LightsProxy.stateOf(r.state));
|
||||
assertNull("a read-back state needs no warning on the key", r.note());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aToggleTheBoxRefusedIsNotAState()
|
||||
{
|
||||
// The proxy refuses when Home Assistant cannot be reached, or when the token is missing - which
|
||||
// is the state the endpoint is in until a token is put in place. The key must say why, and must
|
||||
// NOT show the light as off.
|
||||
LightsProxy.ToggleResult r = LightsProxy.parseToggle(
|
||||
"{\"ok\": false, \"id\": \"bedroom\", \"accepted\": false, \"state\": null,"
|
||||
+ " \"confirmed\": false, \"optimistic_state\": null, \"retried\": false,"
|
||||
+ " \"error\": {\"code\": \"no_token\", \"detail\": \"no ha token configured\"}}");
|
||||
assertNotNull(r);
|
||||
assertFalse(r.accepted);
|
||||
assertFalse(r.confirmed);
|
||||
assertNull("a refused toggle says nothing about the state", r.state);
|
||||
assertEquals("the service's own reason is what the key says", "no_token", r.note());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRefusalOfAServiceIsTheReasonTheKeyShows()
|
||||
{
|
||||
// The refusal the running service actually gives, recorded from
|
||||
// POST http://127.0.0.1:8772/lights/no-such-light/toggle (HTTP 404): a flat code and a prose
|
||||
// reason, and no state at all - so the key must show the code, not a guess.
|
||||
LightsProxy.ToggleResult r = LightsProxy.parseToggle(
|
||||
"{\"ts\": \"2026-09-28T03:14:51Z\", \"error\": \"unknown id\", \"code\": \"unknown_id\","
|
||||
+ " \"id\": \"no-such-light\", \"known_ids\": [\"bedroom\"], \"detail\": \"only ids listed"
|
||||
+ " in lights.toml can be toggled\", \"accepted\": false, \"toggled\": false}");
|
||||
assertNotNull(r);
|
||||
assertFalse(r.accepted);
|
||||
assertNull(r.state);
|
||||
assertEquals("unknown_id", r.note());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aToggleNobodyCouldReadBackIsNotClaimedAsSuccess()
|
||||
{
|
||||
LightsProxy.ToggleResult r = LightsProxy.parseToggle(
|
||||
"{\"ok\": true, \"id\": \"bedroom\", \"accepted\": true, \"state\": null,"
|
||||
+ " \"confirmed\": false, \"optimistic_state\": \"on\","
|
||||
+ " \"warning\": \"state could not be confirmed after 10s\"}");
|
||||
assertNotNull(r);
|
||||
assertTrue(r.accepted);
|
||||
assertFalse("nothing was read back, so nothing is confirmed", r.confirmed);
|
||||
assertNull("the prediction is not the state", r.state);
|
||||
assertEquals("the prose is kept, but a key says the short form of it",
|
||||
"state could not be confirmed after 10s", r.warning);
|
||||
assertEquals("unconfirmed", r.note());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aPredictionIsNeverMistakenForAState()
|
||||
{
|
||||
// optimistic_state is what the proxy thinks the light will be; only `state` is read back.
|
||||
LightsProxy.ToggleResult r = LightsProxy.parseToggle(
|
||||
"{\"accepted\": true, \"id\": \"a\", \"optimistic_state\": \"on\", \"state\": null,"
|
||||
+ " \"confirmed\": false}");
|
||||
assertNotNull(r);
|
||||
assertNull(r.state);
|
||||
assertNotNull("an unconfirmed toggle must say something", r.note());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aBrokenToggleAnswerIsNoAnswer()
|
||||
{
|
||||
assertNull(LightsProxy.parseToggle(null));
|
||||
assertNull(LightsProxy.parseToggle(""));
|
||||
assertNull(LightsProxy.parseToggle("nonsense"));
|
||||
assertNull("a body that says neither yes, no, nor what it saw", LightsProxy.parseToggle("{}"));
|
||||
assertNull(LightsProxy.parseToggle("{\"id\": \"a\"}"));
|
||||
assertNull(LightsProxy.parseToggle("{\"ok\": true, \"retried\": false}"));
|
||||
assertNull(LightsProxy.parseToggle("[1, 2]"));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ the colour rule
|
||||
|
||||
@Test
|
||||
public void theColourRuleIsOnBlueOffPinkAndEverythingElseStale()
|
||||
{
|
||||
// The rule, all of it, in one place. The three colours come from the theme; these are the same
|
||||
// values res/values/themes.xml carries (colorLightOn is the Roku arrows' #00E5FF).
|
||||
final int blue = 0xFF00E5FF, pink = 0xFFFF2D95, grey = 0xFF8A8D93;
|
||||
assertEquals(blue, LightsProxy.colorFor(LightsProxy.State.ON, blue, pink, grey));
|
||||
assertEquals(pink, LightsProxy.colorFor(LightsProxy.State.OFF, blue, pink, grey));
|
||||
assertEquals("an unreachable light is neither colour",
|
||||
grey, LightsProxy.colorFor(LightsProxy.State.UNKNOWN, blue, pink, grey));
|
||||
assertEquals("and neither is one nobody has heard about",
|
||||
grey, LightsProxy.colorFor(null, blue, pink, grey));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theStateGlyphIsTheLitLampOnlyWhenTheLightIsOn()
|
||||
{
|
||||
assertEquals(0xE025, LightsPage.glyphFor(LightsProxy.State.ON).charAt(0));
|
||||
assertEquals(0xE026, LightsPage.glyphFor(LightsProxy.State.OFF).charAt(0));
|
||||
assertEquals("an unreachable light must not look lit",
|
||||
0xE026, LightsPage.glyphFor(LightsProxy.State.UNKNOWN).charAt(0));
|
||||
assertEquals(0xE026, LightsPage.glyphFor(null).charAt(0));
|
||||
assertEquals(1, LightsPage.glyphFor(LightsProxy.State.ON).length());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ the labels
|
||||
|
||||
@Test
|
||||
public void theLabelIsTheRegistryNameNotSomethingShorter()
|
||||
{
|
||||
// The short names the keyboard shows are the registry's own; there is no mapping from a light
|
||||
// to a label on this side, so whatever the endpoint returns is what a key says.
|
||||
assertEquals("Bedroom", LightsPage.displayName("Bedroom"));
|
||||
assertEquals("TV room", LightsPage.displayName("TV room"));
|
||||
assertEquals("Stairs", LightsPage.displayName("Stairs"));
|
||||
assertEquals("space around a name is not part of it", "Bedroom",
|
||||
LightsPage.displayName(" Bedroom\n"));
|
||||
assertEquals("", LightsPage.displayName(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNameTooLongForItsKeyIsCutVisibly()
|
||||
{
|
||||
assertEquals("Basement Stairs Light", LightsPage.ellipsize("Basement Stairs Light", 32));
|
||||
String cut = LightsPage.displayName("Basement Stairs Light");
|
||||
assertTrue("a cut label is marked as cut: " + cut, cut.endsWith("…"));
|
||||
assertEquals(LightsPage.MAX_NAME_CHARS, cut.length());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ the page
|
||||
|
||||
@Test
|
||||
public void oneKeyPerLightThreeToARow()
|
||||
{
|
||||
KeyboardData page = LightsPage.build(template(), lights(4));
|
||||
assertEquals("3 lights, 1 stretched to the whole width, and the Keyboard key",
|
||||
3, page.rows.size());
|
||||
assertEquals(3, page.rows.get(0).keys.size());
|
||||
assertEquals(1, page.rows.get(1).keys.size());
|
||||
assertEquals(1, page.rows.get(2).keys.size());
|
||||
assertEquals("a full row of three is the whole width", 9.9f, page.rows.get(0).keysWidth, 0.001f);
|
||||
assertEquals(3.3f, page.rows.get(0).keys.get(0).width, 0.001f);
|
||||
assertEquals("the short row fills the keyboard", 9.9f, page.rows.get(1).keysWidth, 0.001f);
|
||||
assertEquals("one key stretched to the whole width", 9.9f, page.rows.get(1).keys.get(0).width,
|
||||
0.001f);
|
||||
assertEquals("the way back is the template's own key", 9.9f,
|
||||
page.rows.get(2).keys.get(0).width, 0.001f);
|
||||
assertEquals("Keyboard", page.rows.get(2).keys.get(0).keys[0].getString());
|
||||
assertEquals(KeyValue.Event.SWITCH_TEXT, page.rows.get(2).keys.get(0).keys[0].getEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyKeyShowsItsLightNameAndItsStateGlyph()
|
||||
{
|
||||
KeyboardData page = LightsPage.build(template(), lights(3));
|
||||
assertEquals("the name the registry gave", "Bedroom",
|
||||
page.rows.get(0).keys.get(0).keys[0].getString());
|
||||
assertEquals("TV room", page.rows.get(0).keys.get(2).keys[0].getString());
|
||||
// The name and the glyph sit on the same key and press the same light; the glyph is the value
|
||||
// drawn in the special font, which is what tells the two apart.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
KeyboardData.Key k = page.rows.get(0).keys.get(i);
|
||||
KeyValue name = k.keys[0], glyph = k.keys[LightsPage.GLYPH_SUB_INDEX];
|
||||
assertTrue(LightsPage.isLightKey(name));
|
||||
assertTrue(LightsPage.isStateGlyph(glyph));
|
||||
assertFalse("the name is not the glyph", LightsPage.isStateGlyph(name));
|
||||
assertEquals(LightsPage.eventFor(i + 1), name.getEvent());
|
||||
assertEquals("both values press the same light", name.getEvent(), glyph.getEvent());
|
||||
assertEquals(KeyValue.Kind.Event, name.getKind());
|
||||
assertEquals("the stored glyph never claims a light is on",
|
||||
(char)0xE026, glyph.getString().charAt(0));
|
||||
assertEquals("the name is drawn in the ordinary font, not the glyph font",
|
||||
false, name.hasFlagsAny(KeyValue.FLAG_KEY_FONT));
|
||||
}
|
||||
assertEquals(KeyValue.Event.LIGHT_3, page.rows.get(0).keys.get(2).keys[0].getEvent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNoAnswerYetThePlaceholdersStand()
|
||||
{
|
||||
KeyboardData page = LightsPage.build(template(), null);
|
||||
assertEquals(2, page.rows.size());
|
||||
assertEquals(3, page.rows.get(0).keys.size());
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
KeyboardData.Key k = page.rows.get(0).keys.get(i);
|
||||
assertEquals(LightsPage.PLACEHOLDER, k.keys[0].getString());
|
||||
assertTrue("a slot before the first answer still carries the unlit lamp",
|
||||
LightsPage.isStateGlyph(k.keys[LightsPage.GLYPH_SUB_INDEX]));
|
||||
}
|
||||
// Nothing has been heard from the proxy, so nothing is ON and the page is stale.
|
||||
assertTrue(LightsProxy.isStaleNow());
|
||||
assertEquals(LightsProxy.State.UNKNOWN, LightsProxy.stateAt(1));
|
||||
assertEquals(LightsPage.glyphFor(LightsProxy.State.UNKNOWN),
|
||||
LightsPage.glyphFor(LightsProxy.stateAt(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildingThePageLeavesTheTemplateAlone()
|
||||
{
|
||||
KeyboardData tpl = template();
|
||||
LightsPage.build(tpl, lights(5));
|
||||
assertEquals("KeyboardData.load caches the template, so it must survive being built from",
|
||||
2, tpl.rows.size());
|
||||
assertEquals(3, tpl.rows.get(0).keys.size());
|
||||
assertEquals("the template's slot still points at its own light", KeyValue.Event.LIGHT_1,
|
||||
tpl.rows.get(0).keys.get(0).keys[0].getEvent());
|
||||
assertEquals(LightsPage.PLACEHOLDER, tpl.rows.get(0).keys.get(0).keys[0].getString());
|
||||
assertNull("and the template's corner is still empty",
|
||||
tpl.rows.get(0).keys.get(0).keys[LightsPage.GLYPH_SUB_INDEX]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moreLightsThanSlotsIsSaidRatherThanDropped()
|
||||
{
|
||||
LightsProxy.Light[] many = lights(LightsPage.MAX_LIGHTS + 1);
|
||||
KeyboardData page = LightsPage.build(template(), many);
|
||||
assertEquals(5, page.rows.size());
|
||||
String last = page.rows.get(3).keys.get(2).keys[0].getString();
|
||||
assertTrue("the last key must say how many lights it cannot show: " + last, last.endsWith("+1"));
|
||||
assertTrue(last.length() <= LightsPage.MAX_NAME_CHARS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aKeyThePageDoesNotOwnIsNotALightsKey()
|
||||
{
|
||||
KeyValue door = KeyValue.getSpecialKeyByName("switch_lights");
|
||||
KeyValue back = KeyValue.getSpecialKeyByName("lights_close");
|
||||
assertFalse(LightsPage.isLightKey(door));
|
||||
assertFalse(LightsPage.isStateGlyph(door));
|
||||
assertFalse(LightsPage.isLightKey(back));
|
||||
assertFalse(LightsPage.isLightKey(null));
|
||||
assertEquals(0, LightsPage.slotOf(null));
|
||||
assertEquals(0, LightsPage.slotOf(back.getEvent()));
|
||||
assertEquals((char)0xE025, door.getString().charAt(0));
|
||||
assertTrue("the door is drawn in the special font", door.hasFlagsAny(KeyValue.FLAG_KEY_FONT));
|
||||
assertEquals("and the way back is the same key as the Roku page's",
|
||||
KeyValue.Event.SWITCH_TEXT, back.getEvent());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ what a key says
|
||||
|
||||
@Test
|
||||
public void aNoteSurvivesARefreshUntilTheBoxReportsAState()
|
||||
{
|
||||
LightsProxy.Light noted = new LightsProxy.Light("a", "A", "switch.a",
|
||||
LightsProxy.State.UNKNOWN, "no_token", 1000L);
|
||||
LightsProxy.Light[] previous = { noted };
|
||||
// A refresh that still cannot read a state keeps the last answer ...
|
||||
LightsProxy.Light stillUnknown = new LightsProxy.Light("a", "A", "switch.a",
|
||||
LightsProxy.State.UNKNOWN, null, 0L);
|
||||
assertEquals("no_token", LightsProxy.mergeNote(stillUnknown, previous).note);
|
||||
// ... a real state from the box supersedes it ...
|
||||
LightsProxy.Light known = new LightsProxy.Light("a", "A", "switch.a",
|
||||
LightsProxy.State.ON, null, 0L);
|
||||
assertNull(LightsProxy.mergeNote(known, previous).note);
|
||||
// ... a light that is not in the previous list stands on its own ...
|
||||
assertNull(LightsProxy.mergeNote(stillUnknown, new LightsProxy.Light[0]).note);
|
||||
// ... and the sentence expires, so an old answer cannot pass for the current state.
|
||||
assertNotNull(noted.noteOrNull(1000L + LightsProxy.NOTE_MS - 1));
|
||||
assertNull(noted.noteOrNull(1000L + LightsProxy.NOTE_MS));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ helpers
|
||||
|
||||
/** The page skeleton, built by hand to the same shape as res/xml/lights.xml. */
|
||||
private static KeyboardData template()
|
||||
{
|
||||
List<KeyboardData.Key> slots = new ArrayList<KeyboardData.Key>();
|
||||
for (int i = 1; i <= LightsPage.SLOTS_PER_ROW; i++)
|
||||
slots.add(key(KeyValue.getSpecialKeyByName("light_" + i), 3.3f, 0f));
|
||||
List<KeyboardData.Key> close = new ArrayList<KeyboardData.Key>();
|
||||
close.add(key(KeyValue.getSpecialKeyByName("lights_close"), 9.9f, 0f));
|
||||
List<KeyboardData.Row> rows = new ArrayList<KeyboardData.Row>();
|
||||
rows.add(new KeyboardData.Row(slots, 1.0f, 0f));
|
||||
rows.add(new KeyboardData.Row(close, 1.0f, 0f));
|
||||
return new KeyboardData(rows, 9.9f, null, null, null, "lights", false, false, false);
|
||||
}
|
||||
|
||||
private static KeyboardData.Key key(KeyValue kv, float width, float shift)
|
||||
{
|
||||
KeyValue[] ks = new KeyValue[9];
|
||||
ks[0] = kv;
|
||||
return new KeyboardData.Key(ks, null, 0, width, shift, null, KeyboardData.Key.Role.Action);
|
||||
}
|
||||
|
||||
/** [n] lights with short registry-style names, each row of three named in turn. */
|
||||
private static LightsProxy.Light[] lights(int n)
|
||||
{
|
||||
String[] words = { "Bedroom", "Stairs", "TV room", "Light Four", "Light Five", "Light Six",
|
||||
"Light Seven", "Light Eight", "Light Nine", "Light Ten", "Light Eleven", "Light Twelve",
|
||||
"Light Thirteen" };
|
||||
LightsProxy.Light[] out = new LightsProxy.Light[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
out[i] = new LightsProxy.Light("id" + i, words[i], "switch." + i,
|
||||
i % 2 == 0 ? LightsProxy.State.ON : LightsProxy.State.OFF, null, 0L);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class UsageGaugeTest
|
||||
{
|
||||
private static final String RESPONSE = "{\"codex\":{\"h5\":{\"pct\":2.0},"
|
||||
+ "\"weekly\":{\"pct\":90.0}},\"deepseek\":{"
|
||||
+ "\"spent_today_usd\":1.25,\"topped_up_today_usd\":9.0,\"balance\":0.99}}";
|
||||
|
||||
@Test
|
||||
public void parsesOnlyTheDisplayedUsageValues()
|
||||
{
|
||||
UsageGauge.Reading reading = UsageGauge.parse(RESPONSE);
|
||||
assertEquals(2.0, reading.h5Pct, 0.0);
|
||||
assertEquals(90.0, reading.weeklyPct, 0.0);
|
||||
assertEquals(1.25, reading.spentTodayUsd, 0.0);
|
||||
assertEquals(0.99, reading.balance, 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingRequiredValuesFallBackToUnknown()
|
||||
{
|
||||
assertNull(UsageGauge.parse("{}"));
|
||||
assertNull(UsageGauge.parse("{\"codex\":{\"h5\":{\"pct\":2},"
|
||||
+ "\"weekly\":{\"pct\":4}},\"deepseek\":{\"balance\":2}}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fillUsesLargerCodexWindowAndClampsToRange()
|
||||
{
|
||||
UsageGauge.Reading reading = UsageGauge.parse(RESPONSE);
|
||||
assertEquals(0.9f, UsageGauge.fillFraction(reading), 0.0001f);
|
||||
assertEquals(1.0f, UsageGauge.fillFraction(UsageGauge.parse(
|
||||
RESPONSE.replace("90.0", "120.0"))), 0.0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staleDecisionIncludesAgeFailureAndMissingToken()
|
||||
{
|
||||
assertFalse(UsageGauge.isStale(1000, 1000 + UsageGauge.STALE_AFTER_MS, false, "token"));
|
||||
assertTrue(UsageGauge.isStale(1000, 1001 + UsageGauge.STALE_AFTER_MS, false, "token"));
|
||||
assertTrue(UsageGauge.isStale(1000, 1001, true, "token"));
|
||||
assertTrue(UsageGauge.isStale(1000, 1001, false, " "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lowBalanceWarningFiresAtOneDollarOrLess()
|
||||
{
|
||||
assertTrue(UsageGauge.isLowBalance(UsageGauge.parse(RESPONSE)));
|
||||
// exactly $1.00 counts as low, so the boundary is inclusive
|
||||
assertTrue(UsageGauge.isLowBalance(UsageGauge.parse(
|
||||
RESPONSE.replace("0.99", "1.00"))));
|
||||
// and one cent above it must not
|
||||
assertFalse(UsageGauge.isLowBalance(UsageGauge.parse(
|
||||
RESPONSE.replace("0.99", "1.01"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aPartialDayKeepsTheNumberButFlagsIt()
|
||||
{
|
||||
UsageGauge.Reading partial = UsageGauge.parse(RESPONSE.replace(
|
||||
"\"balance\":0.99}", "\"balance\":0.99,\"partial\":true}"));
|
||||
assertTrue(partial.partial);
|
||||
assertFalse(UsageGauge.parse(RESPONSE).partial);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aReadingTheServerCallsStaleIsNotPresentedAsCurrent()
|
||||
{
|
||||
UsageGauge.Reading stale = UsageGauge.parse(RESPONSE.replace(
|
||||
"\"balance\":0.99}", "\"balance\":0.99,\"stale\":true}"));
|
||||
assertTrue(stale.serverStale);
|
||||
assertFalse(UsageGauge.parse(RESPONSE).serverStale);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneMissedSampleIsNotStaleButThreeAre()
|
||||
{
|
||||
long now = 1000000000000L;
|
||||
assertFalse(UsageGauge.isStale(now - 20 * 60 * 1000L, now, false, "tok"));
|
||||
assertTrue(UsageGauge.isStale(now - 50 * 60 * 1000L, now, false, "tok"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package juloo.keyboard2;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DeepSeekPeakTest
|
||||
{
|
||||
private static long utc(int year, int month, int day, int hour, int minute)
|
||||
{
|
||||
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
calendar.clear();
|
||||
calendar.set(year, month - 1, day, hour, minute, 0);
|
||||
return calendar.getTimeInMillis();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void peakWindowBoundariesAndWeekend()
|
||||
{
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 8, 0, 59))); // Monday 00:59
|
||||
assertTrue(DeepSeekPeak.isPeak(utc(2024, 1, 8, 1, 0))); // Monday 01:00
|
||||
assertTrue(DeepSeekPeak.isPeak(utc(2024, 1, 8, 3, 59))); // Monday 03:59
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 8, 4, 0))); // Monday 04:00
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 8, 5, 59))); // Monday 05:59
|
||||
assertTrue(DeepSeekPeak.isPeak(utc(2024, 1, 8, 6, 0))); // Monday 06:00
|
||||
assertTrue(DeepSeekPeak.isPeak(utc(2024, 1, 8, 9, 59))); // Monday 09:59
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 8, 10, 0))); // Monday 10:00
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 13, 2, 0))); // Saturday 02:00
|
||||
assertFalse(DeepSeekPeak.isPeak(utc(2024, 1, 14, 8, 0))); // Sunday 08:00
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user