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