IDNAConvert.php 94 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608
  1. <?php
  2. namespace MGModule\DNSManager2\mgLibs\custom\dns\utils;
  3. // {{{ license
  4. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
  5. //
  6. // +----------------------------------------------------------------------+
  7. // | This library is free software; you can redistribute it and/or modify |
  8. // | it under the terms of the GNU Lesser General Public License as |
  9. // | published by the Free Software Foundation; either version 2.1 of the |
  10. // | License, or (at your option) any later version. |
  11. // | |
  12. // | This library is distributed in the hope that it will be useful, but |
  13. // | WITHOUT ANY WARRANTY; without even the implied warranty of |
  14. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
  15. // | Lesser General Public License for more details. |
  16. // | |
  17. // | You should have received a copy of the GNU Lesser General Public |
  18. // | License along with this library; if not, write to the Free Software |
  19. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
  20. // | USA. |
  21. // +----------------------------------------------------------------------+
  22. //
  23. // }}}
  24. /**
  25. * Encode/decode Internationalized Domain Names.
  26. *
  27. * The class allows to convert internationalized domain names
  28. * (see RFC 3490 for details) as they can be used with various registries worldwide
  29. * to be translated between their original (localized) form and their encoded form
  30. * as it will be used in the DNS (Domain Name System).
  31. *
  32. * The class provides two public methods, encode() and decode(), which do exactly
  33. * what you would expect them to do. You are allowed to use complete domain names,
  34. * simple strings and complete email addresses as well. That means, that you might
  35. * use any of the following notations:
  36. *
  37. * - www.nörgler.com
  38. * - xn--nrgler-wxa
  39. * - xn--brse-5qa.xn--knrz-1ra.info
  40. *
  41. * Unicode input might be given as either UTF-8 string, UCS-4 string or UCS-4 array.
  42. * Unicode output is available in the same formats.
  43. * You can select your preferred format via {@link set_paramter()}.
  44. *
  45. * ACE input and output is always expected to be ASCII.
  46. *
  47. * @author Matthias Sommerfeld <mso@phlylabs.de>
  48. * @copyright 2004-2011 phlyLabs Berlin, http://phlylabs.de
  49. * @version 0.8.0 2011-03-11
  50. */
  51. class IDNAConvert
  52. {
  53. // NP See below
  54. // Internal settings, do not mess with them
  55. protected $_punycode_prefix = 'xn--';
  56. protected $_invalid_ucs = 0x80000000;
  57. protected $_max_ucs = 0x10FFFF;
  58. protected $_base = 36;
  59. protected $_tmin = 1;
  60. protected $_tmax = 26;
  61. protected $_skew = 38;
  62. protected $_damp = 700;
  63. protected $_initial_bias = 72;
  64. protected $_initial_n = 0x80;
  65. protected $_sbase = 0xAC00;
  66. protected $_lbase = 0x1100;
  67. protected $_vbase = 0x1161;
  68. protected $_tbase = 0x11A7;
  69. protected $_lcount = 19;
  70. protected $_vcount = 21;
  71. protected $_tcount = 28;
  72. protected $_ncount = 588; // _vcount * _tcount
  73. protected $_scount = 11172; // _lcount * _tcount * _vcount
  74. protected $_error = false;
  75. protected static $_mb_string_overload = null;
  76. // See {@link set_paramter()} for details of how to change the following
  77. // settings from within your script / application
  78. protected $_api_encoding = 'utf8'; // Default input charset is UTF-8
  79. protected $_allow_overlong = false; // Overlong UTF-8 encodings are forbidden
  80. protected $_strict_mode = false; // Behave strict or not
  81. protected $_idn_version = 2003; // Can be either 2003 (old, default) or 2008
  82. /**
  83. * the constructor
  84. *
  85. * @param array $options
  86. * @return boolean
  87. * @since 0.5.2
  88. */
  89. public function __construct($options = false)
  90. {
  91. $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
  92. // If parameters are given, pass these to the respective method
  93. if (is_array($options)) {
  94. $this->set_parameter($options);
  95. }
  96. // populate mbstring overloading cache if not set
  97. if (self::$_mb_string_overload === null) {
  98. self::$_mb_string_overload = (extension_loaded('mbstring')
  99. && (ini_get('mbstring.func_overload') & 0x02) === 0x02);
  100. }
  101. }
  102. /**
  103. * Sets a new option value. Available options and values:
  104. * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
  105. * 'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
  106. * [overlong - Unicode does not allow unnecessarily long encodings of chars,
  107. * to allow this, set this parameter to true, else to false;
  108. * default is false.]
  109. * [strict - true: strict mode, good for registration purposes - Causes errors
  110. * on failures; false: loose mode, ideal for "wildlife" applications
  111. * by silently ignoring errors and returning the original input instead
  112. *
  113. * @param mixed Parameter to set (string: single parameter; array of Parameter => Value pairs)
  114. * @param string Value to use (if parameter 1 is a string)
  115. * @return boolean true on success, false otherwise
  116. */
  117. public function set_parameter($option, $value = false)
  118. {
  119. if (!is_array($option)) {
  120. $option = array($option => $value);
  121. }
  122. foreach ($option as $k => $v) {
  123. switch ($k) {
  124. case 'encoding':
  125. switch ($v) {
  126. case 'utf8':
  127. case 'ucs4_string':
  128. case 'ucs4_array':
  129. $this->_api_encoding = $v;
  130. break;
  131. default:
  132. $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k);
  133. return false;
  134. }
  135. break;
  136. case 'overlong':
  137. $this->_allow_overlong = ($v) ? true : false;
  138. break;
  139. case 'strict':
  140. $this->_strict_mode = ($v) ? true : false;
  141. break;
  142. case 'idn_version':
  143. if (in_array($v, array('2003', '2008'))) {
  144. $this->_idn_version = $v;
  145. } else {
  146. $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k);
  147. }
  148. break;
  149. case 'encode_german_sz': // Deprecated
  150. if (!$v) {
  151. self::$NP['replacemaps'][0xDF] = array(0x73, 0x73);
  152. } else {
  153. unset(self::$NP['replacemaps'][0xDF]);
  154. }
  155. break;
  156. default:
  157. $this->_error('Set Parameter: Unknown option '.$k);
  158. return false;
  159. }
  160. }
  161. return true;
  162. }
  163. /**
  164. * Decode a given ACE domain name
  165. * @param string Domain name (ACE string)
  166. * [@param string Desired output encoding, see {@link set_parameter}]
  167. * @return string Decoded Domain name (UTF-8 or UCS-4)
  168. */
  169. public function decode($input, $one_time_encoding = false)
  170. {
  171. // Optionally set
  172. if ($one_time_encoding) {
  173. switch ($one_time_encoding) {
  174. case 'utf8':
  175. case 'ucs4_string':
  176. case 'ucs4_array':
  177. break;
  178. default:
  179. $this->_error('Unknown encoding '.$one_time_encoding);
  180. return false;
  181. }
  182. }
  183. // Make sure to drop any newline characters around
  184. $input = trim($input);
  185. // Negotiate input and try to determine, whether it is a plain string,
  186. // an email address or something like a complete URL
  187. if (strpos($input, '@')) { // Maybe it is an email address
  188. // No no in strict mode
  189. if ($this->_strict_mode) {
  190. $this->_error('Only simple domain name parts can be handled in strict mode');
  191. return false;
  192. }
  193. list ($email_pref, $input) = explode('@', $input, 2);
  194. $arr = explode('.', $input);
  195. foreach ($arr as $k => $v) {
  196. if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
  197. $conv = $this->_decode($v);
  198. if ($conv) $arr[$k] = $conv;
  199. }
  200. }
  201. $input = join('.', $arr);
  202. $arr = explode('.', $email_pref);
  203. foreach ($arr as $k => $v) {
  204. if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
  205. $conv = $this->_decode($v);
  206. if ($conv) $arr[$k] = $conv;
  207. }
  208. }
  209. $email_pref = join('.', $arr);
  210. $return = $email_pref . '@' . $input;
  211. } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters)
  212. // No no in strict mode
  213. if ($this->_strict_mode) {
  214. $this->_error('Only simple domain name parts can be handled in strict mode');
  215. return false;
  216. }
  217. $parsed = parse_url($input);
  218. if (isset($parsed['host'])) {
  219. $arr = explode('.', $parsed['host']);
  220. foreach ($arr as $k => $v) {
  221. $conv = $this->_decode($v);
  222. if ($conv) $arr[$k] = $conv;
  223. }
  224. $parsed['host'] = join('.', $arr);
  225. $return =
  226. (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
  227. .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@')
  228. .$parsed['host']
  229. .(empty($parsed['port']) ? '' : ':'.$parsed['port'])
  230. .(empty($parsed['path']) ? '' : $parsed['path'])
  231. .(empty($parsed['query']) ? '' : '?'.$parsed['query'])
  232. .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']);
  233. } else { // parse_url seems to have failed, try without it
  234. $arr = explode('.', $input);
  235. foreach ($arr as $k => $v) {
  236. $conv = $this->_decode($v);
  237. $arr[$k] = ($conv) ? $conv : $v;
  238. }
  239. $return = join('.', $arr);
  240. }
  241. } else { // Otherwise we consider it being a pure domain name string
  242. $return = $this->_decode($input);
  243. if (!$return) $return = $input;
  244. }
  245. // The output is UTF-8 by default, other output formats need conversion here
  246. // If one time encoding is given, use this, else the objects property
  247. switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
  248. case 'utf8':
  249. return $return;
  250. break;
  251. case 'ucs4_string':
  252. return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
  253. break;
  254. case 'ucs4_array':
  255. return $this->_utf8_to_ucs4($return);
  256. break;
  257. default:
  258. $this->_error('Unsupported output format');
  259. return false;
  260. }
  261. }
  262. /**
  263. * Encode a given UTF-8 domain name
  264. * @param string Domain name (UTF-8 or UCS-4)
  265. * [@param string Desired input encoding, see {@link set_parameter}]
  266. * @return string Encoded Domain name (ACE string)
  267. */
  268. public function encode($decoded, $one_time_encoding = false)
  269. {
  270. // Forcing conversion of input to UCS4 array
  271. // If one time encoding is given, use this, else the objects property
  272. switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) {
  273. case 'utf8':
  274. $decoded = $this->_utf8_to_ucs4($decoded);
  275. break;
  276. case 'ucs4_string':
  277. $decoded = $this->_ucs4_string_to_ucs4($decoded);
  278. case 'ucs4_array':
  279. break;
  280. default:
  281. $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding));
  282. return false;
  283. }
  284. // No input, no output, what else did you expect?
  285. if (empty($decoded)) return '';
  286. // Anchors for iteration
  287. $last_begin = 0;
  288. // Output string
  289. $output = '';
  290. foreach ($decoded as $k => $v) {
  291. // Make sure to use just the plain dot
  292. switch($v) {
  293. case 0x3002:
  294. case 0xFF0E:
  295. case 0xFF61:
  296. $decoded[$k] = 0x2E;
  297. // Right, no break here, the above are converted to dots anyway
  298. // Stumbling across an anchoring character
  299. case 0x2E:
  300. case 0x2F:
  301. case 0x3A:
  302. case 0x3F:
  303. case 0x40:
  304. // Neither email addresses nor URLs allowed in strict mode
  305. if ($this->_strict_mode) {
  306. $this->_error('Neither email addresses nor URLs are allowed in strict mode.');
  307. return false;
  308. } else {
  309. // Skip first char
  310. if ($k) {
  311. $encoded = '';
  312. $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin)));
  313. if ($encoded) {
  314. $output .= $encoded;
  315. } else {
  316. $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin)));
  317. }
  318. $output .= chr($decoded[$k]);
  319. }
  320. $last_begin = $k + 1;
  321. }
  322. }
  323. }
  324. // Catch the rest of the string
  325. if ($last_begin) {
  326. $inp_len = sizeof($decoded);
  327. $encoded = '';
  328. $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
  329. if ($encoded) {
  330. $output .= $encoded;
  331. } else {
  332. $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
  333. }
  334. return $output;
  335. } else {
  336. if ($output = $this->_encode($decoded)) {
  337. return $output;
  338. } else {
  339. return $this->_ucs4_to_utf8($decoded);
  340. }
  341. }
  342. }
  343. /**
  344. * Removes a weakness of encode(), which cannot properly handle URIs but instead encodes their
  345. * path or query components, too.
  346. * @param string $uri Expects the URI as a UTF-8 (or ASCII) string
  347. * @return string The URI encoded to Punycode, everything but the host component is left alone
  348. * @since 0.6.4
  349. */
  350. public function encode_uri($uri)
  351. {
  352. $parsed = parse_url($uri);
  353. if (!isset($parsed['host'])) {
  354. $this->_error('The given string does not look like a URI');
  355. return false;
  356. }
  357. $arr = explode('.', $parsed['host']);
  358. foreach ($arr as $k => $v) {
  359. $conv = $this->encode($v, 'utf8');
  360. if ($conv) $arr[$k] = $conv;
  361. }
  362. $parsed['host'] = join('.', $arr);
  363. $return =
  364. (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
  365. .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@')
  366. .$parsed['host']
  367. .(empty($parsed['port']) ? '' : ':'.$parsed['port'])
  368. .(empty($parsed['path']) ? '' : $parsed['path'])
  369. .(empty($parsed['query']) ? '' : '?'.$parsed['query'])
  370. .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']);
  371. return $return;
  372. }
  373. /**
  374. * Use this method to get the last error ocurred
  375. * @param void
  376. * @return string The last error, that occured
  377. */
  378. public function get_last_error()
  379. {
  380. return $this->_error;
  381. }
  382. /**
  383. * The actual decoding algorithm
  384. * @param string
  385. * @return mixed
  386. */
  387. protected function _decode($encoded)
  388. {
  389. $decoded = array();
  390. // find the Punycode prefix
  391. if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) {
  392. $this->_error('This is not a punycode string');
  393. return false;
  394. }
  395. $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded);
  396. // If nothing left after removing the prefix, it is hopeless
  397. if (!$encode_test) {
  398. $this->_error('The given encoded string was empty');
  399. return false;
  400. }
  401. // Find last occurence of the delimiter
  402. $delim_pos = strrpos($encoded, '-');
  403. if ($delim_pos > self::byteLength($this->_punycode_prefix)) {
  404. for ($k = self::byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) {
  405. $decoded[] = ord($encoded{$k});
  406. }
  407. }
  408. $deco_len = count($decoded);
  409. $enco_len = self::byteLength($encoded);
  410. // Wandering through the strings; init
  411. $is_first = true;
  412. $bias = $this->_initial_bias;
  413. $idx = 0;
  414. $char = $this->_initial_n;
  415. for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
  416. for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) {
  417. $digit = $this->_decode_digit($encoded{$enco_idx++});
  418. $idx += $digit * $w;
  419. $t = ($k <= $bias) ? $this->_tmin :
  420. (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
  421. if ($digit < $t) break;
  422. $w = (int) ($w * ($this->_base - $t));
  423. }
  424. $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first);
  425. $is_first = false;
  426. $char += (int) ($idx / ($deco_len + 1));
  427. $idx %= ($deco_len + 1);
  428. if ($deco_len > 0) {
  429. // Make room for the decoded char
  430. for ($i = $deco_len; $i > $idx; $i--) $decoded[$i] = $decoded[($i - 1)];
  431. }
  432. $decoded[$idx++] = $char;
  433. }
  434. return $this->_ucs4_to_utf8($decoded);
  435. }
  436. /**
  437. * The actual encoding algorithm
  438. * @param string
  439. * @return mixed
  440. */
  441. protected function _encode($decoded)
  442. {
  443. // We cannot encode a domain name containing the Punycode prefix
  444. $extract = self::byteLength($this->_punycode_prefix);
  445. $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix);
  446. $check_deco = array_slice($decoded, 0, $extract);
  447. if ($check_pref == $check_deco) {
  448. $this->_error('This is already a punycode string');
  449. return false;
  450. }
  451. // We will not try to encode strings consisting of basic code points only
  452. $encodable = false;
  453. foreach ($decoded as $k => $v) {
  454. if ($v > 0x7a) {
  455. $encodable = true;
  456. break;
  457. }
  458. }
  459. if (!$encodable) {
  460. $this->_error('The given string does not contain encodable chars');
  461. return false;
  462. }
  463. // Do NAMEPREP
  464. $decoded = $this->_nameprep($decoded);
  465. if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed
  466. $deco_len = count($decoded);
  467. if (!$deco_len) return false; // Empty array
  468. $codecount = 0; // How many chars have been consumed
  469. $encoded = '';
  470. // Copy all basic code points to output
  471. for ($i = 0; $i < $deco_len; ++$i) {
  472. $test = $decoded[$i];
  473. // Will match [-0-9a-zA-Z]
  474. if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B)
  475. || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) {
  476. $encoded .= chr($decoded[$i]);
  477. $codecount++;
  478. }
  479. }
  480. if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones
  481. // Start with the prefix; copy it to output
  482. $encoded = $this->_punycode_prefix.$encoded;
  483. // If we have basic code points in output, add an hyphen to the end
  484. if ($codecount) $encoded .= '-';
  485. // Now find and encode all non-basic code points
  486. $is_first = true;
  487. $cur_code = $this->_initial_n;
  488. $bias = $this->_initial_bias;
  489. $delta = 0;
  490. while ($codecount < $deco_len) {
  491. // Find the smallest code point >= the current code point and
  492. // remember the last ouccrence of it in the input
  493. for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) {
  494. if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) {
  495. $next_code = $decoded[$i];
  496. }
  497. }
  498. $delta += ($next_code - $cur_code) * ($codecount + 1);
  499. $cur_code = $next_code;
  500. // Scan input again and encode all characters whose code point is $cur_code
  501. for ($i = 0; $i < $deco_len; $i++) {
  502. if ($decoded[$i] < $cur_code) {
  503. $delta++;
  504. } elseif ($decoded[$i] == $cur_code) {
  505. for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) {
  506. $t = ($k <= $bias) ? $this->_tmin :
  507. (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias);
  508. if ($q < $t) break;
  509. $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval()
  510. $q = (int) (($q - $t) / ($this->_base - $t));
  511. }
  512. $encoded .= $this->_encode_digit($q);
  513. $bias = $this->_adapt($delta, $codecount+1, $is_first);
  514. $codecount++;
  515. $delta = 0;
  516. $is_first = false;
  517. }
  518. }
  519. $delta++;
  520. $cur_code++;
  521. }
  522. return $encoded;
  523. }
  524. /**
  525. * Adapt the bias according to the current code point and position
  526. * @param int $delta
  527. * @param int $npoints
  528. * @param int $is_first
  529. * @return int
  530. */
  531. protected function _adapt($delta, $npoints, $is_first)
  532. {
  533. $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2));
  534. $delta += intval($delta / $npoints);
  535. for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) {
  536. $delta = intval($delta / ($this->_base - $this->_tmin));
  537. }
  538. return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew));
  539. }
  540. /**
  541. * Encoding a certain digit
  542. * @param int $d
  543. * @return string
  544. */
  545. protected function _encode_digit($d)
  546. {
  547. return chr($d + 22 + 75 * ($d < 26));
  548. }
  549. /**
  550. * Decode a certain digit
  551. * @param int $cp
  552. * @return int
  553. */
  554. protected function _decode_digit($cp)
  555. {
  556. $cp = ord($cp);
  557. return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base));
  558. }
  559. /**
  560. * Internal error handling method
  561. * @param string $error
  562. */
  563. protected function _error($error = '')
  564. {
  565. $this->_error = $error;
  566. }
  567. /**
  568. * Do Nameprep according to RFC3491 and RFC3454
  569. * @param array Unicode Characters
  570. * @return string Unicode Characters, Nameprep'd
  571. */
  572. protected function _nameprep($input)
  573. {
  574. $output = array();
  575. $error = false;
  576. //
  577. // Mapping
  578. // Walking through the input array, performing the required steps on each of
  579. // the input chars and putting the result into the output array
  580. // While mapping required chars we apply the cannonical ordering
  581. foreach ($input as $v) {
  582. // Map to nothing == skip that code point
  583. if (in_array($v, self::$NP['map_nothing'])) continue;
  584. // Try to find prohibited input
  585. if (in_array($v, self::$NP['prohibit']) || in_array($v, self::$NP['general_prohibited'])) {
  586. $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
  587. return false;
  588. }
  589. foreach (self::$NP['prohibit_ranges'] as $range) {
  590. if ($range[0] <= $v && $v <= $range[1]) {
  591. $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
  592. return false;
  593. }
  594. }
  595. if (0xAC00 <= $v && $v <= 0xD7AF) {
  596. // Hangul syllable decomposition
  597. foreach ($this->_hangul_decompose($v) as $out) {
  598. $output[] = (int) $out;
  599. }
  600. } elseif (($this->_idn_version == '2003') && isset(self::$NP['replacemaps'][$v])) {
  601. // There's a decomposition mapping for that code point
  602. // Decompositions only in version 2003 (original) of IDNA
  603. foreach ($this->_apply_cannonical_ordering(self::$NP['replacemaps'][$v]) as $out) {
  604. $output[] = (int) $out;
  605. }
  606. } else {
  607. $output[] = (int) $v;
  608. }
  609. }
  610. // Before applying any Combining, try to rearrange any Hangul syllables
  611. $output = $this->_hangul_compose($output);
  612. //
  613. // Combine code points
  614. //
  615. $last_class = 0;
  616. $last_starter = 0;
  617. $out_len = count($output);
  618. for ($i = 0; $i < $out_len; ++$i) {
  619. $class = $this->_get_combining_class($output[$i]);
  620. if ((!$last_class || $last_class > $class) && $class) {
  621. // Try to match
  622. $seq_len = $i - $last_starter;
  623. $out = $this->_combine(array_slice($output, $last_starter, $seq_len));
  624. // On match: Replace the last starter with the composed character and remove
  625. // the now redundant non-starter(s)
  626. if ($out) {
  627. $output[$last_starter] = $out;
  628. if (count($out) != $seq_len) {
  629. for ($j = $i+1; $j < $out_len; ++$j) $output[$j-1] = $output[$j];
  630. unset($output[$out_len]);
  631. }
  632. // Rewind the for loop by one, since there can be more possible compositions
  633. $i--;
  634. $out_len--;
  635. $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]);
  636. continue;
  637. }
  638. }
  639. // The current class is 0
  640. if (!$class) $last_starter = $i;
  641. $last_class = $class;
  642. }
  643. return $output;
  644. }
  645. /**
  646. * Decomposes a Hangul syllable
  647. * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
  648. * @param integer 32bit UCS4 code point
  649. * @return array Either Hangul Syllable decomposed or original 32bit value as one value array
  650. */
  651. protected function _hangul_decompose($char)
  652. {
  653. $sindex = (int) $char - $this->_sbase;
  654. if ($sindex < 0 || $sindex >= $this->_scount) return array($char);
  655. $result = array();
  656. $result[] = (int) $this->_lbase + $sindex / $this->_ncount;
  657. $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount;
  658. $T = intval($this->_tbase + $sindex % $this->_tcount);
  659. if ($T != $this->_tbase) $result[] = $T;
  660. return $result;
  661. }
  662. /**
  663. * Ccomposes a Hangul syllable
  664. * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
  665. * @param array Decomposed UCS4 sequence
  666. * @return array UCS4 sequence with syllables composed
  667. */
  668. protected function _hangul_compose($input)
  669. {
  670. $inp_len = count($input);
  671. if (!$inp_len) return array();
  672. $result = array();
  673. $last = (int) $input[0];
  674. $result[] = $last; // copy first char from input to output
  675. for ($i = 1; $i < $inp_len; ++$i) {
  676. $char = (int) $input[$i];
  677. $sindex = $last - $this->_sbase;
  678. $lindex = $last - $this->_lbase;
  679. $vindex = $char - $this->_vbase;
  680. $tindex = $char - $this->_tbase;
  681. // Find out, whether two current characters are LV and T
  682. if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0)
  683. && 0 <= $tindex && $tindex <= $this->_tcount) {
  684. // create syllable of form LVT
  685. $last += $tindex;
  686. $result[(count($result) - 1)] = $last; // reset last
  687. continue; // discard char
  688. }
  689. // Find out, whether two current characters form L and V
  690. if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) {
  691. // create syllable of form LV
  692. $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount;
  693. $result[(count($result) - 1)] = $last; // reset last
  694. continue; // discard char
  695. }
  696. // if neither case was true, just add the character
  697. $last = $char;
  698. $result[] = $char;
  699. }
  700. return $result;
  701. }
  702. /**
  703. * Returns the combining class of a certain wide char
  704. * @param integer Wide char to check (32bit integer)
  705. * @return integer Combining class if found, else 0
  706. */
  707. protected function _get_combining_class($char)
  708. {
  709. return isset(self::$NP['norm_combcls'][$char]) ? self::$NP['norm_combcls'][$char] : 0;
  710. }
  711. /**
  712. * Applies the cannonical ordering of a decomposed UCS4 sequence
  713. * @param array Decomposed UCS4 sequence
  714. * @return array Ordered USC4 sequence
  715. */
  716. protected function _apply_cannonical_ordering($input)
  717. {
  718. $swap = true;
  719. $size = count($input);
  720. while ($swap) {
  721. $swap = false;
  722. $last = $this->_get_combining_class(intval($input[0]));
  723. for ($i = 0; $i < $size-1; ++$i) {
  724. $next = $this->_get_combining_class(intval($input[$i+1]));
  725. if ($next != 0 && $last > $next) {
  726. // Move item leftward until it fits
  727. for ($j = $i + 1; $j > 0; --$j) {
  728. if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break;
  729. $t = intval($input[$j]);
  730. $input[$j] = intval($input[$j-1]);
  731. $input[$j-1] = $t;
  732. $swap = true;
  733. }
  734. // Reentering the loop looking at the old character again
  735. $next = $last;
  736. }
  737. $last = $next;
  738. }
  739. }
  740. return $input;
  741. }
  742. /**
  743. * Do composition of a sequence of starter and non-starter
  744. * @param array UCS4 Decomposed sequence
  745. * @return array Ordered USC4 sequence
  746. */
  747. protected function _combine($input)
  748. {
  749. $inp_len = count($input);
  750. foreach (self::$NP['replacemaps'] as $np_src => $np_target) {
  751. if ($np_target[0] != $input[0]) continue;
  752. if (count($np_target) != $inp_len) continue;
  753. $hit = false;
  754. foreach ($input as $k2 => $v2) {
  755. if ($v2 == $np_target[$k2]) {
  756. $hit = true;
  757. } else {
  758. $hit = false;
  759. break;
  760. }
  761. }
  762. if ($hit) return $np_src;
  763. }
  764. return false;
  765. }
  766. /**
  767. * This converts an UTF-8 encoded string to its UCS-4 representation
  768. * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
  769. * each of the "chars". This is due to PHP not being able to handle strings with
  770. * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
  771. * The following UTF-8 encodings are supported:
  772. * bytes bits representation
  773. * 1 7 0xxxxxxx
  774. * 2 11 110xxxxx 10xxxxxx
  775. * 3 16 1110xxxx 10xxxxxx 10xxxxxx
  776. * 4 21 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  777. * 5 26 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  778. * 6 31 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  779. * Each x represents a bit that can be used to store character data.
  780. * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
  781. * @param string $input
  782. * @return string
  783. */
  784. protected function _utf8_to_ucs4($input)
  785. {
  786. $output = array();
  787. $out_len = 0;
  788. $inp_len = self::byteLength($input);
  789. $mode = 'next';
  790. $test = 'none';
  791. for ($k = 0; $k < $inp_len; ++$k) {
  792. $v = ord($input{$k}); // Extract byte from input string
  793. if ($v < 128) { // We found an ASCII char - put into stirng as is
  794. $output[$out_len] = $v;
  795. ++$out_len;
  796. if ('add' == $mode) {
  797. $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
  798. return false;
  799. }
  800. continue;
  801. }
  802. if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
  803. $start_byte = $v;
  804. $mode = 'add';
  805. $test = 'range';
  806. if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
  807. $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
  808. $v = ($v - 192) << 6;
  809. } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
  810. $next_byte = 1;
  811. $v = ($v - 224) << 12;
  812. } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  813. $next_byte = 2;
  814. $v = ($v - 240) << 18;
  815. } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  816. $next_byte = 3;
  817. $v = ($v - 248) << 24;
  818. } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
  819. $next_byte = 4;
  820. $v = ($v - 252) << 30;
  821. } else {
  822. $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k);
  823. return false;
  824. }
  825. if ('add' == $mode) {
  826. $output[$out_len] = (int) $v;
  827. ++$out_len;
  828. continue;
  829. }
  830. }
  831. if ('add' == $mode) {
  832. if (!$this->_allow_overlong && $test == 'range') {
  833. $test = 'none';
  834. if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
  835. $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k);
  836. return false;
  837. }
  838. }
  839. if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
  840. $v = ($v - 128) << ($next_byte * 6);
  841. $output[($out_len - 1)] += $v;
  842. --$next_byte;
  843. } else {
  844. $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
  845. return false;
  846. }
  847. if ($next_byte < 0) {
  848. $mode = 'next';
  849. }
  850. }
  851. } // for
  852. return $output;
  853. }
  854. /**
  855. * Convert UCS-4 string into UTF-8 string
  856. * See _utf8_to_ucs4() for details
  857. * @param string $input
  858. * @return string
  859. */
  860. protected function _ucs4_to_utf8($input)
  861. {
  862. $output = '';
  863. foreach ($input as $k => $v) {
  864. if ($v < 128) { // 7bit are transferred literally
  865. $output .= chr($v);
  866. } elseif ($v < (1 << 11)) { // 2 bytes
  867. $output .= chr(192+($v >> 6)).chr(128+($v & 63));
  868. } elseif ($v < (1 << 16)) { // 3 bytes
  869. $output .= chr(224+($v >> 12)).chr(128+(($v >> 6) & 63)).chr(128+($v & 63));
  870. } elseif ($v < (1 << 21)) { // 4 bytes
  871. $output .= chr(240+($v >> 18)).chr(128+(($v >> 12) & 63)).chr(128+(($v >> 6) & 63)).chr(128+($v & 63));
  872. } elseif (self::$safe_mode) {
  873. $output .= self::$safe_char;
  874. } else {
  875. $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k);
  876. return false;
  877. }
  878. }
  879. return $output;
  880. }
  881. /**
  882. * Convert UCS-4 array into UCS-4 string
  883. *
  884. * @param array $input
  885. * @return string
  886. */
  887. protected function _ucs4_to_ucs4_string($input)
  888. {
  889. $output = '';
  890. // Take array values and split output to 4 bytes per value
  891. // The bit mask is 255, which reads &11111111
  892. foreach ($input as $v) {
  893. $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255);
  894. }
  895. return $output;
  896. }
  897. /**
  898. * Convert UCS-4 strin into UCS-4 garray
  899. *
  900. * @param string $input
  901. * @return array
  902. */
  903. protected function _ucs4_string_to_ucs4($input)
  904. {
  905. $output = array();
  906. $inp_len = self::byteLength($input);
  907. // Input length must be dividable by 4
  908. if ($inp_len % 4) {
  909. $this->_error('Input UCS4 string is broken');
  910. return false;
  911. }
  912. // Empty input - return empty output
  913. if (!$inp_len) return $output;
  914. for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
  915. // Increment output position every 4 input bytes
  916. if (!($i % 4)) {
  917. $out_len++;
  918. $output[$out_len] = 0;
  919. }
  920. $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) );
  921. }
  922. return $output;
  923. }
  924. /**
  925. * Gets the length of a string in bytes even if mbstring function
  926. * overloading is turned on
  927. *
  928. * @param string $string the string for which to get the length.
  929. * @return integer the length of the string in bytes.
  930. */
  931. protected static function byteLength($string)
  932. {
  933. if (self::$_mb_string_overload) {
  934. return mb_strlen($string, '8bit');
  935. }
  936. return strlen((binary) $string);
  937. }
  938. /**
  939. * Attempts to return a concrete IDNA instance.
  940. *
  941. * @param array $params Set of paramaters
  942. * @return IDNAonvert
  943. * @access public
  944. */
  945. public function getInstance($params = array())
  946. {
  947. return new IDNAConvert($params);
  948. }
  949. /**
  950. * Attempts to return a concrete IDNA instance for either php4 or php5,
  951. * only creating a new instance if no IDNA instance with the same
  952. * parameters currently exists.
  953. *
  954. * @param array $params Set of paramaters
  955. *
  956. * @return object IDNAonvert
  957. * @access public
  958. */
  959. public function singleton($params = array())
  960. {
  961. static $instances;
  962. if (!isset($instances)) {
  963. $instances = array();
  964. }
  965. $signature = serialize($params);
  966. if (!isset($instances[$signature])) {
  967. $instances[$signature] = IDNAonvert::getInstance($params);
  968. }
  969. return $instances[$signature];
  970. }
  971. /**
  972. * Holds all relevant mapping tables
  973. * See RFC3454 for details
  974. *
  975. * @private array
  976. * @since 0.5.2
  977. */
  978. protected static $NP = array
  979. ('map_nothing' => array(0xAD, 0x34F, 0x1806, 0x180B, 0x180C, 0x180D, 0x200B, 0x200C
  980. ,0x200D, 0x2060, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07
  981. ,0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, 0xFEFF
  982. )
  983. ,'general_prohibited' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
  984. ,20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ,33, 34, 35, 36, 37, 38, 39, 40, 41, 42
  985. ,43, 44, 47, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 127, 0x3002
  986. )
  987. ,'prohibit' => array(0xA0, 0x340, 0x341, 0x6DD, 0x70F, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003
  988. ,0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x200E, 0x200F
  989. ,0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x202F, 0x205F, 0x206A, 0x206B, 0x206C
  990. ,0x206D, 0x206E, 0x206F, 0x3000, 0xFEFF, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF
  991. ,0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE
  992. ,0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, 0xBFFFF
  993. ,0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xE0001, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF
  994. )
  995. ,'prohibit_ranges' => array(array(0x80, 0x9F), array(0x2060, 0x206F), array(0x1D173, 0x1D17A)
  996. ,array(0xE000, 0xF8FF) ,array(0xF0000, 0xFFFFD), array(0x100000, 0x10FFFD)
  997. ,array(0xFDD0, 0xFDEF), array(0xD800, 0xDFFF), array(0x2FF0, 0x2FFB), array(0xE0020, 0xE007F)
  998. )
  999. ,'replacemaps' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63)
  1000. ,0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), 0x47 => array(0x67)
  1001. ,0x48 => array(0x68), 0x49 => array(0x69), 0x4A => array(0x6A), 0x4B => array(0x6B)
  1002. ,0x4C => array(0x6C), 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F)
  1003. ,0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), 0x53 => array(0x73)
  1004. ,0x54 => array(0x74), 0x55 => array(0x75), 0x56 => array(0x76), 0x57 => array(0x77)
  1005. ,0x58 => array(0x78), 0x59 => array(0x79), 0x5A => array(0x7A), 0xB5 => array(0x3BC)
  1006. ,0xC0 => array(0xE0), 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3)
  1007. ,0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), 0xC7 => array(0xE7)
  1008. ,0xC8 => array(0xE8), 0xC9 => array(0xE9), 0xCA => array(0xEA), 0xCB => array(0xEB)
  1009. ,0xCC => array(0xEC), 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF)
  1010. ,0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), 0xD3 => array(0xF3)
  1011. ,0xD4 => array(0xF4), 0xD5 => array(0xF5), 0xD6 => array(0xF6), 0xD8 => array(0xF8)
  1012. ,0xD9 => array(0xF9), 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC)
  1013. ,0xDD => array(0xFD), 0xDE => array(0xFE), 0xDF => array(0x73, 0x73)
  1014. ,0x100 => array(0x101), 0x102 => array(0x103), 0x104 => array(0x105)
  1015. ,0x106 => array(0x107), 0x108 => array(0x109), 0x10A => array(0x10B)
  1016. ,0x10C => array(0x10D), 0x10E => array(0x10F), 0x110 => array(0x111)
  1017. ,0x112 => array(0x113), 0x114 => array(0x115), 0x116 => array(0x117)
  1018. ,0x118 => array(0x119), 0x11A => array(0x11B), 0x11C => array(0x11D)
  1019. ,0x11E => array(0x11F), 0x120 => array(0x121), 0x122 => array(0x123)
  1020. ,0x124 => array(0x125), 0x126 => array(0x127), 0x128 => array(0x129)
  1021. ,0x12A => array(0x12B), 0x12C => array(0x12D), 0x12E => array(0x12F)
  1022. ,0x130 => array(0x69, 0x307), 0x132 => array(0x133), 0x134 => array(0x135)
  1023. ,0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C)
  1024. ,0x13D => array(0x13E), 0x13F => array(0x140), 0x141 => array(0x142)
  1025. ,0x143 => array(0x144), 0x145 => array(0x146), 0x147 => array(0x148)
  1026. ,0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), 0x14C => array(0x14D)
  1027. ,0x14E => array(0x14F), 0x150 => array(0x151), 0x152 => array(0x153)
  1028. ,0x154 => array(0x155), 0x156 => array(0x157), 0x158 => array(0x159)
  1029. ,0x15A => array(0x15B), 0x15C => array(0x15D), 0x15E => array(0x15F)
  1030. ,0x160 => array(0x161), 0x162 => array(0x163), 0x164 => array(0x165)
  1031. ,0x166 => array(0x167), 0x168 => array(0x169), 0x16A => array(0x16B)
  1032. ,0x16C => array(0x16D), 0x16E => array(0x16F), 0x170 => array(0x171)
  1033. ,0x172 => array(0x173), 0x174 => array(0x175), 0x176 => array(0x177)
  1034. ,0x178 => array(0xFF), 0x179 => array(0x17A), 0x17B => array(0x17C)
  1035. ,0x17D => array(0x17E), 0x17F => array(0x73), 0x181 => array(0x253)
  1036. ,0x182 => array(0x183), 0x184 => array(0x185), 0x186 => array(0x254)
  1037. ,0x187 => array(0x188), 0x189 => array(0x256), 0x18A => array(0x257)
  1038. ,0x18B => array(0x18C), 0x18E => array(0x1DD), 0x18F => array(0x259)
  1039. ,0x190 => array(0x25B), 0x191 => array(0x192), 0x193 => array(0x260)
  1040. ,0x194 => array(0x263), 0x196 => array(0x269), 0x197 => array(0x268)
  1041. ,0x198 => array(0x199), 0x19C => array(0x26F), 0x19D => array(0x272)
  1042. ,0x19F => array(0x275), 0x1A0 => array(0x1A1), 0x1A2 => array(0x1A3)
  1043. ,0x1A4 => array(0x1A5), 0x1A6 => array(0x280), 0x1A7 => array(0x1A8)
  1044. ,0x1A9 => array(0x283), 0x1AC => array(0x1AD), 0x1AE => array(0x288)
  1045. ,0x1AF => array(0x1B0), 0x1B1 => array(0x28A), 0x1B2 => array(0x28B)
  1046. ,0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), 0x1B7 => array(0x292)
  1047. ,0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), 0x1C4 => array(0x1C6)
  1048. ,0x1C5 => array(0x1C6), 0x1C7 => array(0x1C9), 0x1C8 => array(0x1C9)
  1049. ,0x1CA => array(0x1CC), 0x1CB => array(0x1CC), 0x1CD => array(0x1CE)
  1050. ,0x1CF => array(0x1D0), 0x1D1 => array(0x1D2), 0x1D3 => array(0x1D4)
  1051. ,0x1D5 => array(0x1D6), 0x1D7 => array(0x1D8), 0x1D9 => array(0x1DA)
  1052. ,0x1DB => array(0x1DC), 0x1DE => array(0x1DF), 0x1E0 => array(0x1E1)
  1053. ,0x1E2 => array(0x1E3), 0x1E4 => array(0x1E5), 0x1E6 => array(0x1E7)
  1054. ,0x1E8 => array(0x1E9), 0x1EA => array(0x1EB), 0x1EC => array(0x1ED)
  1055. ,0x1EE => array(0x1EF), 0x1F0 => array(0x6A, 0x30C), 0x1F1 => array(0x1F3)
  1056. ,0x1F2 => array(0x1F3), 0x1F4 => array(0x1F5), 0x1F6 => array(0x195)
  1057. ,0x1F7 => array(0x1BF), 0x1F8 => array(0x1F9), 0x1FA => array(0x1FB)
  1058. ,0x1FC => array(0x1FD), 0x1FE => array(0x1FF), 0x200 => array(0x201)
  1059. ,0x202 => array(0x203), 0x204 => array(0x205), 0x206 => array(0x207)
  1060. ,0x208 => array(0x209), 0x20A => array(0x20B), 0x20C => array(0x20D)
  1061. ,0x20E => array(0x20F), 0x210 => array(0x211), 0x212 => array(0x213)
  1062. ,0x214 => array(0x215), 0x216 => array(0x217), 0x218 => array(0x219)
  1063. ,0x21A => array(0x21B), 0x21C => array(0x21D), 0x21E => array(0x21F)
  1064. ,0x220 => array(0x19E), 0x222 => array(0x223), 0x224 => array(0x225)
  1065. ,0x226 => array(0x227), 0x228 => array(0x229), 0x22A => array(0x22B)
  1066. ,0x22C => array(0x22D), 0x22E => array(0x22F), 0x230 => array(0x231)
  1067. ,0x232 => array(0x233), 0x345 => array(0x3B9), 0x37A => array(0x20, 0x3B9)
  1068. ,0x386 => array(0x3AC), 0x388 => array(0x3AD), 0x389 => array(0x3AE)
  1069. ,0x38A => array(0x3AF), 0x38C => array(0x3CC), 0x38E => array(0x3CD)
  1070. ,0x38F => array(0x3CE), 0x390 => array(0x3B9, 0x308, 0x301)
  1071. ,0x391 => array(0x3B1), 0x392 => array(0x3B2), 0x393 => array(0x3B3)
  1072. ,0x394 => array(0x3B4), 0x395 => array(0x3B5), 0x396 => array(0x3B6)
  1073. ,0x397 => array(0x3B7), 0x398 => array(0x3B8), 0x399 => array(0x3B9)
  1074. ,0x39A => array(0x3BA), 0x39B => array(0x3BB), 0x39C => array(0x3BC)
  1075. ,0x39D => array(0x3BD), 0x39E => array(0x3BE), 0x39F => array(0x3BF)
  1076. ,0x3A0 => array(0x3C0), 0x3A1 => array(0x3C1), 0x3A3 => array(0x3C3)
  1077. ,0x3A4 => array(0x3C4), 0x3A5 => array(0x3C5), 0x3A6 => array(0x3C6)
  1078. ,0x3A7 => array(0x3C7), 0x3A8 => array(0x3C8), 0x3A9 => array(0x3C9)
  1079. ,0x3AA => array(0x3CA), 0x3AB => array(0x3CB), 0x3B0 => array(0x3C5, 0x308, 0x301)
  1080. ,0x3C2 => array(0x3C3), 0x3D0 => array(0x3B2), 0x3D1 => array(0x3B8)
  1081. ,0x3D2 => array(0x3C5), 0x3D3 => array(0x3CD), 0x3D4 => array(0x3CB)
  1082. ,0x3D5 => array(0x3C6), 0x3D6 => array(0x3C0), 0x3D8 => array(0x3D9)
  1083. ,0x3DA => array(0x3DB), 0x3DC => array(0x3DD), 0x3DE => array(0x3DF)
  1084. ,0x3E0 => array(0x3E1), 0x3E2 => array(0x3E3), 0x3E4 => array(0x3E5)
  1085. ,0x3E6 => array(0x3E7), 0x3E8 => array(0x3E9), 0x3EA => array(0x3EB)
  1086. ,0x3EC => array(0x3ED), 0x3EE => array(0x3EF), 0x3F0 => array(0x3BA)
  1087. ,0x3F1 => array(0x3C1), 0x3F2 => array(0x3C3), 0x3F4 => array(0x3B8)
  1088. ,0x3F5 => array(0x3B5), 0x400 => array(0x450), 0x401 => array(0x451)
  1089. ,0x402 => array(0x452), 0x403 => array(0x453), 0x404 => array(0x454)
  1090. ,0x405 => array(0x455), 0x406 => array(0x456), 0x407 => array(0x457)
  1091. ,0x408 => array(0x458), 0x409 => array(0x459), 0x40A => array(0x45A)
  1092. ,0x40B => array(0x45B), 0x40C => array(0x45C), 0x40D => array(0x45D)
  1093. ,0x40E => array(0x45E), 0x40F => array(0x45F), 0x410 => array(0x430)
  1094. ,0x411 => array(0x431), 0x412 => array(0x432), 0x413 => array(0x433)
  1095. ,0x414 => array(0x434), 0x415 => array(0x435), 0x416 => array(0x436)
  1096. ,0x417 => array(0x437), 0x418 => array(0x438), 0x419 => array(0x439)
  1097. ,0x41A => array(0x43A), 0x41B => array(0x43B), 0x41C => array(0x43C)
  1098. ,0x41D => array(0x43D), 0x41E => array(0x43E), 0x41F => array(0x43F)
  1099. ,0x420 => array(0x440), 0x421 => array(0x441), 0x422 => array(0x442)
  1100. ,0x423 => array(0x443), 0x424 => array(0x444), 0x425 => array(0x445)
  1101. ,0x426 => array(0x446), 0x427 => array(0x447), 0x428 => array(0x448)
  1102. ,0x429 => array(0x449), 0x42A => array(0x44A), 0x42B => array(0x44B)
  1103. ,0x42C => array(0x44C), 0x42D => array(0x44D), 0x42E => array(0x44E)
  1104. ,0x42F => array(0x44F), 0x460 => array(0x461), 0x462 => array(0x463)
  1105. ,0x464 => array(0x465), 0x466 => array(0x467), 0x468 => array(0x469)
  1106. ,0x46A => array(0x46B), 0x46C => array(0x46D), 0x46E => array(0x46F)
  1107. ,0x470 => array(0x471), 0x472 => array(0x473), 0x474 => array(0x475)
  1108. ,0x476 => array(0x477), 0x478 => array(0x479), 0x47A => array(0x47B)
  1109. ,0x47C => array(0x47D), 0x47E => array(0x47F), 0x480 => array(0x481)
  1110. ,0x48A => array(0x48B), 0x48C => array(0x48D), 0x48E => array(0x48F)
  1111. ,0x490 => array(0x491), 0x492 => array(0x493), 0x494 => array(0x495)
  1112. ,0x496 => array(0x497), 0x498 => array(0x499), 0x49A => array(0x49B)
  1113. ,0x49C => array(0x49D), 0x49E => array(0x49F), 0x4A0 => array(0x4A1)
  1114. ,0x4A2 => array(0x4A3), 0x4A4 => array(0x4A5), 0x4A6 => array(0x4A7)
  1115. ,0x4A8 => array(0x4A9), 0x4AA => array(0x4AB), 0x4AC => array(0x4AD)
  1116. ,0x4AE => array(0x4AF), 0x4B0 => array(0x4B1), 0x4B2 => array(0x4B3)
  1117. ,0x4B4 => array(0x4B5), 0x4B6 => array(0x4B7), 0x4B8 => array(0x4B9)
  1118. ,0x4BA => array(0x4BB), 0x4BC => array(0x4BD), 0x4BE => array(0x4BF)
  1119. ,0x4C1 => array(0x4C2), 0x4C3 => array(0x4C4), 0x4C5 => array(0x4C6)
  1120. ,0x4C7 => array(0x4C8), 0x4C9 => array(0x4CA), 0x4CB => array(0x4CC)
  1121. ,0x4CD => array(0x4CE), 0x4D0 => array(0x4D1), 0x4D2 => array(0x4D3)
  1122. ,0x4D4 => array(0x4D5), 0x4D6 => array(0x4D7), 0x4D8 => array(0x4D9)
  1123. ,0x4DA => array(0x4DB), 0x4DC => array(0x4DD), 0x4DE => array(0x4DF)
  1124. ,0x4E0 => array(0x4E1), 0x4E2 => array(0x4E3), 0x4E4 => array(0x4E5)
  1125. ,0x4E6 => array(0x4E7), 0x4E8 => array(0x4E9), 0x4EA => array(0x4EB)
  1126. ,0x4EC => array(0x4ED), 0x4EE => array(0x4EF), 0x4F0 => array(0x4F1)
  1127. ,0x4F2 => array(0x4F3), 0x4F4 => array(0x4F5), 0x4F8 => array(0x4F9)
  1128. ,0x500 => array(0x501), 0x502 => array(0x503), 0x504 => array(0x505)
  1129. ,0x506 => array(0x507), 0x508 => array(0x509), 0x50A => array(0x50B)
  1130. ,0x50C => array(0x50D), 0x50E => array(0x50F), 0x531 => array(0x561)
  1131. ,0x532 => array(0x562), 0x533 => array(0x563), 0x534 => array(0x564)
  1132. ,0x535 => array(0x565), 0x536 => array(0x566), 0x537 => array(0x567)
  1133. ,0x538 => array(0x568), 0x539 => array(0x569), 0x53A => array(0x56A)
  1134. ,0x53B => array(0x56B), 0x53C => array(0x56C), 0x53D => array(0x56D)
  1135. ,0x53E => array(0x56E), 0x53F => array(0x56F), 0x540 => array(0x570)
  1136. ,0x541 => array(0x571), 0x542 => array(0x572), 0x543 => array(0x573)
  1137. ,0x544 => array(0x574), 0x545 => array(0x575), 0x546 => array(0x576)
  1138. ,0x547 => array(0x577), 0x548 => array(0x578), 0x549 => array(0x579)
  1139. ,0x54A => array(0x57A), 0x54B => array(0x57B), 0x54C => array(0x57C)
  1140. ,0x54D => array(0x57D), 0x54E => array(0x57E), 0x54F => array(0x57F)
  1141. ,0x550 => array(0x580), 0x551 => array(0x581), 0x552 => array(0x582)
  1142. ,0x553 => array(0x583), 0x554 => array(0x584), 0x555 => array(0x585)
  1143. ,0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0xE33 => array(0xE4D, 0xE32)
  1144. ,0x1E00 => array(0x1E01), 0x1E02 => array(0x1E03), 0x1E04 => array(0x1E05)
  1145. ,0x1E06 => array(0x1E07), 0x1E08 => array(0x1E09), 0x1E0A => array(0x1E0B)
  1146. ,0x1E0C => array(0x1E0D), 0x1E0E => array(0x1E0F), 0x1E10 => array(0x1E11)
  1147. ,0x1E12 => array(0x1E13), 0x1E14 => array(0x1E15), 0x1E16 => array(0x1E17)
  1148. ,0x1E18 => array(0x1E19), 0x1E1A => array(0x1E1B), 0x1E1C => array(0x1E1D)
  1149. ,0x1E1E => array(0x1E1F), 0x1E20 => array(0x1E21), 0x1E22 => array(0x1E23)
  1150. ,0x1E24 => array(0x1E25), 0x1E26 => array(0x1E27), 0x1E28 => array(0x1E29)
  1151. ,0x1E2A => array(0x1E2B), 0x1E2C => array(0x1E2D), 0x1E2E => array(0x1E2F)
  1152. ,0x1E30 => array(0x1E31), 0x1E32 => array(0x1E33), 0x1E34 => array(0x1E35)
  1153. ,0x1E36 => array(0x1E37), 0x1E38 => array(0x1E39), 0x1E3A => array(0x1E3B)
  1154. ,0x1E3C => array(0x1E3D), 0x1E3E => array(0x1E3F), 0x1E40 => array(0x1E41)
  1155. ,0x1E42 => array(0x1E43), 0x1E44 => array(0x1E45), 0x1E46 => array(0x1E47)
  1156. ,0x1E48 => array(0x1E49), 0x1E4A => array(0x1E4B), 0x1E4C => array(0x1E4D)
  1157. ,0x1E4E => array(0x1E4F), 0x1E50 => array(0x1E51), 0x1E52 => array(0x1E53)
  1158. ,0x1E54 => array(0x1E55), 0x1E56 => array(0x1E57), 0x1E58 => array(0x1E59)
  1159. ,0x1E5A => array(0x1E5B), 0x1E5C => array(0x1E5D), 0x1E5E => array(0x1E5F)
  1160. ,0x1E60 => array(0x1E61), 0x1E62 => array(0x1E63), 0x1E64 => array(0x1E65)
  1161. ,0x1E66 => array(0x1E67), 0x1E68 => array(0x1E69), 0x1E6A => array(0x1E6B)
  1162. ,0x1E6C => array(0x1E6D), 0x1E6E => array(0x1E6F), 0x1E70 => array(0x1E71)
  1163. ,0x1E72 => array(0x1E73), 0x1E74 => array(0x1E75), 0x1E76 => array(0x1E77)
  1164. ,0x1E78 => array(0x1E79), 0x1E7A => array(0x1E7B), 0x1E7C => array(0x1E7D)
  1165. ,0x1E7E => array(0x1E7F), 0x1E80 => array(0x1E81), 0x1E82 => array(0x1E83)
  1166. ,0x1E84 => array(0x1E85), 0x1E86 => array(0x1E87), 0x1E88 => array(0x1E89)
  1167. ,0x1E8A => array(0x1E8B), 0x1E8C => array(0x1E8D), 0x1E8E => array(0x1E8F)
  1168. ,0x1E90 => array(0x1E91), 0x1E92 => array(0x1E93), 0x1E94 => array(0x1E95)
  1169. ,0x1E96 => array(0x68, 0x331), 0x1E97 => array(0x74, 0x308), 0x1E98 => array(0x77, 0x30A)
  1170. ,0x1E99 => array(0x79, 0x30A), 0x1E9A => array(0x61, 0x2BE), 0x1E9B => array(0x1E61)
  1171. ,0x1EA0 => array(0x1EA1), 0x1EA2 => array(0x1EA3), 0x1EA4 => array(0x1EA5)
  1172. ,0x1EA6 => array(0x1EA7), 0x1EA8 => array(0x1EA9), 0x1EAA => array(0x1EAB)
  1173. ,0x1EAC => array(0x1EAD), 0x1EAE => array(0x1EAF), 0x1EB0 => array(0x1EB1)
  1174. ,0x1EB2 => array(0x1EB3), 0x1EB4 => array(0x1EB5), 0x1EB6 => array(0x1EB7)
  1175. ,0x1EB8 => array(0x1EB9), 0x1EBA => array(0x1EBB), 0x1EBC => array(0x1EBD)
  1176. ,0x1EBE => array(0x1EBF), 0x1EC0 => array(0x1EC1), 0x1EC2 => array(0x1EC3)
  1177. ,0x1EC4 => array(0x1EC5), 0x1EC6 => array(0x1EC7), 0x1EC8 => array(0x1EC9)
  1178. ,0x1ECA => array(0x1ECB), 0x1ECC => array(0x1ECD), 0x1ECE => array(0x1ECF)
  1179. ,0x1ED0 => array(0x1ED1), 0x1ED2 => array(0x1ED3), 0x1ED4 => array(0x1ED5)
  1180. ,0x1ED6 => array(0x1ED7), 0x1ED8 => array(0x1ED9), 0x1EDA => array(0x1EDB)
  1181. ,0x1EDC => array(0x1EDD), 0x1EDE => array(0x1EDF), 0x1EE0 => array(0x1EE1)
  1182. ,0x1EE2 => array(0x1EE3), 0x1EE4 => array(0x1EE5), 0x1EE6 => array(0x1EE7)
  1183. ,0x1EE8 => array(0x1EE9), 0x1EEA => array(0x1EEB), 0x1EEC => array(0x1EED)
  1184. ,0x1EEE => array(0x1EEF), 0x1EF0 => array(0x1EF1), 0x1EF2 => array(0x1EF3)
  1185. ,0x1EF4 => array(0x1EF5), 0x1EF6 => array(0x1EF7), 0x1EF8 => array(0x1EF9)
  1186. ,0x1F08 => array(0x1F00), 0x1F09 => array(0x1F01), 0x1F0A => array(0x1F02)
  1187. ,0x1F0B => array(0x1F03), 0x1F0C => array(0x1F04), 0x1F0D => array(0x1F05)
  1188. ,0x1F0E => array(0x1F06), 0x1F0F => array(0x1F07), 0x1F18 => array(0x1F10)
  1189. ,0x1F19 => array(0x1F11), 0x1F1A => array(0x1F12), 0x1F1B => array(0x1F13)
  1190. ,0x1F1C => array(0x1F14), 0x1F1D => array(0x1F15), 0x1F28 => array(0x1F20)
  1191. ,0x1F29 => array(0x1F21), 0x1F2A => array(0x1F22), 0x1F2B => array(0x1F23)
  1192. ,0x1F2C => array(0x1F24), 0x1F2D => array(0x1F25), 0x1F2E => array(0x1F26)
  1193. ,0x1F2F => array(0x1F27), 0x1F38 => array(0x1F30), 0x1F39 => array(0x1F31)
  1194. ,0x1F3A => array(0x1F32), 0x1F3B => array(0x1F33), 0x1F3C => array(0x1F34)
  1195. ,0x1F3D => array(0x1F35), 0x1F3E => array(0x1F36), 0x1F3F => array(0x1F37)
  1196. ,0x1F48 => array(0x1F40), 0x1F49 => array(0x1F41), 0x1F4A => array(0x1F42)
  1197. ,0x1F4B => array(0x1F43), 0x1F4C => array(0x1F44), 0x1F4D => array(0x1F45)
  1198. ,0x1F50 => array(0x3C5, 0x313), 0x1F52 => array(0x3C5, 0x313, 0x300)
  1199. ,0x1F54 => array(0x3C5, 0x313, 0x301), 0x1F56 => array(0x3C5, 0x313, 0x342)
  1200. ,0x1F59 => array(0x1F51), 0x1F5B => array(0x1F53), 0x1F5D => array(0x1F55)
  1201. ,0x1F5F => array(0x1F57), 0x1F68 => array(0x1F60), 0x1F69 => array(0x1F61)
  1202. ,0x1F6A => array(0x1F62), 0x1F6B => array(0x1F63), 0x1F6C => array(0x1F64)
  1203. ,0x1F6D => array(0x1F65), 0x1F6E => array(0x1F66), 0x1F6F => array(0x1F67)
  1204. ,0x1F80 => array(0x1F00, 0x3B9), 0x1F81 => array(0x1F01, 0x3B9)
  1205. ,0x1F82 => array(0x1F02, 0x3B9), 0x1F83 => array(0x1F03, 0x3B9)
  1206. ,0x1F84 => array(0x1F04, 0x3B9), 0x1F85 => array(0x1F05, 0x3B9)
  1207. ,0x1F86 => array(0x1F06, 0x3B9), 0x1F87 => array(0x1F07, 0x3B9)
  1208. ,0x1F88 => array(0x1F00, 0x3B9), 0x1F89 => array(0x1F01, 0x3B9)
  1209. ,0x1F8A => array(0x1F02, 0x3B9), 0x1F8B => array(0x1F03, 0x3B9)
  1210. ,0x1F8C => array(0x1F04, 0x3B9), 0x1F8D => array(0x1F05, 0x3B9)
  1211. ,0x1F8E => array(0x1F06, 0x3B9), 0x1F8F => array(0x1F07, 0x3B9)
  1212. ,0x1F90 => array(0x1F20, 0x3B9), 0x1F91 => array(0x1F21, 0x3B9)
  1213. ,0x1F92 => array(0x1F22, 0x3B9), 0x1F93 => array(0x1F23, 0x3B9)
  1214. ,0x1F94 => array(0x1F24, 0x3B9), 0x1F95 => array(0x1F25, 0x3B9)
  1215. ,0x1F96 => array(0x1F26, 0x3B9), 0x1F97 => array(0x1F27, 0x3B9)
  1216. ,0x1F98 => array(0x1F20, 0x3B9), 0x1F99 => array(0x1F21, 0x3B9)
  1217. ,0x1F9A => array(0x1F22, 0x3B9), 0x1F9B => array(0x1F23, 0x3B9)
  1218. ,0x1F9C => array(0x1F24, 0x3B9), 0x1F9D => array(0x1F25, 0x3B9)
  1219. ,0x1F9E => array(0x1F26, 0x3B9), 0x1F9F => array(0x1F27, 0x3B9)
  1220. ,0x1FA0 => array(0x1F60, 0x3B9), 0x1FA1 => array(0x1F61, 0x3B9)
  1221. ,0x1FA2 => array(0x1F62, 0x3B9), 0x1FA3 => array(0x1F63, 0x3B9)
  1222. ,0x1FA4 => array(0x1F64, 0x3B9), 0x1FA5 => array(0x1F65, 0x3B9)
  1223. ,0x1FA6 => array(0x1F66, 0x3B9), 0x1FA7 => array(0x1F67, 0x3B9)
  1224. ,0x1FA8 => array(0x1F60, 0x3B9), 0x1FA9 => array(0x1F61, 0x3B9)
  1225. ,0x1FAA => array(0x1F62, 0x3B9), 0x1FAB => array(0x1F63, 0x3B9)
  1226. ,0x1FAC => array(0x1F64, 0x3B9), 0x1FAD => array(0x1F65, 0x3B9)
  1227. ,0x1FAE => array(0x1F66, 0x3B9), 0x1FAF => array(0x1F67, 0x3B9)
  1228. ,0x1FB2 => array(0x1F70, 0x3B9), 0x1FB3 => array(0x3B1, 0x3B9)
  1229. ,0x1FB4 => array(0x3AC, 0x3B9), 0x1FB6 => array(0x3B1, 0x342)
  1230. ,0x1FB7 => array(0x3B1, 0x342, 0x3B9), 0x1FB8 => array(0x1FB0)
  1231. ,0x1FB9 => array(0x1FB1), 0x1FBA => array(0x1F70), 0x1FBB => array(0x1F71)
  1232. ,0x1FBC => array(0x3B1, 0x3B9), 0x1FBE => array(0x3B9)
  1233. ,0x1FC2 => array(0x1F74, 0x3B9), 0x1FC3 => array(0x3B7, 0x3B9)
  1234. ,0x1FC4 => array(0x3AE, 0x3B9), 0x1FC6 => array(0x3B7, 0x342)
  1235. ,0x1FC7 => array(0x3B7, 0x342, 0x3B9), 0x1FC8 => array(0x1F72)
  1236. ,0x1FC9 => array(0x1F73), 0x1FCA => array(0x1F74), 0x1FCB => array(0x1F75)
  1237. ,0x1FCC => array(0x3B7, 0x3B9), 0x1FD2 => array(0x3B9, 0x308, 0x300)
  1238. ,0x1FD3 => array(0x3B9, 0x308, 0x301), 0x1FD6 => array(0x3B9, 0x342)
  1239. ,0x1FD7 => array(0x3B9, 0x308, 0x342), 0x1FD8 => array(0x1FD0)
  1240. ,0x1FD9 => array(0x1FD1), 0x1FDA => array(0x1F76)
  1241. ,0x1FDB => array(0x1F77), 0x1FE2 => array(0x3C5, 0x308, 0x300)
  1242. ,0x1FE3 => array(0x3C5, 0x308, 0x301), 0x1FE4 => array(0x3C1, 0x313)
  1243. ,0x1FE6 => array(0x3C5, 0x342), 0x1FE7 => array(0x3C5, 0x308, 0x342)
  1244. ,0x1FE8 => array(0x1FE0), 0x1FE9 => array(0x1FE1)
  1245. ,0x1FEA => array(0x1F7A), 0x1FEB => array(0x1F7B)
  1246. ,0x1FEC => array(0x1FE5), 0x1FF2 => array(0x1F7C, 0x3B9)
  1247. ,0x1FF3 => array(0x3C9, 0x3B9), 0x1FF4 => array(0x3CE, 0x3B9)
  1248. ,0x1FF6 => array(0x3C9, 0x342), 0x1FF7 => array(0x3C9, 0x342, 0x3B9)
  1249. ,0x1FF8 => array(0x1F78), 0x1FF9 => array(0x1F79), 0x1FFA => array(0x1F7C)
  1250. ,0x1FFB => array(0x1F7D), 0x1FFC => array(0x3C9, 0x3B9)
  1251. ,0x20A8 => array(0x72, 0x73), 0x2102 => array(0x63), 0x2103 => array(0xB0, 0x63)
  1252. ,0x2107 => array(0x25B), 0x2109 => array(0xB0, 0x66), 0x210B => array(0x68)
  1253. ,0x210C => array(0x68), 0x210D => array(0x68), 0x2110 => array(0x69)
  1254. ,0x2111 => array(0x69), 0x2112 => array(0x6C), 0x2115 => array(0x6E)
  1255. ,0x2116 => array(0x6E, 0x6F), 0x2119 => array(0x70), 0x211A => array(0x71)
  1256. ,0x211B => array(0x72), 0x211C => array(0x72), 0x211D => array(0x72)
  1257. ,0x2120 => array(0x73, 0x6D), 0x2121 => array(0x74, 0x65, 0x6C)
  1258. ,0x2122 => array(0x74, 0x6D), 0x2124 => array(0x7A), 0x2126 => array(0x3C9)
  1259. ,0x2128 => array(0x7A), 0x212A => array(0x6B), 0x212B => array(0xE5)
  1260. ,0x212C => array(0x62), 0x212D => array(0x63), 0x2130 => array(0x65)
  1261. ,0x2131 => array(0x66), 0x2133 => array(0x6D), 0x213E => array(0x3B3)
  1262. ,0x213F => array(0x3C0), 0x2145 => array(0x64) ,0x2160 => array(0x2170)
  1263. ,0x2161 => array(0x2171), 0x2162 => array(0x2172), 0x2163 => array(0x2173)
  1264. ,0x2164 => array(0x2174), 0x2165 => array(0x2175), 0x2166 => array(0x2176)
  1265. ,0x2167 => array(0x2177), 0x2168 => array(0x2178), 0x2169 => array(0x2179)
  1266. ,0x216A => array(0x217A), 0x216B => array(0x217B), 0x216C => array(0x217C)
  1267. ,0x216D => array(0x217D), 0x216E => array(0x217E), 0x216F => array(0x217F)
  1268. ,0x24B6 => array(0x24D0), 0x24B7 => array(0x24D1), 0x24B8 => array(0x24D2)
  1269. ,0x24B9 => array(0x24D3), 0x24BA => array(0x24D4), 0x24BB => array(0x24D5)
  1270. ,0x24BC => array(0x24D6), 0x24BD => array(0x24D7), 0x24BE => array(0x24D8)
  1271. ,0x24BF => array(0x24D9), 0x24C0 => array(0x24DA), 0x24C1 => array(0x24DB)
  1272. ,0x24C2 => array(0x24DC), 0x24C3 => array(0x24DD), 0x24C4 => array(0x24DE)
  1273. ,0x24C5 => array(0x24DF), 0x24C6 => array(0x24E0), 0x24C7 => array(0x24E1)
  1274. ,0x24C8 => array(0x24E2), 0x24C9 => array(0x24E3), 0x24CA => array(0x24E4)
  1275. ,0x24CB => array(0x24E5), 0x24CC => array(0x24E6), 0x24CD => array(0x24E7)
  1276. ,0x24CE => array(0x24E8), 0x24CF => array(0x24E9), 0x3371 => array(0x68, 0x70, 0x61)
  1277. ,0x3373 => array(0x61, 0x75), 0x3375 => array(0x6F, 0x76)
  1278. ,0x3380 => array(0x70, 0x61), 0x3381 => array(0x6E, 0x61)
  1279. ,0x3382 => array(0x3BC, 0x61), 0x3383 => array(0x6D, 0x61)
  1280. ,0x3384 => array(0x6B, 0x61), 0x3385 => array(0x6B, 0x62)
  1281. ,0x3386 => array(0x6D, 0x62), 0x3387 => array(0x67, 0x62)
  1282. ,0x338A => array(0x70, 0x66), 0x338B => array(0x6E, 0x66)
  1283. ,0x338C => array(0x3BC, 0x66), 0x3390 => array(0x68, 0x7A)
  1284. ,0x3391 => array(0x6B, 0x68, 0x7A), 0x3392 => array(0x6D, 0x68, 0x7A)
  1285. ,0x3393 => array(0x67, 0x68, 0x7A), 0x3394 => array(0x74, 0x68, 0x7A)
  1286. ,0x33A9 => array(0x70, 0x61), 0x33AA => array(0x6B, 0x70, 0x61)
  1287. ,0x33AB => array(0x6D, 0x70, 0x61), 0x33AC => array(0x67, 0x70, 0x61)
  1288. ,0x33B4 => array(0x70, 0x76), 0x33B5 => array(0x6E, 0x76)
  1289. ,0x33B6 => array(0x3BC, 0x76), 0x33B7 => array(0x6D, 0x76)
  1290. ,0x33B8 => array(0x6B, 0x76), 0x33B9 => array(0x6D, 0x76)
  1291. ,0x33BA => array(0x70, 0x77), 0x33BB => array(0x6E, 0x77)
  1292. ,0x33BC => array(0x3BC, 0x77), 0x33BD => array(0x6D, 0x77)
  1293. ,0x33BE => array(0x6B, 0x77), 0x33BF => array(0x6D, 0x77)
  1294. ,0x33C0 => array(0x6B, 0x3C9), 0x33C1 => array(0x6D, 0x3C9) /*
  1295. ,0x33C2 => array(0x61, 0x2E, 0x6D, 0x2E) */
  1296. ,0x33C3 => array(0x62, 0x71), 0x33C6 => array(0x63, 0x2215, 0x6B, 0x67)
  1297. ,0x33C7 => array(0x63, 0x6F, 0x2E), 0x33C8 => array(0x64, 0x62)
  1298. ,0x33C9 => array(0x67, 0x79), 0x33CB => array(0x68, 0x70)
  1299. ,0x33CD => array(0x6B, 0x6B), 0x33CE => array(0x6B, 0x6D)
  1300. ,0x33D7 => array(0x70, 0x68), 0x33D9 => array(0x70, 0x70, 0x6D)
  1301. ,0x33DA => array(0x70, 0x72), 0x33DC => array(0x73, 0x76)
  1302. ,0x33DD => array(0x77, 0x62), 0xFB00 => array(0x66, 0x66)
  1303. ,0xFB01 => array(0x66, 0x69), 0xFB02 => array(0x66, 0x6C)
  1304. ,0xFB03 => array(0x66, 0x66, 0x69), 0xFB04 => array(0x66, 0x66, 0x6C)
  1305. ,0xFB05 => array(0x73, 0x74), 0xFB06 => array(0x73, 0x74)
  1306. ,0xFB13 => array(0x574, 0x576), 0xFB14 => array(0x574, 0x565)
  1307. ,0xFB15 => array(0x574, 0x56B), 0xFB16 => array(0x57E, 0x576)
  1308. ,0xFB17 => array(0x574, 0x56D), 0xFF21 => array(0xFF41)
  1309. ,0xFF22 => array(0xFF42), 0xFF23 => array(0xFF43), 0xFF24 => array(0xFF44)
  1310. ,0xFF25 => array(0xFF45), 0xFF26 => array(0xFF46), 0xFF27 => array(0xFF47)
  1311. ,0xFF28 => array(0xFF48), 0xFF29 => array(0xFF49), 0xFF2A => array(0xFF4A)
  1312. ,0xFF2B => array(0xFF4B), 0xFF2C => array(0xFF4C), 0xFF2D => array(0xFF4D)
  1313. ,0xFF2E => array(0xFF4E), 0xFF2F => array(0xFF4F), 0xFF30 => array(0xFF50)
  1314. ,0xFF31 => array(0xFF51), 0xFF32 => array(0xFF52), 0xFF33 => array(0xFF53)
  1315. ,0xFF34 => array(0xFF54), 0xFF35 => array(0xFF55), 0xFF36 => array(0xFF56)
  1316. ,0xFF37 => array(0xFF57), 0xFF38 => array(0xFF58), 0xFF39 => array(0xFF59)
  1317. ,0xFF3A => array(0xFF5A), 0x10400 => array(0x10428), 0x10401 => array(0x10429)
  1318. ,0x10402 => array(0x1042A), 0x10403 => array(0x1042B), 0x10404 => array(0x1042C)
  1319. ,0x10405 => array(0x1042D), 0x10406 => array(0x1042E), 0x10407 => array(0x1042F)
  1320. ,0x10408 => array(0x10430), 0x10409 => array(0x10431), 0x1040A => array(0x10432)
  1321. ,0x1040B => array(0x10433), 0x1040C => array(0x10434), 0x1040D => array(0x10435)
  1322. ,0x1040E => array(0x10436), 0x1040F => array(0x10437), 0x10410 => array(0x10438)
  1323. ,0x10411 => array(0x10439), 0x10412 => array(0x1043A), 0x10413 => array(0x1043B)
  1324. ,0x10414 => array(0x1043C), 0x10415 => array(0x1043D), 0x10416 => array(0x1043E)
  1325. ,0x10417 => array(0x1043F), 0x10418 => array(0x10440), 0x10419 => array(0x10441)
  1326. ,0x1041A => array(0x10442), 0x1041B => array(0x10443), 0x1041C => array(0x10444)
  1327. ,0x1041D => array(0x10445), 0x1041E => array(0x10446), 0x1041F => array(0x10447)
  1328. ,0x10420 => array(0x10448), 0x10421 => array(0x10449), 0x10422 => array(0x1044A)
  1329. ,0x10423 => array(0x1044B), 0x10424 => array(0x1044C), 0x10425 => array(0x1044D)
  1330. ,0x1D400 => array(0x61), 0x1D401 => array(0x62), 0x1D402 => array(0x63)
  1331. ,0x1D403 => array(0x64), 0x1D404 => array(0x65), 0x1D405 => array(0x66)
  1332. ,0x1D406 => array(0x67), 0x1D407 => array(0x68), 0x1D408 => array(0x69)
  1333. ,0x1D409 => array(0x6A), 0x1D40A => array(0x6B), 0x1D40B => array(0x6C)
  1334. ,0x1D40C => array(0x6D), 0x1D40D => array(0x6E), 0x1D40E => array(0x6F)
  1335. ,0x1D40F => array(0x70), 0x1D410 => array(0x71), 0x1D411 => array(0x72)
  1336. ,0x1D412 => array(0x73), 0x1D413 => array(0x74), 0x1D414 => array(0x75)
  1337. ,0x1D415 => array(0x76), 0x1D416 => array(0x77), 0x1D417 => array(0x78)
  1338. ,0x1D418 => array(0x79), 0x1D419 => array(0x7A), 0x1D434 => array(0x61)
  1339. ,0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64)
  1340. ,0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67)
  1341. ,0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A)
  1342. ,0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D)
  1343. ,0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70)
  1344. ,0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73)
  1345. ,0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76)
  1346. ,0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79)
  1347. ,0x1D44D => array(0x7A), 0x1D468 => array(0x61), 0x1D469 => array(0x62)
  1348. ,0x1D46A => array(0x63), 0x1D46B => array(0x64), 0x1D46C => array(0x65)
  1349. ,0x1D46D => array(0x66), 0x1D46E => array(0x67), 0x1D46F => array(0x68)
  1350. ,0x1D470 => array(0x69), 0x1D471 => array(0x6A), 0x1D472 => array(0x6B)
  1351. ,0x1D473 => array(0x6C), 0x1D474 => array(0x6D), 0x1D475 => array(0x6E)
  1352. ,0x1D476 => array(0x6F), 0x1D477 => array(0x70), 0x1D478 => array(0x71)
  1353. ,0x1D479 => array(0x72), 0x1D47A => array(0x73), 0x1D47B => array(0x74)
  1354. ,0x1D47C => array(0x75), 0x1D47D => array(0x76), 0x1D47E => array(0x77)
  1355. ,0x1D47F => array(0x78), 0x1D480 => array(0x79), 0x1D481 => array(0x7A)
  1356. ,0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64)
  1357. ,0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B)
  1358. ,0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70)
  1359. ,0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74)
  1360. ,0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77)
  1361. ,0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A)
  1362. ,0x1D4D0 => array(0x61), 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63)
  1363. ,0x1D4D3 => array(0x64), 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66)
  1364. ,0x1D4D6 => array(0x67), 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69)
  1365. ,0x1D4D9 => array(0x6A), 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C)
  1366. ,0x1D4DC => array(0x6D), 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F)
  1367. ,0x1D4DF => array(0x70), 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72)
  1368. ,0x1D4E2 => array(0x73), 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75)
  1369. ,0x1D4E5 => array(0x76), 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78)
  1370. ,0x1D4E8 => array(0x79), 0x1D4E9 => array(0x7A), 0x1D504 => array(0x61)
  1371. ,0x1D505 => array(0x62), 0x1D507 => array(0x64), 0x1D508 => array(0x65)
  1372. ,0x1D509 => array(0x66), 0x1D50A => array(0x67), 0x1D50D => array(0x6A)
  1373. ,0x1D50E => array(0x6B), 0x1D50F => array(0x6C), 0x1D510 => array(0x6D)
  1374. ,0x1D511 => array(0x6E), 0x1D512 => array(0x6F), 0x1D513 => array(0x70)
  1375. ,0x1D514 => array(0x71), 0x1D516 => array(0x73), 0x1D517 => array(0x74)
  1376. ,0x1D518 => array(0x75), 0x1D519 => array(0x76), 0x1D51A => array(0x77)
  1377. ,0x1D51B => array(0x78), 0x1D51C => array(0x79), 0x1D538 => array(0x61)
  1378. ,0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65)
  1379. ,0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69)
  1380. ,0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C)
  1381. ,0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73)
  1382. ,0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76)
  1383. ,0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79)
  1384. ,0x1D56C => array(0x61), 0x1D56D => array(0x62), 0x1D56E => array(0x63)
  1385. ,0x1D56F => array(0x64), 0x1D570 => array(0x65), 0x1D571 => array(0x66)
  1386. ,0x1D572 => array(0x67), 0x1D573 => array(0x68), 0x1D574 => array(0x69)
  1387. ,0x1D575 => array(0x6A), 0x1D576 => array(0x6B), 0x1D577 => array(0x6C)
  1388. ,0x1D578 => array(0x6D), 0x1D579 => array(0x6E), 0x1D57A => array(0x6F)
  1389. ,0x1D57B => array(0x70), 0x1D57C => array(0x71), 0x1D57D => array(0x72)
  1390. ,0x1D57E => array(0x73), 0x1D57F => array(0x74), 0x1D580 => array(0x75)
  1391. ,0x1D581 => array(0x76), 0x1D582 => array(0x77), 0x1D583 => array(0x78)
  1392. ,0x1D584 => array(0x79), 0x1D585 => array(0x7A), 0x1D5A0 => array(0x61)
  1393. ,0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), 0x1D5A3 => array(0x64)
  1394. ,0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), 0x1D5A6 => array(0x67)
  1395. ,0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), 0x1D5A9 => array(0x6A)
  1396. ,0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), 0x1D5AC => array(0x6D)
  1397. ,0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), 0x1D5AF => array(0x70)
  1398. ,0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), 0x1D5B2 => array(0x73)
  1399. ,0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), 0x1D5B5 => array(0x76)
  1400. ,0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), 0x1D5B8 => array(0x79)
  1401. ,0x1D5B9 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62)
  1402. ,0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65)
  1403. ,0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68)
  1404. ,0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B)
  1405. ,0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E)
  1406. ,0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71)
  1407. ,0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74)
  1408. ,0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77)
  1409. ,0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A)
  1410. ,0x1D608 => array(0x61), 0x1D609 => array(0x62) ,0x1D60A => array(0x63)
  1411. ,0x1D60B => array(0x64), 0x1D60C => array(0x65), 0x1D60D => array(0x66)
  1412. ,0x1D60E => array(0x67), 0x1D60F => array(0x68), 0x1D610 => array(0x69)
  1413. ,0x1D611 => array(0x6A), 0x1D612 => array(0x6B), 0x1D613 => array(0x6C)
  1414. ,0x1D614 => array(0x6D), 0x1D615 => array(0x6E), 0x1D616 => array(0x6F)
  1415. ,0x1D617 => array(0x70), 0x1D618 => array(0x71), 0x1D619 => array(0x72)
  1416. ,0x1D61A => array(0x73), 0x1D61B => array(0x74), 0x1D61C => array(0x75)
  1417. ,0x1D61D => array(0x76), 0x1D61E => array(0x77), 0x1D61F => array(0x78)
  1418. ,0x1D620 => array(0x79), 0x1D621 => array(0x7A), 0x1D63C => array(0x61)
  1419. ,0x1D63D => array(0x62), 0x1D63E => array(0x63), 0x1D63F => array(0x64)
  1420. ,0x1D640 => array(0x65), 0x1D641 => array(0x66), 0x1D642 => array(0x67)
  1421. ,0x1D643 => array(0x68), 0x1D644 => array(0x69), 0x1D645 => array(0x6A)
  1422. ,0x1D646 => array(0x6B), 0x1D647 => array(0x6C), 0x1D648 => array(0x6D)
  1423. ,0x1D649 => array(0x6E), 0x1D64A => array(0x6F), 0x1D64B => array(0x70)
  1424. ,0x1D64C => array(0x71), 0x1D64D => array(0x72), 0x1D64E => array(0x73)
  1425. ,0x1D64F => array(0x74), 0x1D650 => array(0x75), 0x1D651 => array(0x76)
  1426. ,0x1D652 => array(0x77), 0x1D653 => array(0x78), 0x1D654 => array(0x79)
  1427. ,0x1D655 => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62)
  1428. ,0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65)
  1429. ,0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68)
  1430. ,0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B)
  1431. ,0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E)
  1432. ,0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71)
  1433. ,0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74)
  1434. ,0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77)
  1435. ,0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A)
  1436. ,0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), 0x1D6AA => array(0x3B3)
  1437. ,0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), 0x1D6AD => array(0x3B6)
  1438. ,0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), 0x1D6B0 => array(0x3B9)
  1439. ,0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), 0x1D6B3 => array(0x3BC)
  1440. ,0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), 0x1D6B6 => array(0x3BF)
  1441. ,0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), 0x1D6B9 => array(0x3B8)
  1442. ,0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), 0x1D6BC => array(0x3C5)
  1443. ,0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), 0x1D6BF => array(0x3C8)
  1444. ,0x1D6C0 => array(0x3C9), 0x1D6D3 => array(0x3C3), 0x1D6E2 => array(0x3B1)
  1445. ,0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4)
  1446. ,0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7)
  1447. ,0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA)
  1448. ,0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD)
  1449. ,0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0)
  1450. ,0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8) ,0x1D6F4 => array(0x3C3)
  1451. ,0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6)
  1452. ,0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8) ,0x1D6FA => array(0x3C9)
  1453. ,0x1D70D => array(0x3C3), 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2)
  1454. ,0x1D71E => array(0x3B3), 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5)
  1455. ,0x1D721 => array(0x3B6), 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8)
  1456. ,0x1D724 => array(0x3B9), 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB)
  1457. ,0x1D727 => array(0x3BC), 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE)
  1458. ,0x1D72A => array(0x3BF), 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1)
  1459. ,0x1D72D => array(0x3B8), 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4)
  1460. ,0x1D730 => array(0x3C5), 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7)
  1461. ,0x1D733 => array(0x3C8), 0x1D734 => array(0x3C9), 0x1D747 => array(0x3C3)
  1462. ,0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), 0x1D758 => array(0x3B3)
  1463. ,0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), 0x1D75B => array(0x3B6)
  1464. ,0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), 0x1D75E => array(0x3B9)
  1465. ,0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), 0x1D761 => array(0x3BC)
  1466. ,0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), 0x1D764 => array(0x3BF)
  1467. ,0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), 0x1D767 => array(0x3B8)
  1468. ,0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), 0x1D76A => array(0x3C5)
  1469. ,0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), 0x1D76D => array(0x3C8)
  1470. ,0x1D76E => array(0x3C9), 0x1D781 => array(0x3C3), 0x1D790 => array(0x3B1)
  1471. ,0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4)
  1472. ,0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7)
  1473. ,0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA)
  1474. ,0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD)
  1475. ,0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0)
  1476. ,0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3)
  1477. ,0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6)
  1478. ,0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9)
  1479. ,0x1D7BB => array(0x3C3), 0x3F9 => array(0x3C3), 0x1D2C => array(0x61)
  1480. ,0x1D2D => array(0xE6), 0x1D2E => array(0x62), 0x1D30 => array(0x64)
  1481. ,0x1D31 => array(0x65), 0x1D32 => array(0x1DD), 0x1D33 => array(0x67)
  1482. ,0x1D34 => array(0x68), 0x1D35 => array(0x69), 0x1D36 => array(0x6A)
  1483. ,0x1D37 => array(0x6B), 0x1D38 => array(0x6C), 0x1D39 => array(0x6D)
  1484. ,0x1D3A => array(0x6E), 0x1D3C => array(0x6F), 0x1D3D => array(0x223)
  1485. ,0x1D3E => array(0x70), 0x1D3F => array(0x72), 0x1D40 => array(0x74)
  1486. ,0x1D41 => array(0x75), 0x1D42 => array(0x77), 0x213B => array(0x66, 0x61, 0x78)
  1487. ,0x3250 => array(0x70, 0x74, 0x65), 0x32CC => array(0x68, 0x67)
  1488. ,0x32CE => array(0x65, 0x76), 0x32CF => array(0x6C, 0x74, 0x64)
  1489. ,0x337A => array(0x69, 0x75), 0x33DE => array(0x76, 0x2215, 0x6D)
  1490. ,0x33DF => array(0x61, 0x2215, 0x6D)
  1491. )
  1492. ,'norm_combcls' => array(0x334 => 1, 0x335 => 1, 0x336 => 1, 0x337 => 1
  1493. ,0x338 => 1, 0x93C => 7, 0x9BC => 7, 0xA3C => 7, 0xABC => 7
  1494. ,0xB3C => 7, 0xCBC => 7, 0x1037 => 7, 0x3099 => 8, 0x309A => 8
  1495. ,0x94D => 9, 0x9CD => 9, 0xA4D => 9, 0xACD => 9, 0xB4D => 9
  1496. ,0xBCD => 9, 0xC4D => 9, 0xCCD => 9, 0xD4D => 9, 0xDCA => 9
  1497. ,0xE3A => 9, 0xF84 => 9, 0x1039 => 9, 0x1714 => 9, 0x1734 => 9
  1498. ,0x17D2 => 9, 0x5B0 => 10, 0x5B1 => 11, 0x5B2 => 12, 0x5B3 => 13
  1499. ,0x5B4 => 14, 0x5B5 => 15, 0x5B6 => 16, 0x5B7 => 17, 0x5B8 => 18
  1500. ,0x5B9 => 19, 0x5BB => 20, 0x5Bc => 21, 0x5BD => 22, 0x5BF => 23
  1501. ,0x5C1 => 24, 0x5C2 => 25, 0xFB1E => 26, 0x64B => 27, 0x64C => 28
  1502. ,0x64D => 29, 0x64E => 30, 0x64F => 31, 0x650 => 32, 0x651 => 33
  1503. ,0x652 => 34, 0x670 => 35, 0x711 => 36, 0xC55 => 84, 0xC56 => 91
  1504. ,0xE38 => 103, 0xE39 => 103, 0xE48 => 107, 0xE49 => 107, 0xE4A => 107
  1505. ,0xE4B => 107, 0xEB8 => 118, 0xEB9 => 118, 0xEC8 => 122, 0xEC9 => 122
  1506. ,0xECA => 122, 0xECB => 122, 0xF71 => 129, 0xF72 => 130, 0xF7A => 130
  1507. ,0xF7B => 130, 0xF7C => 130, 0xF7D => 130, 0xF80 => 130, 0xF74 => 132
  1508. ,0x321 => 202, 0x322 => 202, 0x327 => 202, 0x328 => 202, 0x31B => 216
  1509. ,0xF39 => 216, 0x1D165 => 216, 0x1D166 => 216, 0x1D16E => 216, 0x1D16F => 216
  1510. ,0x1D170 => 216, 0x1D171 => 216, 0x1D172 => 216, 0x302A => 218, 0x316 => 220
  1511. ,0x317 => 220, 0x318 => 220, 0x319 => 220, 0x31C => 220, 0x31D => 220
  1512. ,0x31E => 220, 0x31F => 220, 0x320 => 220, 0x323 => 220, 0x324 => 220
  1513. ,0x325 => 220, 0x326 => 220, 0x329 => 220, 0x32A => 220, 0x32B => 220
  1514. ,0x32C => 220, 0x32D => 220, 0x32E => 220, 0x32F => 220, 0x330 => 220
  1515. ,0x331 => 220, 0x332 => 220, 0x333 => 220, 0x339 => 220, 0x33A => 220
  1516. ,0x33B => 220, 0x33C => 220, 0x347 => 220, 0x348 => 220, 0x349 => 220
  1517. ,0x34D => 220, 0x34E => 220, 0x353 => 220, 0x354 => 220, 0x355 => 220
  1518. ,0x356 => 220, 0x591 => 220, 0x596 => 220, 0x59B => 220, 0x5A3 => 220
  1519. ,0x5A4 => 220, 0x5A5 => 220, 0x5A6 => 220, 0x5A7 => 220, 0x5AA => 220
  1520. ,0x655 => 220, 0x656 => 220, 0x6E3 => 220, 0x6EA => 220, 0x6ED => 220
  1521. ,0x731 => 220, 0x734 => 220, 0x737 => 220, 0x738 => 220, 0x739 => 220
  1522. ,0x73B => 220, 0x73C => 220, 0x73E => 220, 0x742 => 220, 0x744 => 220
  1523. ,0x746 => 220, 0x748 => 220, 0x952 => 220, 0xF18 => 220, 0xF19 => 220
  1524. ,0xF35 => 220, 0xF37 => 220, 0xFC6 => 220, 0x193B => 220, 0x20E8 => 220
  1525. ,0x1D17B => 220, 0x1D17C => 220, 0x1D17D => 220, 0x1D17E => 220, 0x1D17F => 220
  1526. ,0x1D180 => 220, 0x1D181 => 220, 0x1D182 => 220, 0x1D18A => 220, 0x1D18B => 220
  1527. ,0x59A => 222, 0x5AD => 222, 0x1929 => 222, 0x302D => 222, 0x302E => 224
  1528. ,0x302F => 224, 0x1D16D => 226, 0x5AE => 228, 0x18A9 => 228, 0x302B => 228
  1529. ,0x300 => 230, 0x301 => 230, 0x302 => 230, 0x303 => 230, 0x304 => 230
  1530. ,0x305 => 230, 0x306 => 230, 0x307 => 230, 0x308 => 230, 0x309 => 230
  1531. ,0x30A => 230, 0x30B => 230, 0x30C => 230, 0x30D => 230, 0x30E => 230
  1532. ,0x30F => 230, 0x310 => 230, 0x311 => 230, 0x312 => 230, 0x313 => 230
  1533. ,0x314 => 230, 0x33D => 230, 0x33E => 230, 0x33F => 230, 0x340 => 230
  1534. ,0x341 => 230, 0x342 => 230, 0x343 => 230, 0x344 => 230, 0x346 => 230
  1535. ,0x34A => 230, 0x34B => 230, 0x34C => 230, 0x350 => 230, 0x351 => 230
  1536. ,0x352 => 230, 0x357 => 230, 0x363 => 230, 0x364 => 230, 0x365 => 230
  1537. ,0x366 => 230, 0x367 => 230, 0x368 => 230, 0x369 => 230, 0x36A => 230
  1538. ,0x36B => 230, 0x36C => 230, 0x36D => 230, 0x36E => 230, 0x36F => 230
  1539. ,0x483 => 230, 0x484 => 230, 0x485 => 230, 0x486 => 230, 0x592 => 230
  1540. ,0x593 => 230, 0x594 => 230, 0x595 => 230, 0x597 => 230, 0x598 => 230
  1541. ,0x599 => 230, 0x59C => 230, 0x59D => 230, 0x59E => 230, 0x59F => 230
  1542. ,0x5A0 => 230, 0x5A1 => 230, 0x5A8 => 230, 0x5A9 => 230, 0x5AB => 230
  1543. ,0x5AC => 230, 0x5AF => 230, 0x5C4 => 230, 0x610 => 230, 0x611 => 230
  1544. ,0x612 => 230, 0x613 => 230, 0x614 => 230, 0x615 => 230, 0x653 => 230
  1545. ,0x654 => 230, 0x657 => 230, 0x658 => 230, 0x6D6 => 230, 0x6D7 => 230
  1546. ,0x6D8 => 230, 0x6D9 => 230, 0x6DA => 230, 0x6DB => 230, 0x6DC => 230
  1547. ,0x6DF => 230, 0x6E0 => 230, 0x6E1 => 230, 0x6E2 => 230, 0x6E4 => 230
  1548. ,0x6E7 => 230, 0x6E8 => 230, 0x6EB => 230, 0x6EC => 230, 0x730 => 230
  1549. ,0x732 => 230, 0x733 => 230, 0x735 => 230, 0x736 => 230, 0x73A => 230
  1550. ,0x73D => 230, 0x73F => 230, 0x740 => 230, 0x741 => 230, 0x743 => 230
  1551. ,0x745 => 230, 0x747 => 230, 0x749 => 230, 0x74A => 230, 0x951 => 230
  1552. ,0x953 => 230, 0x954 => 230, 0xF82 => 230, 0xF83 => 230, 0xF86 => 230
  1553. ,0xF87 => 230, 0x170D => 230, 0x193A => 230, 0x20D0 => 230, 0x20D1 => 230
  1554. ,0x20D4 => 230, 0x20D5 => 230, 0x20D6 => 230, 0x20D7 => 230, 0x20DB => 230
  1555. ,0x20DC => 230, 0x20E1 => 230, 0x20E7 => 230, 0x20E9 => 230, 0xFE20 => 230
  1556. ,0xFE21 => 230, 0xFE22 => 230, 0xFE23 => 230, 0x1D185 => 230, 0x1D186 => 230
  1557. ,0x1D187 => 230, 0x1D189 => 230, 0x1D188 => 230, 0x1D1AA => 230, 0x1D1AB => 230
  1558. ,0x1D1AC => 230, 0x1D1AD => 230, 0x315 => 232, 0x31A => 232, 0x302C => 232
  1559. ,0x35F => 233, 0x362 => 233, 0x35D => 234, 0x35E => 234, 0x360 => 234
  1560. ,0x361 => 234, 0x345 => 240
  1561. )
  1562. );
  1563. }