Project

General

Profile

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
 * @author  Leonid Kogan <lko@neuse.de>
51
 * @copyright 2004-2010 phlyLabs Berlin, http://phlylabs.de
52
 * @version 0.6.9 2010-11-04
53
 */
54
class idna_convert
55
{
56
    // NP See below
57

    
58
    // Internal settings, do not mess with them
59
    protected $_punycode_prefix = 'xn--';
60
    protected $_invalid_ucs = 0x80000000;
61
    protected $_max_ucs = 0x10FFFF;
62
    protected $_base = 36;
63
    protected $_tmin = 1;
64
    protected $_tmax = 26;
65
    protected $_skew = 38;
66
    protected $_damp = 700;
67
    protected $_initial_bias = 72;
68
    protected $_initial_n = 0x80;
69
    protected $_sbase = 0xAC00;
70
    protected $_lbase = 0x1100;
71
    protected $_vbase = 0x1161;
72
    protected $_tbase = 0x11A7;
73
    protected $_lcount = 19;
74
    protected $_vcount = 21;
75
    protected $_tcount = 28;
76
    protected $_ncount = 588;   // _vcount * _tcount
77
    protected $_scount = 11172; // _lcount * _tcount * _vcount
78
    protected $_error = false;
79

    
80
    // See {@link set_paramter()} for details of how to change the following
81
    // settings from within your script / application
82
    protected $_api_encoding = 'utf8';   // Default input charset is UTF-8
83
    protected $_allow_overlong = false;  // Overlong UTF-8 encodings are forbidden
84
    protected $_strict_mode = false;     // Behave strict or not
85
    protected $_encode_german_sz = true; // True to encode German ?; False, if not
86

    
87
    /**
88
     * the constructor
89
     *
90
     * @param array $options
91
     * @return boolean
92
     * @since 0.5.2
93
     */
94
    public function __construct($options = false)
95
    {
96
        $this->slast = $this->_sbase + $this->_lcount * $this->_vcount * $this->_tcount;
97
        // If parameters are given, pass these to the respective method
98
        if (is_array($options)) return $this->set_parameter($options);
99
        if (!$this->_encode_german_sz) {
100
            $this->NP['replacemaps'][0xDF] = array(0x73, 0x73);
101
        }
102
    }
103

    
104
    /**
105
     * Sets a new option value. Available options and values:
106
     * [encoding - Use either UTF-8, UCS4 as array or UCS4 as string as input ('utf8' for UTF-8,
107
     *         'ucs4_string' and 'ucs4_array' respectively for UCS4); The output is always UTF-8]
108
     * [overlong - Unicode does not allow unnecessarily long encodings of chars,
109
     *             to allow this, set this parameter to true, else to false;
110
     *             default is false.]
111
     * [strict - true: strict mode, good for registration purposes - Causes errors
112
     *           on failures; false: loose mode, ideal for "wildlife" applications
113
     *           by silently ignoring errors and returning the original input instead
114
     *
115
     * @param    mixed     Parameter to set (string: single parameter; array of Parameter => Value pairs)
116
     * @param    string    Value to use (if parameter 1 is a string)
117
     * @return   boolean   true on success, false otherwise
118
     */
119
    public function set_parameter($option, $value = false)
120
    {
121
        if (!is_array($option)) {
122
            $option = array($option => $value);
123
        }
124
        foreach ($option as $k => $v) {
125
            switch ($k) {
126
            case 'encoding':
127
                switch ($v) {
128
                case 'utf8':
129
                case 'ucs4_string':
130
                case 'ucs4_array':
131
                    $this->_api_encoding = $v;
132
                    break;
133
                default:
134
                    $this->_error('Set Parameter: Unknown parameter '.$v.' for option '.$k);
135
                    return false;
136
                }
137
                break;
138
            case 'overlong':
139
                $this->_allow_overlong = ($v) ? true : false;
140
                break;
141
            case 'strict':
142
                $this->_strict_mode = ($v) ? true : false;
143
                break;
144
            case 'encode_german_sz':
145
                $this->_encode_german_sz = ($v) ? true : false;
146
                break;
147
            default:
148
                $this->_error('Set Parameter: Unknown option '.$k);
149
                return false;
150
            }
151
        }
152
        return true;
153
    }
154

    
155
    /**
156
     * Decode a given ACE domain name
157
     * @param    string   Domain name (ACE string)
158
     * [@param    string   Desired output encoding, see {@link set_parameter}]
159
     * @return   string   Decoded Domain name (UTF-8 or UCS-4)
160
     */
161
    public function decode($input, $one_time_encoding = false)
162
    {
163
        // Optionally set
164
        if ($one_time_encoding) {
165
            switch ($one_time_encoding) {
166
            case 'utf8':
167
            case 'ucs4_string':
168
            case 'ucs4_array':
169
                break;
170
            default:
171
                $this->_error('Unknown encoding '.$one_time_encoding);
172
                return false;
173
            }
174
        }
175
        // Make sure to drop any newline characters around
176
        $input = trim($input);
177

    
178
        // Negotiate input and try to determine, whether it is a plain string,
179
        // an email address or something like a complete URL
180
        if (strpos($input, '@')) { // Maybe it is an email address
181
            // No no in strict mode
182
            if ($this->_strict_mode) {
183
                $this->_error('Only simple domain name parts can be handled in strict mode');
184
                return false;
185
            }
186
            list ($email_pref, $input) = explode('@', $input, 2);
187
            $arr = explode('.', $input);
188
            foreach ($arr as $k => $v) {
189
                if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
190
                    $conv = $this->_decode($v);
191
                    if ($conv) $arr[$k] = $conv;
192
                }
193
            }
194
            $input = join('.', $arr);
195
            $arr = explode('.', $email_pref);
196
            foreach ($arr as $k => $v) {
197
                if (preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $v)) {
198
                    $conv = $this->_decode($v);
199
                    if ($conv) $arr[$k] = $conv;
200
                }
201
            }
202
            $email_pref = join('.', $arr);
203
            $return = $email_pref . '@' . $input;
204
        } elseif (preg_match('![:\./]!', $input)) { // Or a complete domain name (with or without paths / parameters)
205
            // No no in strict mode
206
            if ($this->_strict_mode) {
207
                $this->_error('Only simple domain name parts can be handled in strict mode');
208
                return false;
209
            }
210
            $parsed = parse_url($input);
211
            if (isset($parsed['host'])) {
212
                $arr = explode('.', $parsed['host']);
213
                foreach ($arr as $k => $v) {
214
                    $conv = $this->_decode($v);
215
                    if ($conv) $arr[$k] = $conv;
216
                }
217
                $parsed['host'] = join('.', $arr);
218
                $return =
219
                        (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
220
                        .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@')
221
                        .$parsed['host']
222
                        .(empty($parsed['port']) ? '' : ':'.$parsed['port'])
223
                        .(empty($parsed['path']) ? '' : $parsed['path'])
224
                        .(empty($parsed['query']) ? '' : '?'.$parsed['query'])
225
                        .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']);
226
            } else { // parse_url seems to have failed, try without it
227
                $arr = explode('.', $input);
228
                foreach ($arr as $k => $v) {
229
                    $conv = $this->_decode($v);
230
                    $arr[$k] = ($conv) ? $conv : $v;
231
                }
232
                $return = join('.', $arr);
233
            }
234
        } else { // Otherwise we consider it being a pure domain name string
235
            $return = $this->_decode($input);
236
            if (!$return) $return = $input;
237
        }
238
        // The output is UTF-8 by default, other output formats need conversion here
239
        // If one time encoding is given, use this, else the objects property
240
        switch (($one_time_encoding) ? $one_time_encoding : $this->_api_encoding) {
241
        case 'utf8':
242
            return $return;
243
            break;
244
        case 'ucs4_string':
245
           return $this->_ucs4_to_ucs4_string($this->_utf8_to_ucs4($return));
246
           break;
247
        case 'ucs4_array':
248
            return $this->_utf8_to_ucs4($return);
249
            break;
250
        default:
251
            $this->_error('Unsupported output format');
252
            return false;
253
        }
254
    }
255

    
256
    /**
257
     * Encode a given UTF-8 domain name
258
     * @param    string   Domain name (UTF-8 or UCS-4)
259
     * [@param    string   Desired input encoding, see {@link set_parameter}]
260
     * @return   string   Encoded Domain name (ACE string)
261
     */
262
    public function encode($decoded, $one_time_encoding = false)
263
    {
264
        // Forcing conversion of input to UCS4 array
265
        // If one time encoding is given, use this, else the objects property
266
        switch ($one_time_encoding ? $one_time_encoding : $this->_api_encoding) {
267
        case 'utf8':
268
            $decoded = $this->_utf8_to_ucs4($decoded);
269
            break;
270
        case 'ucs4_string':
271
           $decoded = $this->_ucs4_string_to_ucs4($decoded);
272
        case 'ucs4_array':
273
           break;
274
        default:
275
            $this->_error('Unsupported input format: '.($one_time_encoding ? $one_time_encoding : $this->_api_encoding));
276
            return false;
277
        }
278

    
279
        // No input, no output, what else did you expect?
280
        if (empty($decoded)) return '';
281

    
282
        // Anchors for iteration
283
        $last_begin = 0;
284
        // Output string
285
        $output = '';
286
        foreach ($decoded as $k => $v) {
287
            // Make sure to use just the plain dot
288
            switch($v) {
289
            case 0x3002:
290
            case 0xFF0E:
291
            case 0xFF61:
292
                $decoded[$k] = 0x2E;
293
                // Right, no break here, the above are converted to dots anyway
294
            // Stumbling across an anchoring character
295
            case 0x2E:
296
            case 0x2F:
297
            case 0x3A:
298
            case 0x3F:
299
            case 0x40:
300
                // Neither email addresses nor URLs allowed in strict mode
301
                if ($this->_strict_mode) {
302
                   $this->_error('Neither email addresses nor URLs are allowed in strict mode.');
303
                   return false;
304
                } else {
305
                    // Skip first char
306
                    if ($k) {
307
                        $encoded = '';
308
                        $encoded = $this->_encode(array_slice($decoded, $last_begin, (($k)-$last_begin)));
309
                        if ($encoded) {
310
                            $output .= $encoded;
311
                        } else {
312
                            $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($k)-$last_begin)));
313
                        }
314
                        $output .= chr($decoded[$k]);
315
                    }
316
                    $last_begin = $k + 1;
317
                }
318
            }
319
        }
320
        // Catch the rest of the string
321
        if ($last_begin) {
322
            $inp_len = sizeof($decoded);
323
            $encoded = '';
324
            $encoded = $this->_encode(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
325
            if ($encoded) {
326
                $output .= $encoded;
327
            } else {
328
                $output .= $this->_ucs4_to_utf8(array_slice($decoded, $last_begin, (($inp_len)-$last_begin)));
329
            }
330
            return $output;
331
        } else {
332
            if ($output = $this->_encode($decoded)) {
333
                return $output;
334
            } else {
335
                return $this->_ucs4_to_utf8($decoded);
336
            }
337
        }
338
    }
339

    
340
    /**
341
     * Removes a weakness of encode(), which cannot properly handle URIs but instead encodes their
342
     * path or query components, too.
343
     * @param string  $uri  Expects the URI as a UTF-8 (or ASCII) string
344
     * @return  string  The URI encoded to Punycode, everything but the host component is left alone
345
     * @since 0.6.4
346
     */
347
    public function encode_uri($uri)
348
    {
349
        $parsed = parse_url($uri);
350
        if (!isset($parsed['host'])) {
351
            $this->_error('The given string does not look like a URI');
352
            return false;
353
        }
354
        $arr = explode('.', $parsed['host']);
355
        foreach ($arr as $k => $v) {
356
            $conv = $this->encode($v, 'utf8');
357
            if ($conv) $arr[$k] = $conv;
358
        }
359
        $parsed['host'] = join('.', $arr);
360
        $return =
361
                (empty($parsed['scheme']) ? '' : $parsed['scheme'].(strtolower($parsed['scheme']) == 'mailto' ? ':' : '://'))
362
                .(empty($parsed['user']) ? '' : $parsed['user'].(empty($parsed['pass']) ? '' : ':'.$parsed['pass']).'@')
363
                .$parsed['host']
364
                .(empty($parsed['port']) ? '' : ':'.$parsed['port'])
365
                .(empty($parsed['path']) ? '' : $parsed['path'])
366
                .(empty($parsed['query']) ? '' : '?'.$parsed['query'])
367
                .(empty($parsed['fragment']) ? '' : '#'.$parsed['fragment']);
368
        return $return;
369
    }
370

    
371
    /**
372
     * Use this method to get the last error ocurred
373
     * @param    void
374
     * @return   string   The last error, that occured
375
     */
376
    public function get_last_error()
377
    {
378
        return $this->_error;
379
    }
380

    
381
    /**
382
     * The actual decoding algorithm
383
     * @param string
384
     * @return mixed
385
     */
386
    protected function _decode($encoded)
387
    {
388
        $decoded = array();
389
        // find the Punycode prefix
390
        if (!preg_match('!^'.preg_quote($this->_punycode_prefix, '!').'!', $encoded)) {
391
            $this->_error('This is not a punycode string');
392
            return false;
393
        }
394
        $encode_test = preg_replace('!^'.preg_quote($this->_punycode_prefix, '!').'!', '', $encoded);
395
        // If nothing left after removing the prefix, it is hopeless
396
        if (!$encode_test) {
397
            $this->_error('The given encoded string was empty');
398
            return false;
399
        }
400
        // Find last occurence of the delimiter
401
        $delim_pos = strrpos($encoded, '-');
402
        if ($delim_pos > strlen($this->_punycode_prefix)) {
403
            for ($k = strlen($this->_punycode_prefix); $k < $delim_pos; ++$k) {
404
                $decoded[] = ord($encoded{$k});
405
            }
406
        }
407
        $deco_len = count($decoded);
408
        $enco_len = strlen($encoded);
409

    
410
        // Wandering through the strings; init
411
        $is_first = true;
412
        $bias = $this->_initial_bias;
413
        $idx = 0;
414
        $char = $this->_initial_n;
415

    
416
        for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
417
            for ($old_idx = $idx, $w = 1, $k = $this->_base; 1 ; $k += $this->_base) {
418
                $digit = $this->_decode_digit($encoded{$enco_idx++});
419
                $idx += $digit * $w;
420
                $t = ($k <= $bias) ? $this->_tmin :
421
                        (($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
422
                if ($digit < $t) break;
423
                $w = (int) ($w * ($this->_base - $t));
424
            }
425
            $bias = $this->_adapt($idx - $old_idx, $deco_len + 1, $is_first);
426
            $is_first = false;
427
            $char += (int) ($idx / ($deco_len + 1));
428
            $idx %= ($deco_len + 1);
429
            if ($deco_len > 0) {
430
                // Make room for the decoded char
431
                for ($i = $deco_len; $i > $idx; $i--) $decoded[$i] = $decoded[($i - 1)];
432
            }
433
            $decoded[$idx++] = $char;
434
        }
435
        return $this->_ucs4_to_utf8($decoded);
436
    }
437

    
438
    /**
439
     * The actual encoding algorithm
440
     * @param  string
441
     * @return mixed
442
     */
443
    protected function _encode($decoded)
444
    {
445
        // We cannot encode a domain name containing the Punycode prefix
446
        $extract = strlen($this->_punycode_prefix);
447
        $check_pref = $this->_utf8_to_ucs4($this->_punycode_prefix);
448
        $check_deco = array_slice($decoded, 0, $extract);
449

    
450
        if ($check_pref == $check_deco) {
451
            $this->_error('This is already a punycode string');
452
            return false;
453
        }
454
        // We will not try to encode strings consisting of basic code points only
455
        $encodable = false;
456
        foreach ($decoded as $k => $v) {
457
            if ($v > 0x7a) {
458
                $encodable = true;
459
                break;
460
            }
461
        }
462
        if (!$encodable) {
463
            $this->_error('The given string does not contain encodable chars');
464
            return false;
465
        }
466
        // Do NAMEPREP
467
        $decoded = $this->_nameprep($decoded);
468
        if (!$decoded || !is_array($decoded)) return false; // NAMEPREP failed
469
        $deco_len  = count($decoded);
470
        if (!$deco_len) return false; // Empty array
471
        $codecount = 0; // How many chars have been consumed
472
        $encoded = '';
473
        // Copy all basic code points to output
474
        for ($i = 0; $i < $deco_len; ++$i) {
475
            $test = $decoded[$i];
476
            // Will match [-0-9a-zA-Z]
477
            if ((0x2F < $test && $test < 0x40) || (0x40 < $test && $test < 0x5B)
478
                    || (0x60 < $test && $test <= 0x7B) || (0x2D == $test)) {
479
                $encoded .= chr($decoded[$i]);
480
                $codecount++;
481
            }
482
        }
483
        if ($codecount == $deco_len) return $encoded; // All codepoints were basic ones
484

    
485
        // Start with the prefix; copy it to output
486
        $encoded = $this->_punycode_prefix.$encoded;
487
        // If we have basic code points in output, add an hyphen to the end
488
        if ($codecount) $encoded .= '-';
489
        // Now find and encode all non-basic code points
490
        $is_first = true;
491
        $cur_code = $this->_initial_n;
492
        $bias = $this->_initial_bias;
493
        $delta = 0;
494
        while ($codecount < $deco_len) {
495
            // Find the smallest code point >= the current code point and
496
            // remember the last ouccrence of it in the input
497
            for ($i = 0, $next_code = $this->_max_ucs; $i < $deco_len; $i++) {
498
                if ($decoded[$i] >= $cur_code && $decoded[$i] <= $next_code) {
499
                    $next_code = $decoded[$i];
500
                }
501
            }
502
            $delta += ($next_code - $cur_code) * ($codecount + 1);
503
            $cur_code = $next_code;
504

    
505
            // Scan input again and encode all characters whose code point is $cur_code
506
            for ($i = 0; $i < $deco_len; $i++) {
507
                if ($decoded[$i] < $cur_code) {
508
                    $delta++;
509
                } elseif ($decoded[$i] == $cur_code) {
510
                    for ($q = $delta, $k = $this->_base; 1; $k += $this->_base) {
511
                        $t = ($k <= $bias) ? $this->_tmin :
512
                                (($k >= $bias + $this->_tmax) ? $this->_tmax : $k - $bias);
513
                        if ($q < $t) break;
514
                        $encoded .= $this->_encode_digit(intval($t + (($q - $t) % ($this->_base - $t)))); //v0.4.5 Changed from ceil() to intval()
515
                        $q = (int) (($q - $t) / ($this->_base - $t));
516
                    }
517
                    $encoded .= $this->_encode_digit($q);
518
                    $bias = $this->_adapt($delta, $codecount+1, $is_first);
519
                    $codecount++;
520
                    $delta = 0;
521
                    $is_first = false;
522
                }
523
            }
524
            $delta++;
525
            $cur_code++;
526
        }
527
        return $encoded;
528
    }
529

    
530
    /**
531
     * Adapt the bias according to the current code point and position
532
     * @param int $delta
533
     * @param int $npoints
534
     * @param int $is_first
535
     * @return int
536
     */
537
    protected function _adapt($delta, $npoints, $is_first)
538
    {
539
        $delta = intval($is_first ? ($delta / $this->_damp) : ($delta / 2));
540
        $delta += intval($delta / $npoints);
541
        for ($k = 0; $delta > (($this->_base - $this->_tmin) * $this->_tmax) / 2; $k += $this->_base) {
542
            $delta = intval($delta / ($this->_base - $this->_tmin));
543
        }
544
        return intval($k + ($this->_base - $this->_tmin + 1) * $delta / ($delta + $this->_skew));
545
    }
546

    
547
    /**
548
     * Encoding a certain digit
549
     * @param    int $d
550
     * @return string
551
     */
552
    protected function _encode_digit($d)
553
    {
554
        return chr($d + 22 + 75 * ($d < 26));
555
    }
556

    
557
    /**
558
     * Decode a certain digit
559
     * @param    int $cp
560
     * @return int
561
     */
562
    protected function _decode_digit($cp)
563
    {
564
        $cp = ord($cp);
565
        return ($cp - 48 < 10) ? $cp - 22 : (($cp - 65 < 26) ? $cp - 65 : (($cp - 97 < 26) ? $cp - 97 : $this->_base));
566
    }
567

    
568
    /**
569
     * Internal error handling method
570
     * @param  string $error
571
     */
572
    protected function _error($error = '')
573
    {
574
        $this->_error = $error;
575
    }
576

    
577
    /**
578
     * Do Nameprep according to RFC3491 and RFC3454
579
     * @param    array    Unicode Characters
580
     * @return   string   Unicode Characters, Nameprep'd
581
     */
582
    protected function _nameprep($input)
583
    {
584
        $output = array();
585
        $error = false;
586
        //
587
        // Mapping
588
        // Walking through the input array, performing the required steps on each of
589
        // the input chars and putting the result into the output array
590
        // While mapping required chars we apply the cannonical ordering
591
        foreach ($input as $v) {
592
            // Map to nothing == skip that code point
593
            if (in_array($v, $this->NP['map_nothing'])) continue;
594
            // Try to find prohibited input
595
            if (in_array($v, $this->NP['prohibit']) || in_array($v, $this->NP['general_prohibited'])) {
596
                $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
597
                return false;
598
            }
599
            foreach ($this->NP['prohibit_ranges'] as $range) {
600
                if ($range[0] <= $v && $v <= $range[1]) {
601
                    $this->_error('NAMEPREP: Prohibited input U+'.sprintf('%08X', $v));
602
                    return false;
603
                }
604
            }
605
            // Hangul syllable decomposition
606
            if (0xAC00 <= $v && $v <= 0xD7AF) {
607
                foreach ($this->_hangul_decompose($v) as $out) $output[] = (int) $out;
608
            // There's a decomposition mapping for that code point
609
            } elseif (isset($this->NP['replacemaps'][$v])) {
610
                foreach ($this->_apply_cannonical_ordering($this->NP['replacemaps'][$v]) as $out) {
611
                    $output[] = (int) $out;
612
                }
613
            } else {
614
                $output[] = (int) $v;
615
            }
616
        }
617
        // Before applying any Combining, try to rearrange any Hangul syllables
618
        $output = $this->_hangul_compose($output);
619
        //
620
        // Combine code points
621
        //
622
        $last_class = 0;
623
        $last_starter = 0;
624
        $out_len = count($output);
625
        for ($i = 0; $i < $out_len; ++$i) {
626
            $class = $this->_get_combining_class($output[$i]);
627
            if ((!$last_class || $last_class > $class) && $class) {
628
                // Try to match
629
                $seq_len = $i - $last_starter;
630
                $out = $this->_combine(array_slice($output, $last_starter, $seq_len));
631
                // On match: Replace the last starter with the composed character and remove
632
                // the now redundant non-starter(s)
633
                if ($out) {
634
                    $output[$last_starter] = $out;
635
                    if (count($out) != $seq_len) {
636
                        for ($j = $i+1; $j < $out_len; ++$j) $output[$j-1] = $output[$j];
637
                        unset($output[$out_len]);
638
                    }
639
                    // Rewind the for loop by one, since there can be more possible compositions
640
                    $i--;
641
                    $out_len--;
642
                    $last_class = ($i == $last_starter) ? 0 : $this->_get_combining_class($output[$i-1]);
643
                    continue;
644
                }
645
            }
646
            // The current class is 0
647
            if (!$class) $last_starter = $i;
648
            $last_class = $class;
649
        }
650
        return $output;
651
    }
652

    
653
    /**
654
     * Decomposes a Hangul syllable
655
     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
656
     * @param    integer  32bit UCS4 code point
657
     * @return   array    Either Hangul Syllable decomposed or original 32bit value as one value array
658
     */
659
    protected function _hangul_decompose($char)
660
    {
661
        $sindex = (int) $char - $this->_sbase;
662
        if ($sindex < 0 || $sindex >= $this->_scount) return array($char);
663
        $result = array();
664
        $result[] = (int) $this->_lbase + $sindex / $this->_ncount;
665
        $result[] = (int) $this->_vbase + ($sindex % $this->_ncount) / $this->_tcount;
666
        $T = intval($this->_tbase + $sindex % $this->_tcount);
667
        if ($T != $this->_tbase) $result[] = $T;
668
        return $result;
669
    }
670
    /**
671
     * Ccomposes a Hangul syllable
672
     * (see http://www.unicode.org/unicode/reports/tr15/#Hangul
673
     * @param    array    Decomposed UCS4 sequence
674
     * @return   array    UCS4 sequence with syllables composed
675
     */
676
    protected function _hangul_compose($input)
677
    {
678
        $inp_len = count($input);
679
        if (!$inp_len) return array();
680
        $result = array();
681
        $last = (int) $input[0];
682
        $result[] = $last; // copy first char from input to output
683

    
684
        for ($i = 1; $i < $inp_len; ++$i) {
685
            $char = (int) $input[$i];
686
            $sindex = $last - $this->_sbase;
687
            $lindex = $last - $this->_lbase;
688
            $vindex = $char - $this->_vbase;
689
            $tindex = $char - $this->_tbase;
690
            // Find out, whether two current characters are LV and T
691
            if (0 <= $sindex && $sindex < $this->_scount && ($sindex % $this->_tcount == 0)
692
                    && 0 <= $tindex && $tindex <= $this->_tcount) {
693
                // create syllable of form LVT
694
                $last += $tindex;
695
                $result[(count($result) - 1)] = $last; // reset last
696
                continue; // discard char
697
            }
698
            // Find out, whether two current characters form L and V
699
            if (0 <= $lindex && $lindex < $this->_lcount && 0 <= $vindex && $vindex < $this->_vcount) {
700
                // create syllable of form LV
701
                $last = (int) $this->_sbase + ($lindex * $this->_vcount + $vindex) * $this->_tcount;
702
                $result[(count($result) - 1)] = $last; // reset last
703
                continue; // discard char
704
            }
705
            // if neither case was true, just add the character
706
            $last = $char;
707
            $result[] = $char;
708
        }
709
        return $result;
710
    }
711

    
712
    /**
713
     * Returns the combining class of a certain wide char
714
     * @param    integer    Wide char to check (32bit integer)
715
     * @return   integer    Combining class if found, else 0
716
     */
717
    protected function _get_combining_class($char)
718
    {
719
        return isset($this->NP['norm_combcls'][$char]) ? $this->NP['norm_combcls'][$char] : 0;
720
    }
721

    
722
    /**
723
     * Apllies the cannonical ordering of a decomposed UCS4 sequence
724
     * @param    array      Decomposed UCS4 sequence
725
     * @return   array      Ordered USC4 sequence
726
     */
727
    protected function _apply_cannonical_ordering($input)
728
    {
729
        $swap = true;
730
        $size = count($input);
731
        while ($swap) {
732
            $swap = false;
733
            $last = $this->_get_combining_class(intval($input[0]));
734
            for ($i = 0; $i < $size-1; ++$i) {
735
                $next = $this->_get_combining_class(intval($input[$i+1]));
736
                if ($next != 0 && $last > $next) {
737
                    // Move item leftward until it fits
738
                    for ($j = $i + 1; $j > 0; --$j) {
739
                        if ($this->_get_combining_class(intval($input[$j-1])) <= $next) break;
740
                        $t = intval($input[$j]);
741
                        $input[$j] = intval($input[$j-1]);
742
                        $input[$j-1] = $t;
743
                        $swap = true;
744
                    }
745
                    // Reentering the loop looking at the old character again
746
                    $next = $last;
747
                }
748
                $last = $next;
749
            }
750
        }
751
        return $input;
752
    }
753

    
754
    /**
755
     * Do composition of a sequence of starter and non-starter
756
     * @param    array      UCS4 Decomposed sequence
757
     * @return   array      Ordered USC4 sequence
758
     */
759
    protected function _combine($input)
760
    {
761
        $inp_len = count($input);
762
        foreach ($this->NP['replacemaps'] as $np_src => $np_target) {
763
            if ($np_target[0] != $input[0]) continue;
764
            if (count($np_target) != $inp_len) continue;
765
            $hit = false;
766
            foreach ($input as $k2 => $v2) {
767
                if ($v2 == $np_target[$k2]) {
768
                    $hit = true;
769
                } else {
770
                    $hit = false;
771
                    break;
772
                }
773
            }
774
            if ($hit) return $np_src;
775
        }
776
        return false;
777
    }
778

    
779
    /**
780
     * This converts an UTF-8 encoded string to its UCS-4 representation
781
     * By talking about UCS-4 "strings" we mean arrays of 32bit integers representing
782
     * each of the "chars". This is due to PHP not being able to handle strings with
783
     * bit depth different from 8. This apllies to the reverse method _ucs4_to_utf8(), too.
784
     * The following UTF-8 encodings are supported:
785
     * bytes bits  representation
786
     * 1        7  0xxxxxxx
787
     * 2       11  110xxxxx 10xxxxxx
788
     * 3       16  1110xxxx 10xxxxxx 10xxxxxx
789
     * 4       21  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
790
     * 5       26  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
791
     * 6       31  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
792
     * Each x represents a bit that can be used to store character data.
793
     * The five and six byte sequences are part of Annex D of ISO/IEC 10646-1:2000
794
     * @param string $input
795
     * @return string
796
     */
797
    protected function _utf8_to_ucs4($input)
798
    {
799
        $output = array();
800
        $out_len = 0;
801
        // Patch by Daniel Hahler; work around prolbem with mbstring.func_overload
802
        if (function_exists('mb_strlen')) {
803
            $inp_len = mb_strlen($input, '8bit');
804
        } else {
805
            $inp_len = strlen($input);
806
        }
807
        $mode = 'next';
808
        $test = 'none';
809
        for ($k = 0; $k < $inp_len; ++$k) {
810
            $v = ord($input{$k}); // Extract byte from input string
811
            if ($v < 128) { // We found an ASCII char - put into stirng as is
812
                $output[$out_len] = $v;
813
                ++$out_len;
814
                if ('add' == $mode) {
815
                    $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
816
                    return false;
817
                }
818
                continue;
819
            }
820
            if ('next' == $mode) { // Try to find the next start byte; determine the width of the Unicode char
821
                $start_byte = $v;
822
                $mode = 'add';
823
                $test = 'range';
824
                if ($v >> 5 == 6) { // &110xxxxx 10xxxxx
825
                    $next_byte = 0; // Tells, how many times subsequent bitmasks must rotate 6bits to the left
826
                    $v = ($v - 192) << 6;
827
                } elseif ($v >> 4 == 14) { // &1110xxxx 10xxxxxx 10xxxxxx
828
                    $next_byte = 1;
829
                    $v = ($v - 224) << 12;
830
                } elseif ($v >> 3 == 30) { // &11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
831
                    $next_byte = 2;
832
                    $v = ($v - 240) << 18;
833
                } elseif ($v >> 2 == 62) { // &111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
834
                    $next_byte = 3;
835
                    $v = ($v - 248) << 24;
836
                } elseif ($v >> 1 == 126) { // &1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
837
                    $next_byte = 4;
838
                    $v = ($v - 252) << 30;
839
                } else {
840
                    $this->_error('This might be UTF-8, but I don\'t understand it at byte '.$k);
841
                    return false;
842
                }
843
                if ('add' == $mode) {
844
                    $output[$out_len] = (int) $v;
845
                    ++$out_len;
846
                    continue;
847
                }
848
            }
849
            if ('add' == $mode) {
850
                if (!$this->_allow_overlong && $test == 'range') {
851
                    $test = 'none';
852
                    if (($v < 0xA0 && $start_byte == 0xE0) || ($v < 0x90 && $start_byte == 0xF0) || ($v > 0x8F && $start_byte == 0xF4)) {
853
                        $this->_error('Bogus UTF-8 character detected (out of legal range) at byte '.$k);
854
                        return false;
855
                    }
856
                }
857
                if ($v >> 6 == 2) { // Bit mask must be 10xxxxxx
858
                    $v = ($v - 128) << ($next_byte * 6);
859
                    $output[($out_len - 1)] += $v;
860
                    --$next_byte;
861
                } else {
862
                    $this->_error('Conversion from UTF-8 to UCS-4 failed: malformed input at byte '.$k);
863
                    return false;
864
                }
865
                if ($next_byte < 0) {
866
                    $mode = 'next';
867
                }
868
            }
869
        } // for
870
        return $output;
871
    }
872

    
873
    /**
874
     * Convert UCS-4 string into UTF-8 string
875
     * See _utf8_to_ucs4() for details
876
     * @param string  $input
877
     * @return string
878
     */
879
    protected function _ucs4_to_utf8($input)
880
    {
881
        $output = '';
882
        foreach ($input as $k => $v) {
883
            if ($v < 128) { // 7bit are transferred literally
884
                $output .= chr($v);
885
            } elseif ($v < (1 << 11)) { // 2 bytes
886
                $output .= chr(192+($v >> 6)).chr(128+($v & 63));
887
            } elseif ($v < (1 << 16)) { // 3 bytes
888
                $output .= chr(224+($v >> 12)).chr(128+(($v >> 6) & 63)).chr(128+($v & 63));
889
            } elseif ($v < (1 << 21)) { // 4 bytes
890
                $output .= chr(240+($v >> 18)).chr(128+(($v >> 12) & 63)).chr(128+(($v >> 6) & 63)).chr(128+($v & 63));
891
            } elseif (self::$safe_mode) {
892
                $output .= self::$safe_char;
893
            } else {
894
                $this->_error('Conversion from UCS-4 to UTF-8 failed: malformed input at byte '.$k);
895
                return false;
896
            }
897
        }
898
        return $output;
899
    }
900

    
901
    /**
902
     * Convert UCS-4 array into UCS-4 string
903
     *
904
     * @param array $input
905
     * @return string
906
     */
907
    protected function _ucs4_to_ucs4_string($input)
908
    {
909
        $output = '';
910
        // Take array values and split output to 4 bytes per value
911
        // The bit mask is 255, which reads &11111111
912
        foreach ($input as $v) {
913
            $output .= chr(($v >> 24) & 255).chr(($v >> 16) & 255).chr(($v >> 8) & 255).chr($v & 255);
914
        }
915
        return $output;
916
    }
917

    
918
    /**
919
     * Convert UCS-4 strin into UCS-4 garray
920
     *
921
     * @param  string $input
922
     * @return array
923
     */
924
    protected function _ucs4_string_to_ucs4($input)
925
    {
926
        $output = array();
927
        $inp_len = strlen($input);
928
        // Input length must be dividable by 4
929
        if ($inp_len % 4) {
930
            $this->_error('Input UCS4 string is broken');
931
            return false;
932
        }
933
        // Empty input - return empty output
934
        if (!$inp_len) return $output;
935
        for ($i = 0, $out_len = -1; $i < $inp_len; ++$i) {
936
            // Increment output position every 4 input bytes
937
            if (!($i % 4)) {
938
                $out_len++;
939
                $output[$out_len] = 0;
940
            }
941
            $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) );
942
        }
943
        return $output;
944
    }
945

    
946
    /**
947
     * Holds all relevant mapping tables
948
     * See RFC3454 for details
949
     *
950
     * @private array
951
     * @since 0.5.2
952
     */
953
    protected $NP = array
954
            ('map_nothing' => array(0xAD, 0x34F, 0x1806, 0x180B, 0x180C, 0x180D, 0x200B, 0x200C
955
                    ,0x200D, 0x2060, 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07
956
                    ,0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F, 0xFEFF
957
                    )
958
            ,'general_prohibited' => array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
959
                    ,20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ,33, 34, 35, 36, 37, 38, 39, 40, 41, 42
960
                    ,43, 44, 47, 59, 60, 61, 62, 63, 64, 91, 92, 93, 94, 95, 96, 123, 124, 125, 126, 127, 0x3002
961
                    )
962
            ,'prohibit' => array(0xA0, 0x340, 0x341, 0x6DD, 0x70F, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003
963
                    ,0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x200B, 0x200C, 0x200D, 0x200E, 0x200F
964
                    ,0x2028, 0x2029, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E, 0x202F, 0x205F, 0x206A, 0x206B, 0x206C
965
                    ,0x206D, 0x206E, 0x206F, 0x3000, 0xFEFF, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF
966
                    ,0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE
967
                    ,0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, 0xBFFFF
968
                    ,0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xE0001, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF
969
                    )
970
            ,'prohibit_ranges' => array(array(0x80, 0x9F), array(0x2060, 0x206F), array(0x1D173, 0x1D17A)
971
                    ,array(0xE000, 0xF8FF) ,array(0xF0000, 0xFFFFD), array(0x100000, 0x10FFFD)
972
                    ,array(0xFDD0, 0xFDEF), array(0xD800, 0xDFFF), array(0x2FF0, 0x2FFB), array(0xE0020, 0xE007F)
973
                    )
974
            ,'replacemaps' => array(0x41 => array(0x61), 0x42 => array(0x62), 0x43 => array(0x63)
975
                    ,0x44 => array(0x64), 0x45 => array(0x65), 0x46 => array(0x66), 0x47 => array(0x67)
976
                    ,0x48 => array(0x68), 0x49 => array(0x69), 0x4A => array(0x6A), 0x4B => array(0x6B)
977
                    ,0x4C => array(0x6C), 0x4D => array(0x6D), 0x4E => array(0x6E), 0x4F => array(0x6F)
978
                    ,0x50 => array(0x70), 0x51 => array(0x71), 0x52 => array(0x72), 0x53 => array(0x73)
979
                    ,0x54 => array(0x74), 0x55 => array(0x75), 0x56 => array(0x76), 0x57 => array(0x77)
980
                    ,0x58 => array(0x78), 0x59 => array(0x79), 0x5A => array(0x7A), 0xB5 => array(0x3BC)
981
                    ,0xC0 => array(0xE0), 0xC1 => array(0xE1), 0xC2 => array(0xE2), 0xC3 => array(0xE3)
982
                    ,0xC4 => array(0xE4), 0xC5 => array(0xE5), 0xC6 => array(0xE6), 0xC7 => array(0xE7)
983
                    ,0xC8 => array(0xE8), 0xC9 => array(0xE9), 0xCA => array(0xEA), 0xCB => array(0xEB)
984
                    ,0xCC => array(0xEC), 0xCD => array(0xED), 0xCE => array(0xEE), 0xCF => array(0xEF)
985
                    ,0xD0 => array(0xF0), 0xD1 => array(0xF1), 0xD2 => array(0xF2), 0xD3 => array(0xF3)
986
                    ,0xD4 => array(0xF4), 0xD5 => array(0xF5), 0xD6 => array(0xF6), 0xD8 => array(0xF8)
987
                    ,0xD9 => array(0xF9), 0xDA => array(0xFA), 0xDB => array(0xFB), 0xDC => array(0xFC)
988
                    ,0xDD => array(0xFD), 0xDE => array(0xFE) /* Here was German "?" -> "ss", is now configurable */
989
                    ,0x100 => array(0x101), 0x102 => array(0x103), 0x104 => array(0x105)
990
                    ,0x106 => array(0x107), 0x108 => array(0x109), 0x10A => array(0x10B)
991
                    ,0x10C => array(0x10D), 0x10E => array(0x10F), 0x110 => array(0x111)
992
                    ,0x112 => array(0x113), 0x114 => array(0x115), 0x116 => array(0x117)
993
                    ,0x118 => array(0x119), 0x11A => array(0x11B), 0x11C => array(0x11D)
994
                    ,0x11E => array(0x11F), 0x120 => array(0x121), 0x122 => array(0x123)
995
                    ,0x124 => array(0x125), 0x126 => array(0x127), 0x128 => array(0x129)
996
                    ,0x12A => array(0x12B), 0x12C => array(0x12D), 0x12E => array(0x12F)
997
                    ,0x130 => array(0x69, 0x307), 0x132 => array(0x133), 0x134 => array(0x135)
998
                    ,0x136 => array(0x137), 0x139 => array(0x13A), 0x13B => array(0x13C)
999
                    ,0x13D => array(0x13E), 0x13F => array(0x140), 0x141 => array(0x142)
1000
                    ,0x143 => array(0x144), 0x145 => array(0x146), 0x147 => array(0x148)
1001
                    ,0x149 => array(0x2BC, 0x6E), 0x14A => array(0x14B), 0x14C => array(0x14D)
1002
                    ,0x14E => array(0x14F), 0x150 => array(0x151), 0x152 => array(0x153)
1003
                    ,0x154 => array(0x155), 0x156 => array(0x157), 0x158 => array(0x159)
1004
                    ,0x15A => array(0x15B), 0x15C => array(0x15D), 0x15E => array(0x15F)
1005
                    ,0x160 => array(0x161), 0x162 => array(0x163), 0x164 => array(0x165)
1006
                    ,0x166 => array(0x167), 0x168 => array(0x169), 0x16A => array(0x16B)
1007
                    ,0x16C => array(0x16D), 0x16E => array(0x16F), 0x170 => array(0x171)
1008
                    ,0x172 => array(0x173), 0x174 => array(0x175), 0x176 => array(0x177)
1009
                    ,0x178 => array(0xFF), 0x179 => array(0x17A), 0x17B => array(0x17C)
1010
                    ,0x17D => array(0x17E), 0x17F => array(0x73), 0x181 => array(0x253)
1011
                    ,0x182 => array(0x183), 0x184 => array(0x185), 0x186 => array(0x254)
1012
                    ,0x187 => array(0x188), 0x189 => array(0x256), 0x18A => array(0x257)
1013
                    ,0x18B => array(0x18C), 0x18E => array(0x1DD), 0x18F => array(0x259)
1014
                    ,0x190 => array(0x25B), 0x191 => array(0x192), 0x193 => array(0x260)
1015
                    ,0x194 => array(0x263), 0x196 => array(0x269), 0x197 => array(0x268)
1016
                    ,0x198 => array(0x199), 0x19C => array(0x26F), 0x19D => array(0x272)
1017
                    ,0x19F => array(0x275), 0x1A0 => array(0x1A1), 0x1A2 => array(0x1A3)
1018
                    ,0x1A4 => array(0x1A5), 0x1A6 => array(0x280), 0x1A7 => array(0x1A8)
1019
                    ,0x1A9 => array(0x283), 0x1AC => array(0x1AD), 0x1AE => array(0x288)
1020
                    ,0x1AF => array(0x1B0), 0x1B1 => array(0x28A), 0x1B2 => array(0x28B)
1021
                    ,0x1B3 => array(0x1B4), 0x1B5 => array(0x1B6), 0x1B7 => array(0x292)
1022
                    ,0x1B8 => array(0x1B9), 0x1BC => array(0x1BD), 0x1C4 => array(0x1C6)
1023
                    ,0x1C5 => array(0x1C6), 0x1C7 => array(0x1C9), 0x1C8 => array(0x1C9)
1024
                    ,0x1CA => array(0x1CC), 0x1CB => array(0x1CC), 0x1CD => array(0x1CE)
1025
                    ,0x1CF => array(0x1D0), 0x1D1   => array(0x1D2), 0x1D3   => array(0x1D4)
1026
                    ,0x1D5   => array(0x1D6), 0x1D7   => array(0x1D8), 0x1D9   => array(0x1DA)
1027
                    ,0x1DB   => array(0x1DC), 0x1DE   => array(0x1DF), 0x1E0   => array(0x1E1)
1028
                    ,0x1E2   => array(0x1E3), 0x1E4   => array(0x1E5), 0x1E6   => array(0x1E7)
1029
                    ,0x1E8   => array(0x1E9), 0x1EA   => array(0x1EB), 0x1EC   => array(0x1ED)
1030
                    ,0x1EE   => array(0x1EF), 0x1F0   => array(0x6A, 0x30C), 0x1F1   => array(0x1F3)
1031
                    ,0x1F2   => array(0x1F3), 0x1F4   => array(0x1F5), 0x1F6   => array(0x195)
1032
                    ,0x1F7   => array(0x1BF), 0x1F8   => array(0x1F9), 0x1FA   => array(0x1FB)
1033
                    ,0x1FC   => array(0x1FD), 0x1FE   => array(0x1FF), 0x200   => array(0x201)
1034
                    ,0x202   => array(0x203), 0x204   => array(0x205), 0x206   => array(0x207)
1035
                    ,0x208   => array(0x209), 0x20A   => array(0x20B), 0x20C   => array(0x20D)
1036
                    ,0x20E   => array(0x20F), 0x210   => array(0x211), 0x212   => array(0x213)
1037
                    ,0x214   => array(0x215), 0x216   => array(0x217), 0x218   => array(0x219)
1038
                    ,0x21A   => array(0x21B), 0x21C   => array(0x21D), 0x21E   => array(0x21F)
1039
                    ,0x220   => array(0x19E), 0x222   => array(0x223), 0x224   => array(0x225)
1040
                    ,0x226   => array(0x227), 0x228   => array(0x229), 0x22A   => array(0x22B)
1041
                    ,0x22C   => array(0x22D), 0x22E   => array(0x22F), 0x230   => array(0x231)
1042
                    ,0x232   => array(0x233), 0x345   => array(0x3B9), 0x37A   => array(0x20, 0x3B9)
1043
                    ,0x386   => array(0x3AC), 0x388   => array(0x3AD), 0x389   => array(0x3AE)
1044
                    ,0x38A   => array(0x3AF), 0x38C   => array(0x3CC), 0x38E   => array(0x3CD)
1045
                    ,0x38F   => array(0x3CE), 0x390   => array(0x3B9, 0x308, 0x301)
1046
                    ,0x391   => array(0x3B1), 0x392   => array(0x3B2), 0x393   => array(0x3B3)
1047
                    ,0x394   => array(0x3B4), 0x395   => array(0x3B5), 0x396   => array(0x3B6)
1048
                    ,0x397   => array(0x3B7), 0x398   => array(0x3B8), 0x399   => array(0x3B9)
1049
                    ,0x39A   => array(0x3BA), 0x39B   => array(0x3BB), 0x39C   => array(0x3BC)
1050
                    ,0x39D   => array(0x3BD), 0x39E   => array(0x3BE), 0x39F   => array(0x3BF)
1051
                    ,0x3A0   => array(0x3C0), 0x3A1   => array(0x3C1), 0x3A3   => array(0x3C3)
1052
                    ,0x3A4   => array(0x3C4), 0x3A5   => array(0x3C5), 0x3A6   => array(0x3C6)
1053
                    ,0x3A7   => array(0x3C7), 0x3A8   => array(0x3C8), 0x3A9   => array(0x3C9)
1054
                    ,0x3AA   => array(0x3CA), 0x3AB   => array(0x3CB), 0x3B0   => array(0x3C5, 0x308, 0x301)
1055
                    ,0x3C2   => array(0x3C3), 0x3D0   => array(0x3B2), 0x3D1   => array(0x3B8)
1056
                    ,0x3D2   => array(0x3C5), 0x3D3   => array(0x3CD), 0x3D4   => array(0x3CB)
1057
                    ,0x3D5   => array(0x3C6), 0x3D6   => array(0x3C0), 0x3D8   => array(0x3D9)
1058
                    ,0x3DA   => array(0x3DB), 0x3DC   => array(0x3DD), 0x3DE   => array(0x3DF)
1059
                    ,0x3E0   => array(0x3E1), 0x3E2   => array(0x3E3), 0x3E4   => array(0x3E5)
1060
                    ,0x3E6   => array(0x3E7), 0x3E8   => array(0x3E9), 0x3EA   => array(0x3EB)
1061
                    ,0x3EC   => array(0x3ED), 0x3EE   => array(0x3EF), 0x3F0   => array(0x3BA)
1062
                    ,0x3F1   => array(0x3C1), 0x3F2   => array(0x3C3), 0x3F4   => array(0x3B8)
1063
                    ,0x3F5   => array(0x3B5), 0x400   => array(0x450), 0x401   => array(0x451)
1064
                    ,0x402   => array(0x452), 0x403   => array(0x453), 0x404   => array(0x454)
1065
                    ,0x405   => array(0x455), 0x406   => array(0x456), 0x407   => array(0x457)
1066
                    ,0x408   => array(0x458), 0x409   => array(0x459), 0x40A   => array(0x45A)
1067
                    ,0x40B   => array(0x45B), 0x40C   => array(0x45C), 0x40D   => array(0x45D)
1068
                    ,0x40E   => array(0x45E), 0x40F   => array(0x45F), 0x410   => array(0x430)
1069
                    ,0x411   => array(0x431), 0x412   => array(0x432), 0x413   => array(0x433)
1070
                    ,0x414   => array(0x434), 0x415   => array(0x435), 0x416   => array(0x436)
1071
                    ,0x417   => array(0x437), 0x418   => array(0x438), 0x419   => array(0x439)
1072
                    ,0x41A   => array(0x43A), 0x41B   => array(0x43B), 0x41C   => array(0x43C)
1073
                    ,0x41D   => array(0x43D), 0x41E   => array(0x43E), 0x41F   => array(0x43F)
1074
                    ,0x420   => array(0x440), 0x421   => array(0x441), 0x422   => array(0x442)
1075
                    ,0x423   => array(0x443), 0x424   => array(0x444), 0x425   => array(0x445)
1076
                    ,0x426   => array(0x446), 0x427   => array(0x447), 0x428   => array(0x448)
1077
                    ,0x429   => array(0x449), 0x42A   => array(0x44A), 0x42B   => array(0x44B)
1078
                    ,0x42C   => array(0x44C), 0x42D   => array(0x44D), 0x42E   => array(0x44E)
1079
                    ,0x42F   => array(0x44F), 0x460   => array(0x461), 0x462   => array(0x463)
1080
                    ,0x464   => array(0x465), 0x466   => array(0x467), 0x468   => array(0x469)
1081
                    ,0x46A   => array(0x46B), 0x46C   => array(0x46D), 0x46E   => array(0x46F)
1082
                    ,0x470   => array(0x471), 0x472   => array(0x473), 0x474   => array(0x475)
1083
                    ,0x476   => array(0x477), 0x478   => array(0x479), 0x47A   => array(0x47B)
1084
                    ,0x47C   => array(0x47D), 0x47E   => array(0x47F), 0x480   => array(0x481)
1085
                    ,0x48A   => array(0x48B), 0x48C   => array(0x48D), 0x48E   => array(0x48F)
1086
                    ,0x490   => array(0x491), 0x492   => array(0x493), 0x494   => array(0x495)
1087
                    ,0x496   => array(0x497), 0x498   => array(0x499), 0x49A   => array(0x49B)
1088
                    ,0x49C   => array(0x49D), 0x49E   => array(0x49F), 0x4A0   => array(0x4A1)
1089
                    ,0x4A2   => array(0x4A3), 0x4A4   => array(0x4A5), 0x4A6   => array(0x4A7)
1090
                    ,0x4A8   => array(0x4A9), 0x4AA   => array(0x4AB), 0x4AC   => array(0x4AD)
1091
                    ,0x4AE   => array(0x4AF), 0x4B0   => array(0x4B1), 0x4B2   => array(0x4B3)
1092
                    ,0x4B4   => array(0x4B5), 0x4B6   => array(0x4B7), 0x4B8   => array(0x4B9)
1093
                    ,0x4BA   => array(0x4BB), 0x4BC   => array(0x4BD), 0x4BE   => array(0x4BF)
1094
                    ,0x4C1   => array(0x4C2), 0x4C3   => array(0x4C4), 0x4C5   => array(0x4C6)
1095
                    ,0x4C7   => array(0x4C8), 0x4C9   => array(0x4CA), 0x4CB   => array(0x4CC)
1096
                    ,0x4CD   => array(0x4CE), 0x4D0   => array(0x4D1), 0x4D2   => array(0x4D3)
1097
                    ,0x4D4   => array(0x4D5), 0x4D6   => array(0x4D7), 0x4D8   => array(0x4D9)
1098
                    ,0x4DA   => array(0x4DB), 0x4DC   => array(0x4DD), 0x4DE   => array(0x4DF)
1099
                    ,0x4E0   => array(0x4E1), 0x4E2   => array(0x4E3), 0x4E4   => array(0x4E5)
1100
                    ,0x4E6   => array(0x4E7), 0x4E8   => array(0x4E9), 0x4EA   => array(0x4EB)
1101
                    ,0x4EC   => array(0x4ED), 0x4EE   => array(0x4EF), 0x4F0   => array(0x4F1)
1102
                    ,0x4F2   => array(0x4F3), 0x4F4   => array(0x4F5), 0x4F8   => array(0x4F9)
1103
                    ,0x500   => array(0x501), 0x502   => array(0x503), 0x504   => array(0x505)
1104
                    ,0x506   => array(0x507), 0x508   => array(0x509), 0x50A   => array(0x50B)
1105
                    ,0x50C   => array(0x50D), 0x50E   => array(0x50F), 0x531   => array(0x561)
1106
                    ,0x532   => array(0x562), 0x533   => array(0x563), 0x534   => array(0x564)
1107
                    ,0x535   => array(0x565), 0x536   => array(0x566), 0x537   => array(0x567)
1108
                    ,0x538   => array(0x568), 0x539   => array(0x569), 0x53A   => array(0x56A)
1109
                    ,0x53B   => array(0x56B), 0x53C   => array(0x56C), 0x53D   => array(0x56D)
1110
                    ,0x53E   => array(0x56E), 0x53F   => array(0x56F), 0x540   => array(0x570)
1111
                    ,0x541   => array(0x571), 0x542   => array(0x572), 0x543   => array(0x573)
1112
                    ,0x544   => array(0x574), 0x545   => array(0x575), 0x546   => array(0x576)
1113
                    ,0x547   => array(0x577), 0x548   => array(0x578), 0x549   => array(0x579)
1114
                    ,0x54A   => array(0x57A), 0x54B   => array(0x57B), 0x54C   => array(0x57C)
1115
                    ,0x54D   => array(0x57D), 0x54E   => array(0x57E), 0x54F   => array(0x57F)
1116
                    ,0x550   => array(0x580), 0x551   => array(0x581), 0x552   => array(0x582)
1117
                    ,0x553   => array(0x583), 0x554   => array(0x584), 0x555   => array(0x585)
1118
                    ,0x556 => array(0x586), 0x587 => array(0x565, 0x582), 0xE33 => array(0xE4D, 0xE32)
1119
                    ,0x1E00  => array(0x1E01), 0x1E02  => array(0x1E03), 0x1E04  => array(0x1E05)
1120
                    ,0x1E06  => array(0x1E07), 0x1E08  => array(0x1E09), 0x1E0A  => array(0x1E0B)
1121
                    ,0x1E0C  => array(0x1E0D), 0x1E0E  => array(0x1E0F), 0x1E10  => array(0x1E11)
1122
                    ,0x1E12  => array(0x1E13), 0x1E14  => array(0x1E15), 0x1E16  => array(0x1E17)
1123
                    ,0x1E18  => array(0x1E19), 0x1E1A  => array(0x1E1B), 0x1E1C  => array(0x1E1D)
1124
                    ,0x1E1E  => array(0x1E1F), 0x1E20  => array(0x1E21), 0x1E22  => array(0x1E23)
1125
                    ,0x1E24  => array(0x1E25), 0x1E26  => array(0x1E27), 0x1E28  => array(0x1E29)
1126
                    ,0x1E2A  => array(0x1E2B), 0x1E2C  => array(0x1E2D), 0x1E2E  => array(0x1E2F)
1127
                    ,0x1E30  => array(0x1E31), 0x1E32  => array(0x1E33), 0x1E34  => array(0x1E35)
1128
                    ,0x1E36  => array(0x1E37), 0x1E38  => array(0x1E39), 0x1E3A  => array(0x1E3B)
1129
                    ,0x1E3C  => array(0x1E3D), 0x1E3E  => array(0x1E3F), 0x1E40  => array(0x1E41)
1130
                    ,0x1E42  => array(0x1E43), 0x1E44  => array(0x1E45), 0x1E46  => array(0x1E47)
1131
                    ,0x1E48  => array(0x1E49), 0x1E4A  => array(0x1E4B), 0x1E4C  => array(0x1E4D)
1132
                    ,0x1E4E  => array(0x1E4F), 0x1E50  => array(0x1E51), 0x1E52  => array(0x1E53)
1133
                    ,0x1E54  => array(0x1E55), 0x1E56  => array(0x1E57), 0x1E58  => array(0x1E59)
1134
                    ,0x1E5A  => array(0x1E5B), 0x1E5C  => array(0x1E5D), 0x1E5E  => array(0x1E5F)
1135
                    ,0x1E60  => array(0x1E61), 0x1E62  => array(0x1E63), 0x1E64  => array(0x1E65)
1136
                    ,0x1E66  => array(0x1E67), 0x1E68  => array(0x1E69), 0x1E6A  => array(0x1E6B)
1137
                    ,0x1E6C  => array(0x1E6D), 0x1E6E  => array(0x1E6F), 0x1E70  => array(0x1E71)
1138
                    ,0x1E72  => array(0x1E73), 0x1E74  => array(0x1E75), 0x1E76  => array(0x1E77)
1139
                    ,0x1E78  => array(0x1E79), 0x1E7A  => array(0x1E7B), 0x1E7C  => array(0x1E7D)
1140
                    ,0x1E7E  => array(0x1E7F), 0x1E80  => array(0x1E81), 0x1E82  => array(0x1E83)
1141
                    ,0x1E84  => array(0x1E85), 0x1E86  => array(0x1E87), 0x1E88  => array(0x1E89)
1142
                    ,0x1E8A  => array(0x1E8B), 0x1E8C  => array(0x1E8D), 0x1E8E  => array(0x1E8F)
1143
                    ,0x1E90  => array(0x1E91), 0x1E92  => array(0x1E93), 0x1E94  => array(0x1E95)
1144
                    ,0x1E96  => array(0x68, 0x331), 0x1E97  => array(0x74, 0x308), 0x1E98  => array(0x77, 0x30A)
1145
                    ,0x1E99  => array(0x79, 0x30A), 0x1E9A  => array(0x61, 0x2BE), 0x1E9B  => array(0x1E61)
1146
                    ,0x1EA0  => array(0x1EA1), 0x1EA2  => array(0x1EA3), 0x1EA4  => array(0x1EA5)
1147
                    ,0x1EA6  => array(0x1EA7), 0x1EA8  => array(0x1EA9), 0x1EAA  => array(0x1EAB)
1148
                    ,0x1EAC  => array(0x1EAD), 0x1EAE  => array(0x1EAF), 0x1EB0  => array(0x1EB1)
1149
                    ,0x1EB2  => array(0x1EB3), 0x1EB4  => array(0x1EB5), 0x1EB6  => array(0x1EB7)
1150
                    ,0x1EB8  => array(0x1EB9), 0x1EBA  => array(0x1EBB), 0x1EBC  => array(0x1EBD)
1151
                    ,0x1EBE  => array(0x1EBF), 0x1EC0  => array(0x1EC1), 0x1EC2  => array(0x1EC3)
1152
                    ,0x1EC4  => array(0x1EC5), 0x1EC6  => array(0x1EC7), 0x1EC8  => array(0x1EC9)
1153
                    ,0x1ECA  => array(0x1ECB), 0x1ECC  => array(0x1ECD), 0x1ECE  => array(0x1ECF)
1154
                    ,0x1ED0  => array(0x1ED1), 0x1ED2  => array(0x1ED3), 0x1ED4  => array(0x1ED5)
1155
                    ,0x1ED6  => array(0x1ED7), 0x1ED8  => array(0x1ED9), 0x1EDA  => array(0x1EDB)
1156
                    ,0x1EDC  => array(0x1EDD), 0x1EDE  => array(0x1EDF), 0x1EE0  => array(0x1EE1)
1157
                    ,0x1EE2  => array(0x1EE3), 0x1EE4  => array(0x1EE5), 0x1EE6  => array(0x1EE7)
1158
                    ,0x1EE8  => array(0x1EE9), 0x1EEA  => array(0x1EEB), 0x1EEC  => array(0x1EED)
1159
                    ,0x1EEE  => array(0x1EEF), 0x1EF0  => array(0x1EF1), 0x1EF2  => array(0x1EF3)
1160
                    ,0x1EF4  => array(0x1EF5), 0x1EF6  => array(0x1EF7), 0x1EF8  => array(0x1EF9)
1161
                    ,0x1F08  => array(0x1F00), 0x1F09  => array(0x1F01), 0x1F0A  => array(0x1F02)
1162
                    ,0x1F0B  => array(0x1F03), 0x1F0C  => array(0x1F04), 0x1F0D  => array(0x1F05)
1163
                    ,0x1F0E  => array(0x1F06), 0x1F0F  => array(0x1F07), 0x1F18  => array(0x1F10)
1164
                    ,0x1F19  => array(0x1F11), 0x1F1A  => array(0x1F12), 0x1F1B  => array(0x1F13)
1165
                    ,0x1F1C  => array(0x1F14), 0x1F1D  => array(0x1F15), 0x1F28  => array(0x1F20)
1166
                    ,0x1F29  => array(0x1F21), 0x1F2A  => array(0x1F22), 0x1F2B  => array(0x1F23)
1167
                    ,0x1F2C  => array(0x1F24), 0x1F2D  => array(0x1F25), 0x1F2E  => array(0x1F26)
1168
                    ,0x1F2F  => array(0x1F27), 0x1F38  => array(0x1F30), 0x1F39  => array(0x1F31)
1169
                    ,0x1F3A  => array(0x1F32), 0x1F3B  => array(0x1F33), 0x1F3C  => array(0x1F34)
1170
                    ,0x1F3D  => array(0x1F35), 0x1F3E  => array(0x1F36), 0x1F3F  => array(0x1F37)
1171
                    ,0x1F48  => array(0x1F40), 0x1F49  => array(0x1F41), 0x1F4A  => array(0x1F42)
1172
                    ,0x1F4B  => array(0x1F43), 0x1F4C  => array(0x1F44), 0x1F4D  => array(0x1F45)
1173
                    ,0x1F50  => array(0x3C5, 0x313), 0x1F52  => array(0x3C5, 0x313, 0x300)
1174
                    ,0x1F54  => array(0x3C5, 0x313, 0x301), 0x1F56  => array(0x3C5, 0x313, 0x342)
1175
                    ,0x1F59  => array(0x1F51), 0x1F5B  => array(0x1F53), 0x1F5D  => array(0x1F55)
1176
                    ,0x1F5F  => array(0x1F57), 0x1F68  => array(0x1F60), 0x1F69  => array(0x1F61)
1177
                    ,0x1F6A  => array(0x1F62), 0x1F6B  => array(0x1F63), 0x1F6C  => array(0x1F64)
1178
                    ,0x1F6D  => array(0x1F65), 0x1F6E  => array(0x1F66), 0x1F6F  => array(0x1F67)
1179
                    ,0x1F80  => array(0x1F00, 0x3B9), 0x1F81  => array(0x1F01, 0x3B9)
1180
                    ,0x1F82  => array(0x1F02, 0x3B9), 0x1F83  => array(0x1F03, 0x3B9)
1181
                    ,0x1F84  => array(0x1F04, 0x3B9), 0x1F85  => array(0x1F05, 0x3B9)
1182
                    ,0x1F86  => array(0x1F06, 0x3B9), 0x1F87  => array(0x1F07, 0x3B9)
1183
                    ,0x1F88  => array(0x1F00, 0x3B9), 0x1F89  => array(0x1F01, 0x3B9)
1184
                    ,0x1F8A  => array(0x1F02, 0x3B9), 0x1F8B  => array(0x1F03, 0x3B9)
1185
                    ,0x1F8C  => array(0x1F04, 0x3B9), 0x1F8D  => array(0x1F05, 0x3B9)
1186
                    ,0x1F8E  => array(0x1F06, 0x3B9), 0x1F8F  => array(0x1F07, 0x3B9)
1187
                    ,0x1F90  => array(0x1F20, 0x3B9), 0x1F91  => array(0x1F21, 0x3B9)
1188
                    ,0x1F92  => array(0x1F22, 0x3B9), 0x1F93  => array(0x1F23, 0x3B9)
1189
                    ,0x1F94  => array(0x1F24, 0x3B9), 0x1F95  => array(0x1F25, 0x3B9)
1190
                    ,0x1F96  => array(0x1F26, 0x3B9), 0x1F97  => array(0x1F27, 0x3B9)
1191
                    ,0x1F98  => array(0x1F20, 0x3B9), 0x1F99  => array(0x1F21, 0x3B9)
1192
                    ,0x1F9A  => array(0x1F22, 0x3B9), 0x1F9B  => array(0x1F23, 0x3B9)
1193
                    ,0x1F9C  => array(0x1F24, 0x3B9), 0x1F9D  => array(0x1F25, 0x3B9)
1194
                    ,0x1F9E  => array(0x1F26, 0x3B9), 0x1F9F  => array(0x1F27, 0x3B9)
1195
                    ,0x1FA0  => array(0x1F60, 0x3B9), 0x1FA1  => array(0x1F61, 0x3B9)
1196
                    ,0x1FA2  => array(0x1F62, 0x3B9), 0x1FA3  => array(0x1F63, 0x3B9)
1197
                    ,0x1FA4  => array(0x1F64, 0x3B9), 0x1FA5  => array(0x1F65, 0x3B9)
1198
                    ,0x1FA6  => array(0x1F66, 0x3B9), 0x1FA7  => array(0x1F67, 0x3B9)
1199
                    ,0x1FA8  => array(0x1F60, 0x3B9), 0x1FA9  => array(0x1F61, 0x3B9)
1200
                    ,0x1FAA  => array(0x1F62, 0x3B9), 0x1FAB  => array(0x1F63, 0x3B9)
1201
                    ,0x1FAC  => array(0x1F64, 0x3B9), 0x1FAD  => array(0x1F65, 0x3B9)
1202
                    ,0x1FAE  => array(0x1F66, 0x3B9), 0x1FAF  => array(0x1F67, 0x3B9)
1203
                    ,0x1FB2  => array(0x1F70, 0x3B9), 0x1FB3  => array(0x3B1, 0x3B9)
1204
                    ,0x1FB4  => array(0x3AC, 0x3B9), 0x1FB6  => array(0x3B1, 0x342)
1205
                    ,0x1FB7  => array(0x3B1, 0x342, 0x3B9), 0x1FB8  => array(0x1FB0)
1206
                    ,0x1FB9  => array(0x1FB1), 0x1FBA  => array(0x1F70), 0x1FBB  => array(0x1F71)
1207
                    ,0x1FBC  => array(0x3B1, 0x3B9), 0x1FBE  => array(0x3B9)
1208
                    ,0x1FC2  => array(0x1F74, 0x3B9), 0x1FC3  => array(0x3B7, 0x3B9)
1209
                    ,0x1FC4  => array(0x3AE, 0x3B9), 0x1FC6  => array(0x3B7, 0x342)
1210
                    ,0x1FC7  => array(0x3B7, 0x342, 0x3B9), 0x1FC8  => array(0x1F72)
1211
                    ,0x1FC9  => array(0x1F73), 0x1FCA  => array(0x1F74), 0x1FCB  => array(0x1F75)
1212
                    ,0x1FCC  => array(0x3B7, 0x3B9), 0x1FD2  => array(0x3B9, 0x308, 0x300)
1213
                    ,0x1FD3  => array(0x3B9, 0x308, 0x301), 0x1FD6  => array(0x3B9, 0x342)
1214
                    ,0x1FD7  => array(0x3B9, 0x308, 0x342), 0x1FD8  => array(0x1FD0)
1215
                    ,0x1FD9  => array(0x1FD1), 0x1FDA  => array(0x1F76)
1216
                    ,0x1FDB  => array(0x1F77), 0x1FE2  => array(0x3C5, 0x308, 0x300)
1217
                    ,0x1FE3  => array(0x3C5, 0x308, 0x301), 0x1FE4  => array(0x3C1, 0x313)
1218
                    ,0x1FE6  => array(0x3C5, 0x342), 0x1FE7  => array(0x3C5, 0x308, 0x342)
1219
                    ,0x1FE8  => array(0x1FE0), 0x1FE9  => array(0x1FE1)
1220
                    ,0x1FEA  => array(0x1F7A), 0x1FEB  => array(0x1F7B)
1221
                    ,0x1FEC  => array(0x1FE5), 0x1FF2  => array(0x1F7C, 0x3B9)
1222
                    ,0x1FF3  => array(0x3C9, 0x3B9), 0x1FF4  => array(0x3CE, 0x3B9)
1223
                    ,0x1FF6  => array(0x3C9, 0x342), 0x1FF7  => array(0x3C9, 0x342, 0x3B9)
1224
                    ,0x1FF8  => array(0x1F78), 0x1FF9  => array(0x1F79), 0x1FFA  => array(0x1F7C)
1225
                    ,0x1FFB  => array(0x1F7D), 0x1FFC  => array(0x3C9, 0x3B9)
1226
                    ,0x20A8  => array(0x72, 0x73), 0x2102  => array(0x63), 0x2103  => array(0xB0, 0x63)
1227
                    ,0x2107  => array(0x25B), 0x2109  => array(0xB0, 0x66), 0x210B  => array(0x68)
1228
                    ,0x210C  => array(0x68), 0x210D  => array(0x68), 0x2110  => array(0x69)
1229
                    ,0x2111  => array(0x69), 0x2112  => array(0x6C), 0x2115  => array(0x6E)
1230
                    ,0x2116  => array(0x6E, 0x6F), 0x2119  => array(0x70), 0x211A  => array(0x71)
1231
                    ,0x211B  => array(0x72), 0x211C  => array(0x72), 0x211D  => array(0x72)
1232
                    ,0x2120  => array(0x73, 0x6D), 0x2121  => array(0x74, 0x65, 0x6C)
1233
                    ,0x2122  => array(0x74, 0x6D), 0x2124  => array(0x7A), 0x2126  => array(0x3C9)
1234
                    ,0x2128  => array(0x7A), 0x212A  => array(0x6B), 0x212B  => array(0xE5)
1235
                    ,0x212C  => array(0x62), 0x212D  => array(0x63), 0x2130  => array(0x65)
1236
                    ,0x2131  => array(0x66), 0x2133  => array(0x6D), 0x213E  => array(0x3B3)
1237
                    ,0x213F  => array(0x3C0), 0x2145  => array(0x64) ,0x2160  => array(0x2170)
1238
                    ,0x2161  => array(0x2171), 0x2162  => array(0x2172), 0x2163  => array(0x2173)
1239
                    ,0x2164  => array(0x2174), 0x2165  => array(0x2175), 0x2166  => array(0x2176)
1240
                    ,0x2167  => array(0x2177), 0x2168  => array(0x2178), 0x2169  => array(0x2179)
1241
                    ,0x216A  => array(0x217A), 0x216B  => array(0x217B), 0x216C  => array(0x217C)
1242
                    ,0x216D  => array(0x217D), 0x216E  => array(0x217E), 0x216F  => array(0x217F)
1243
                    ,0x24B6  => array(0x24D0), 0x24B7  => array(0x24D1), 0x24B8  => array(0x24D2)
1244
                    ,0x24B9  => array(0x24D3), 0x24BA  => array(0x24D4), 0x24BB  => array(0x24D5)
1245
                    ,0x24BC  => array(0x24D6), 0x24BD  => array(0x24D7), 0x24BE  => array(0x24D8)
1246
                    ,0x24BF  => array(0x24D9), 0x24C0  => array(0x24DA), 0x24C1  => array(0x24DB)
1247
                    ,0x24C2  => array(0x24DC), 0x24C3  => array(0x24DD), 0x24C4  => array(0x24DE)
1248
                    ,0x24C5  => array(0x24DF), 0x24C6  => array(0x24E0), 0x24C7  => array(0x24E1)
1249
                    ,0x24C8  => array(0x24E2), 0x24C9  => array(0x24E3), 0x24CA  => array(0x24E4)
1250
                    ,0x24CB  => array(0x24E5), 0x24CC  => array(0x24E6), 0x24CD  => array(0x24E7)
1251
                    ,0x24CE  => array(0x24E8), 0x24CF  => array(0x24E9), 0x3371  => array(0x68, 0x70, 0x61)
1252
                    ,0x3373  => array(0x61, 0x75), 0x3375  => array(0x6F, 0x76)
1253
                    ,0x3380  => array(0x70, 0x61), 0x3381  => array(0x6E, 0x61)
1254
                    ,0x3382  => array(0x3BC, 0x61), 0x3383  => array(0x6D, 0x61)
1255
                    ,0x3384  => array(0x6B, 0x61), 0x3385  => array(0x6B, 0x62)
1256
                    ,0x3386  => array(0x6D, 0x62), 0x3387  => array(0x67, 0x62)
1257
                    ,0x338A  => array(0x70, 0x66), 0x338B  => array(0x6E, 0x66)
1258
                    ,0x338C  => array(0x3BC, 0x66), 0x3390  => array(0x68, 0x7A)
1259
                    ,0x3391  => array(0x6B, 0x68, 0x7A), 0x3392  => array(0x6D, 0x68, 0x7A)
1260
                    ,0x3393  => array(0x67, 0x68, 0x7A), 0x3394  => array(0x74, 0x68, 0x7A)
1261
                    ,0x33A9  => array(0x70, 0x61), 0x33AA  => array(0x6B, 0x70, 0x61)
1262
                    ,0x33AB  => array(0x6D, 0x70, 0x61), 0x33AC  => array(0x67, 0x70, 0x61)
1263
                    ,0x33B4  => array(0x70, 0x76), 0x33B5  => array(0x6E, 0x76)
1264
                    ,0x33B6  => array(0x3BC, 0x76), 0x33B7  => array(0x6D, 0x76)
1265
                    ,0x33B8  => array(0x6B, 0x76), 0x33B9  => array(0x6D, 0x76)
1266
                    ,0x33BA  => array(0x70, 0x77), 0x33BB  => array(0x6E, 0x77)
1267
                    ,0x33BC  => array(0x3BC, 0x77), 0x33BD  => array(0x6D, 0x77)
1268
                    ,0x33BE  => array(0x6B, 0x77), 0x33BF  => array(0x6D, 0x77)
1269
                    ,0x33C0  => array(0x6B, 0x3C9), 0x33C1  => array(0x6D, 0x3C9) /*
1270
                    ,0x33C2  => array(0x61, 0x2E, 0x6D, 0x2E) */
1271
                    ,0x33C3  => array(0x62, 0x71), 0x33C6  => array(0x63, 0x2215, 0x6B, 0x67)
1272
                    ,0x33C7  => array(0x63, 0x6F, 0x2E), 0x33C8  => array(0x64, 0x62)
1273
                    ,0x33C9  => array(0x67, 0x79), 0x33CB  => array(0x68, 0x70)
1274
                    ,0x33CD  => array(0x6B, 0x6B), 0x33CE  => array(0x6B, 0x6D)
1275
                    ,0x33D7  => array(0x70, 0x68), 0x33D9  => array(0x70, 0x70, 0x6D)
1276
                    ,0x33DA  => array(0x70, 0x72), 0x33DC  => array(0x73, 0x76)
1277
                    ,0x33DD  => array(0x77, 0x62), 0xFB00  => array(0x66, 0x66)
1278
                    ,0xFB01  => array(0x66, 0x69), 0xFB02  => array(0x66, 0x6C)
1279
                    ,0xFB03  => array(0x66, 0x66, 0x69), 0xFB04  => array(0x66, 0x66, 0x6C)
1280
                    ,0xFB05  => array(0x73, 0x74), 0xFB06  => array(0x73, 0x74)
1281
                    ,0xFB13  => array(0x574, 0x576), 0xFB14  => array(0x574, 0x565)
1282
                    ,0xFB15  => array(0x574, 0x56B), 0xFB16  => array(0x57E, 0x576)
1283
                    ,0xFB17  => array(0x574, 0x56D), 0xFF21  => array(0xFF41)
1284
                    ,0xFF22  => array(0xFF42), 0xFF23  => array(0xFF43), 0xFF24  => array(0xFF44)
1285
                    ,0xFF25  => array(0xFF45), 0xFF26  => array(0xFF46), 0xFF27  => array(0xFF47)
1286
                    ,0xFF28  => array(0xFF48), 0xFF29  => array(0xFF49), 0xFF2A  => array(0xFF4A)
1287
                    ,0xFF2B  => array(0xFF4B), 0xFF2C  => array(0xFF4C), 0xFF2D  => array(0xFF4D)
1288
                    ,0xFF2E  => array(0xFF4E), 0xFF2F  => array(0xFF4F), 0xFF30  => array(0xFF50)
1289
                    ,0xFF31  => array(0xFF51), 0xFF32  => array(0xFF52), 0xFF33  => array(0xFF53)
1290
                    ,0xFF34  => array(0xFF54), 0xFF35  => array(0xFF55), 0xFF36  => array(0xFF56)
1291
                    ,0xFF37  => array(0xFF57), 0xFF38  => array(0xFF58), 0xFF39  => array(0xFF59)
1292
                    ,0xFF3A  => array(0xFF5A), 0x10400 => array(0x10428), 0x10401 => array(0x10429)
1293
                    ,0x10402 => array(0x1042A), 0x10403 => array(0x1042B), 0x10404 => array(0x1042C)
1294
                    ,0x10405 => array(0x1042D), 0x10406 => array(0x1042E), 0x10407 => array(0x1042F)
1295
                    ,0x10408 => array(0x10430), 0x10409 => array(0x10431), 0x1040A => array(0x10432)
1296
                    ,0x1040B => array(0x10433), 0x1040C => array(0x10434), 0x1040D => array(0x10435)
1297
                    ,0x1040E => array(0x10436), 0x1040F => array(0x10437), 0x10410 => array(0x10438)
1298
                    ,0x10411 => array(0x10439), 0x10412 => array(0x1043A), 0x10413 => array(0x1043B)
1299
                    ,0x10414 => array(0x1043C), 0x10415 => array(0x1043D), 0x10416 => array(0x1043E)
1300
                    ,0x10417 => array(0x1043F), 0x10418 => array(0x10440), 0x10419 => array(0x10441)
1301
                    ,0x1041A => array(0x10442), 0x1041B => array(0x10443), 0x1041C => array(0x10444)
1302
                    ,0x1041D => array(0x10445), 0x1041E => array(0x10446), 0x1041F => array(0x10447)
1303
                    ,0x10420 => array(0x10448), 0x10421 => array(0x10449), 0x10422 => array(0x1044A)
1304
                    ,0x10423 => array(0x1044B), 0x10424 => array(0x1044C), 0x10425 => array(0x1044D)
1305
                    ,0x1D400 => array(0x61), 0x1D401 => array(0x62), 0x1D402 => array(0x63)
1306
                    ,0x1D403 => array(0x64), 0x1D404 => array(0x65), 0x1D405 => array(0x66)
1307
                    ,0x1D406 => array(0x67), 0x1D407 => array(0x68), 0x1D408 => array(0x69)
1308
                    ,0x1D409 => array(0x6A), 0x1D40A => array(0x6B), 0x1D40B => array(0x6C)
1309
                    ,0x1D40C => array(0x6D), 0x1D40D => array(0x6E), 0x1D40E => array(0x6F)
1310
                    ,0x1D40F => array(0x70), 0x1D410 => array(0x71), 0x1D411 => array(0x72)
1311
                    ,0x1D412 => array(0x73), 0x1D413 => array(0x74), 0x1D414 => array(0x75)
1312
                    ,0x1D415 => array(0x76), 0x1D416 => array(0x77), 0x1D417 => array(0x78)
1313
                    ,0x1D418 => array(0x79), 0x1D419 => array(0x7A), 0x1D434 => array(0x61)
1314
                    ,0x1D435 => array(0x62), 0x1D436 => array(0x63), 0x1D437 => array(0x64)
1315
                    ,0x1D438 => array(0x65), 0x1D439 => array(0x66), 0x1D43A => array(0x67)
1316
                    ,0x1D43B => array(0x68), 0x1D43C => array(0x69), 0x1D43D => array(0x6A)
1317
                    ,0x1D43E => array(0x6B), 0x1D43F => array(0x6C), 0x1D440 => array(0x6D)
1318
                    ,0x1D441 => array(0x6E), 0x1D442 => array(0x6F), 0x1D443 => array(0x70)
1319
                    ,0x1D444 => array(0x71), 0x1D445 => array(0x72), 0x1D446 => array(0x73)
1320
                    ,0x1D447 => array(0x74), 0x1D448 => array(0x75), 0x1D449 => array(0x76)
1321
                    ,0x1D44A => array(0x77), 0x1D44B => array(0x78), 0x1D44C => array(0x79)
1322
                    ,0x1D44D => array(0x7A), 0x1D468 => array(0x61), 0x1D469 => array(0x62)
1323
                    ,0x1D46A => array(0x63), 0x1D46B => array(0x64), 0x1D46C => array(0x65)
1324
                    ,0x1D46D => array(0x66), 0x1D46E => array(0x67), 0x1D46F => array(0x68)
1325
                    ,0x1D470 => array(0x69), 0x1D471 => array(0x6A), 0x1D472 => array(0x6B)
1326
                    ,0x1D473 => array(0x6C), 0x1D474 => array(0x6D), 0x1D475 => array(0x6E)
1327
                    ,0x1D476 => array(0x6F), 0x1D477 => array(0x70), 0x1D478 => array(0x71)
1328
                    ,0x1D479 => array(0x72), 0x1D47A => array(0x73), 0x1D47B => array(0x74)
1329
                    ,0x1D47C => array(0x75), 0x1D47D => array(0x76), 0x1D47E => array(0x77)
1330
                    ,0x1D47F => array(0x78), 0x1D480 => array(0x79), 0x1D481 => array(0x7A)
1331
                    ,0x1D49C => array(0x61), 0x1D49E => array(0x63), 0x1D49F => array(0x64)
1332
                    ,0x1D4A2 => array(0x67), 0x1D4A5 => array(0x6A), 0x1D4A6 => array(0x6B)
1333
                    ,0x1D4A9 => array(0x6E), 0x1D4AA => array(0x6F), 0x1D4AB => array(0x70)
1334
                    ,0x1D4AC => array(0x71), 0x1D4AE => array(0x73), 0x1D4AF => array(0x74)
1335
                    ,0x1D4B0 => array(0x75), 0x1D4B1 => array(0x76), 0x1D4B2 => array(0x77)
1336
                    ,0x1D4B3 => array(0x78), 0x1D4B4 => array(0x79), 0x1D4B5 => array(0x7A)
1337
                    ,0x1D4D0 => array(0x61), 0x1D4D1 => array(0x62), 0x1D4D2 => array(0x63)
1338
                    ,0x1D4D3 => array(0x64), 0x1D4D4 => array(0x65), 0x1D4D5 => array(0x66)
1339
                    ,0x1D4D6 => array(0x67), 0x1D4D7 => array(0x68), 0x1D4D8 => array(0x69)
1340
                    ,0x1D4D9 => array(0x6A), 0x1D4DA => array(0x6B), 0x1D4DB => array(0x6C)
1341
                    ,0x1D4DC => array(0x6D), 0x1D4DD => array(0x6E), 0x1D4DE => array(0x6F)
1342
                    ,0x1D4DF => array(0x70), 0x1D4E0 => array(0x71), 0x1D4E1 => array(0x72)
1343
                    ,0x1D4E2 => array(0x73), 0x1D4E3 => array(0x74), 0x1D4E4 => array(0x75)
1344
                    ,0x1D4E5 => array(0x76), 0x1D4E6 => array(0x77), 0x1D4E7 => array(0x78)
1345
                    ,0x1D4E8 => array(0x79), 0x1D4E9 => array(0x7A), 0x1D504 => array(0x61)
1346
                    ,0x1D505 => array(0x62), 0x1D507 => array(0x64), 0x1D508 => array(0x65)
1347
                    ,0x1D509 => array(0x66), 0x1D50A => array(0x67), 0x1D50D => array(0x6A)
1348
                    ,0x1D50E => array(0x6B), 0x1D50F => array(0x6C), 0x1D510 => array(0x6D)
1349
                    ,0x1D511 => array(0x6E), 0x1D512 => array(0x6F), 0x1D513 => array(0x70)
1350
                    ,0x1D514 => array(0x71), 0x1D516 => array(0x73), 0x1D517 => array(0x74)
1351
                    ,0x1D518 => array(0x75), 0x1D519 => array(0x76), 0x1D51A => array(0x77)
1352
                    ,0x1D51B => array(0x78), 0x1D51C => array(0x79), 0x1D538 => array(0x61)
1353
                    ,0x1D539 => array(0x62), 0x1D53B => array(0x64), 0x1D53C => array(0x65)
1354
                    ,0x1D53D => array(0x66), 0x1D53E => array(0x67), 0x1D540 => array(0x69)
1355
                    ,0x1D541 => array(0x6A), 0x1D542 => array(0x6B), 0x1D543 => array(0x6C)
1356
                    ,0x1D544 => array(0x6D), 0x1D546 => array(0x6F), 0x1D54A => array(0x73)
1357
                    ,0x1D54B => array(0x74), 0x1D54C => array(0x75), 0x1D54D => array(0x76)
1358
                    ,0x1D54E => array(0x77), 0x1D54F => array(0x78), 0x1D550 => array(0x79)
1359
                    ,0x1D56C => array(0x61), 0x1D56D => array(0x62), 0x1D56E => array(0x63)
1360
                    ,0x1D56F => array(0x64), 0x1D570 => array(0x65), 0x1D571 => array(0x66)
1361
                    ,0x1D572 => array(0x67), 0x1D573 => array(0x68), 0x1D574 => array(0x69)
1362
                    ,0x1D575 => array(0x6A), 0x1D576 => array(0x6B), 0x1D577 => array(0x6C)
1363
                    ,0x1D578 => array(0x6D), 0x1D579 => array(0x6E), 0x1D57A => array(0x6F)
1364
                    ,0x1D57B => array(0x70), 0x1D57C => array(0x71), 0x1D57D => array(0x72)
1365
                    ,0x1D57E => array(0x73), 0x1D57F => array(0x74), 0x1D580 => array(0x75)
1366
                    ,0x1D581 => array(0x76), 0x1D582 => array(0x77), 0x1D583 => array(0x78)
1367
                    ,0x1D584 => array(0x79), 0x1D585 => array(0x7A), 0x1D5A0 => array(0x61)
1368
                    ,0x1D5A1 => array(0x62), 0x1D5A2 => array(0x63), 0x1D5A3 => array(0x64)
1369
                    ,0x1D5A4 => array(0x65), 0x1D5A5 => array(0x66), 0x1D5A6 => array(0x67)
1370
                    ,0x1D5A7 => array(0x68), 0x1D5A8 => array(0x69), 0x1D5A9 => array(0x6A)
1371
                    ,0x1D5AA => array(0x6B), 0x1D5AB => array(0x6C), 0x1D5AC => array(0x6D)
1372
                    ,0x1D5AD => array(0x6E), 0x1D5AE => array(0x6F), 0x1D5AF => array(0x70)
1373
                    ,0x1D5B0 => array(0x71), 0x1D5B1 => array(0x72), 0x1D5B2 => array(0x73)
1374
                    ,0x1D5B3 => array(0x74), 0x1D5B4 => array(0x75), 0x1D5B5 => array(0x76)
1375
                    ,0x1D5B6 => array(0x77), 0x1D5B7 => array(0x78), 0x1D5B8 => array(0x79)
1376
                    ,0x1D5B9 => array(0x7A), 0x1D5D4 => array(0x61), 0x1D5D5 => array(0x62)
1377
                    ,0x1D5D6 => array(0x63), 0x1D5D7 => array(0x64), 0x1D5D8 => array(0x65)
1378
                    ,0x1D5D9 => array(0x66), 0x1D5DA => array(0x67), 0x1D5DB => array(0x68)
1379
                    ,0x1D5DC => array(0x69), 0x1D5DD => array(0x6A), 0x1D5DE => array(0x6B)
1380
                    ,0x1D5DF => array(0x6C), 0x1D5E0 => array(0x6D), 0x1D5E1 => array(0x6E)
1381
                    ,0x1D5E2 => array(0x6F), 0x1D5E3 => array(0x70), 0x1D5E4 => array(0x71)
1382
                    ,0x1D5E5 => array(0x72), 0x1D5E6 => array(0x73), 0x1D5E7 => array(0x74)
1383
                    ,0x1D5E8 => array(0x75), 0x1D5E9 => array(0x76), 0x1D5EA => array(0x77)
1384
                    ,0x1D5EB => array(0x78), 0x1D5EC => array(0x79), 0x1D5ED => array(0x7A)
1385
                    ,0x1D608 => array(0x61), 0x1D609 => array(0x62) ,0x1D60A => array(0x63)
1386
                    ,0x1D60B => array(0x64), 0x1D60C => array(0x65), 0x1D60D => array(0x66)
1387
                    ,0x1D60E => array(0x67), 0x1D60F => array(0x68), 0x1D610 => array(0x69)
1388
                    ,0x1D611 => array(0x6A), 0x1D612 => array(0x6B), 0x1D613 => array(0x6C)
1389
                    ,0x1D614 => array(0x6D), 0x1D615 => array(0x6E), 0x1D616 => array(0x6F)
1390
                    ,0x1D617 => array(0x70), 0x1D618 => array(0x71), 0x1D619 => array(0x72)
1391
                    ,0x1D61A => array(0x73), 0x1D61B => array(0x74), 0x1D61C => array(0x75)
1392
                    ,0x1D61D => array(0x76), 0x1D61E => array(0x77), 0x1D61F => array(0x78)
1393
                    ,0x1D620 => array(0x79), 0x1D621 => array(0x7A), 0x1D63C => array(0x61)
1394
                    ,0x1D63D => array(0x62), 0x1D63E => array(0x63), 0x1D63F => array(0x64)
1395
                    ,0x1D640 => array(0x65), 0x1D641 => array(0x66), 0x1D642 => array(0x67)
1396
                    ,0x1D643 => array(0x68), 0x1D644 => array(0x69), 0x1D645 => array(0x6A)
1397
                    ,0x1D646 => array(0x6B), 0x1D647 => array(0x6C), 0x1D648 => array(0x6D)
1398
                    ,0x1D649 => array(0x6E), 0x1D64A => array(0x6F), 0x1D64B => array(0x70)
1399
                    ,0x1D64C => array(0x71), 0x1D64D => array(0x72), 0x1D64E => array(0x73)
1400
                    ,0x1D64F => array(0x74), 0x1D650 => array(0x75), 0x1D651 => array(0x76)
1401
                    ,0x1D652 => array(0x77), 0x1D653 => array(0x78), 0x1D654 => array(0x79)
1402
                    ,0x1D655 => array(0x7A), 0x1D670 => array(0x61), 0x1D671 => array(0x62)
1403
                    ,0x1D672 => array(0x63), 0x1D673 => array(0x64), 0x1D674 => array(0x65)
1404
                    ,0x1D675 => array(0x66), 0x1D676 => array(0x67), 0x1D677 => array(0x68)
1405
                    ,0x1D678 => array(0x69), 0x1D679 => array(0x6A), 0x1D67A => array(0x6B)
1406
                    ,0x1D67B => array(0x6C), 0x1D67C => array(0x6D), 0x1D67D => array(0x6E)
1407
                    ,0x1D67E => array(0x6F), 0x1D67F => array(0x70), 0x1D680 => array(0x71)
1408
                    ,0x1D681 => array(0x72), 0x1D682 => array(0x73), 0x1D683 => array(0x74)
1409
                    ,0x1D684 => array(0x75), 0x1D685 => array(0x76), 0x1D686 => array(0x77)
1410
                    ,0x1D687 => array(0x78), 0x1D688 => array(0x79), 0x1D689 => array(0x7A)
1411
                    ,0x1D6A8 => array(0x3B1), 0x1D6A9 => array(0x3B2), 0x1D6AA => array(0x3B3)
1412
                    ,0x1D6AB => array(0x3B4), 0x1D6AC => array(0x3B5), 0x1D6AD => array(0x3B6)
1413
                    ,0x1D6AE => array(0x3B7), 0x1D6AF => array(0x3B8), 0x1D6B0 => array(0x3B9)
1414
                    ,0x1D6B1 => array(0x3BA), 0x1D6B2 => array(0x3BB), 0x1D6B3 => array(0x3BC)
1415
                    ,0x1D6B4 => array(0x3BD), 0x1D6B5 => array(0x3BE), 0x1D6B6 => array(0x3BF)
1416
                    ,0x1D6B7 => array(0x3C0), 0x1D6B8 => array(0x3C1), 0x1D6B9 => array(0x3B8)
1417
                    ,0x1D6BA => array(0x3C3), 0x1D6BB => array(0x3C4), 0x1D6BC => array(0x3C5)
1418
                    ,0x1D6BD => array(0x3C6), 0x1D6BE => array(0x3C7), 0x1D6BF => array(0x3C8)
1419
                    ,0x1D6C0 => array(0x3C9), 0x1D6D3 => array(0x3C3), 0x1D6E2 => array(0x3B1)
1420
                    ,0x1D6E3 => array(0x3B2), 0x1D6E4 => array(0x3B3), 0x1D6E5 => array(0x3B4)
1421
                    ,0x1D6E6 => array(0x3B5), 0x1D6E7 => array(0x3B6), 0x1D6E8 => array(0x3B7)
1422
                    ,0x1D6E9 => array(0x3B8), 0x1D6EA => array(0x3B9), 0x1D6EB => array(0x3BA)
1423
                    ,0x1D6EC => array(0x3BB), 0x1D6ED => array(0x3BC), 0x1D6EE => array(0x3BD)
1424
                    ,0x1D6EF => array(0x3BE), 0x1D6F0 => array(0x3BF), 0x1D6F1 => array(0x3C0)
1425
                    ,0x1D6F2 => array(0x3C1), 0x1D6F3 => array(0x3B8) ,0x1D6F4 => array(0x3C3)
1426
                    ,0x1D6F5 => array(0x3C4), 0x1D6F6 => array(0x3C5), 0x1D6F7 => array(0x3C6)
1427
                    ,0x1D6F8 => array(0x3C7), 0x1D6F9 => array(0x3C8) ,0x1D6FA => array(0x3C9)
1428
                    ,0x1D70D => array(0x3C3), 0x1D71C => array(0x3B1), 0x1D71D => array(0x3B2)
1429
                    ,0x1D71E => array(0x3B3), 0x1D71F => array(0x3B4), 0x1D720 => array(0x3B5)
1430
                    ,0x1D721 => array(0x3B6), 0x1D722 => array(0x3B7), 0x1D723 => array(0x3B8)
1431
                    ,0x1D724 => array(0x3B9), 0x1D725 => array(0x3BA), 0x1D726 => array(0x3BB)
1432
                    ,0x1D727 => array(0x3BC), 0x1D728 => array(0x3BD), 0x1D729 => array(0x3BE)
1433
                    ,0x1D72A => array(0x3BF), 0x1D72B => array(0x3C0), 0x1D72C => array(0x3C1)
1434
                    ,0x1D72D => array(0x3B8), 0x1D72E => array(0x3C3), 0x1D72F => array(0x3C4)
1435
                    ,0x1D730 => array(0x3C5), 0x1D731 => array(0x3C6), 0x1D732 => array(0x3C7)
1436
                    ,0x1D733 => array(0x3C8), 0x1D734 => array(0x3C9), 0x1D747 => array(0x3C3)
1437
                    ,0x1D756 => array(0x3B1), 0x1D757 => array(0x3B2), 0x1D758 => array(0x3B3)
1438
                    ,0x1D759 => array(0x3B4), 0x1D75A => array(0x3B5), 0x1D75B => array(0x3B6)
1439
                    ,0x1D75C => array(0x3B7), 0x1D75D => array(0x3B8), 0x1D75E => array(0x3B9)
1440
                    ,0x1D75F => array(0x3BA), 0x1D760 => array(0x3BB), 0x1D761 => array(0x3BC)
1441
                    ,0x1D762 => array(0x3BD), 0x1D763 => array(0x3BE), 0x1D764 => array(0x3BF)
1442
                    ,0x1D765 => array(0x3C0), 0x1D766 => array(0x3C1), 0x1D767 => array(0x3B8)
1443
                    ,0x1D768 => array(0x3C3), 0x1D769 => array(0x3C4), 0x1D76A => array(0x3C5)
1444
                    ,0x1D76B => array(0x3C6), 0x1D76C => array(0x3C7), 0x1D76D => array(0x3C8)
1445
                    ,0x1D76E => array(0x3C9), 0x1D781 => array(0x3C3), 0x1D790 => array(0x3B1)
1446
                    ,0x1D791 => array(0x3B2), 0x1D792 => array(0x3B3), 0x1D793 => array(0x3B4)
1447
                    ,0x1D794 => array(0x3B5), 0x1D795 => array(0x3B6), 0x1D796 => array(0x3B7)
1448
                    ,0x1D797 => array(0x3B8), 0x1D798 => array(0x3B9), 0x1D799 => array(0x3BA)
1449
                    ,0x1D79A => array(0x3BB), 0x1D79B => array(0x3BC), 0x1D79C => array(0x3BD)
1450
                    ,0x1D79D => array(0x3BE), 0x1D79E => array(0x3BF), 0x1D79F => array(0x3C0)
1451
                    ,0x1D7A0 => array(0x3C1), 0x1D7A1 => array(0x3B8), 0x1D7A2 => array(0x3C3)
1452
                    ,0x1D7A3 => array(0x3C4), 0x1D7A4 => array(0x3C5), 0x1D7A5 => array(0x3C6)
1453
                    ,0x1D7A6 => array(0x3C7), 0x1D7A7 => array(0x3C8), 0x1D7A8 => array(0x3C9)
1454
                    ,0x1D7BB => array(0x3C3), 0x3F9   => array(0x3C3), 0x1D2C  => array(0x61)
1455
                    ,0x1D2D  => array(0xE6), 0x1D2E  => array(0x62), 0x1D30  => array(0x64)
1456
                    ,0x1D31  => array(0x65), 0x1D32  => array(0x1DD), 0x1D33  => array(0x67)
1457
                    ,0x1D34  => array(0x68), 0x1D35  => array(0x69), 0x1D36  => array(0x6A)
1458
                    ,0x1D37  => array(0x6B), 0x1D38  => array(0x6C), 0x1D39  => array(0x6D)
1459
                    ,0x1D3A  => array(0x6E), 0x1D3C  => array(0x6F), 0x1D3D  => array(0x223)
1460
                    ,0x1D3E  => array(0x70), 0x1D3F  => array(0x72), 0x1D40  => array(0x74)
1461
                    ,0x1D41  => array(0x75), 0x1D42  => array(0x77), 0x213B  => array(0x66, 0x61, 0x78)
1462
                    ,0x3250  => array(0x70, 0x74, 0x65), 0x32CC  => array(0x68, 0x67)
1463
                    ,0x32CE  => array(0x65, 0x76), 0x32CF  => array(0x6C, 0x74, 0x64)
1464
                    ,0x337A  => array(0x69, 0x75), 0x33DE  => array(0x76, 0x2215, 0x6D)
1465
                    ,0x33DF  => array(0x61, 0x2215, 0x6D)
1466
                    )
1467
            ,'norm_combcls' => array(0x334   => 1,   0x335   => 1,   0x336   => 1,   0x337   => 1
1468
                    ,0x338   => 1,   0x93C   => 7,   0x9BC   => 7,   0xA3C   => 7,   0xABC   => 7
1469
                    ,0xB3C   => 7,   0xCBC   => 7,   0x1037  => 7,   0x3099  => 8,   0x309A  => 8
1470
                    ,0x94D   => 9,   0x9CD   => 9,   0xA4D   => 9,   0xACD   => 9,   0xB4D   => 9
1471
                    ,0xBCD   => 9,   0xC4D   => 9,   0xCCD   => 9,   0xD4D   => 9,   0xDCA   => 9
1472
                    ,0xE3A   => 9,   0xF84   => 9,   0x1039  => 9,   0x1714  => 9,   0x1734  => 9
1473
                    ,0x17D2  => 9,   0x5B0   => 10,  0x5B1   => 11,  0x5B2   => 12,  0x5B3   => 13
1474
                    ,0x5B4   => 14,  0x5B5   => 15,  0x5B6   => 16,  0x5B7   => 17,  0x5B8   => 18
1475
                    ,0x5B9   => 19,  0x5BB   => 20,  0x5Bc   => 21,  0x5BD   => 22,  0x5BF   => 23
1476
                    ,0x5C1   => 24,  0x5C2   => 25,  0xFB1E  => 26,  0x64B   => 27,  0x64C   => 28
1477
                    ,0x64D   => 29,  0x64E   => 30,  0x64F   => 31,  0x650   => 32,  0x651   => 33
1478
                    ,0x652   => 34,  0x670   => 35,  0x711   => 36,  0xC55   => 84,  0xC56   => 91
1479
                    ,0xE38   => 103, 0xE39   => 103, 0xE48   => 107, 0xE49   => 107, 0xE4A   => 107
1480
                    ,0xE4B   => 107, 0xEB8   => 118, 0xEB9   => 118, 0xEC8   => 122, 0xEC9   => 122
1481
                    ,0xECA   => 122, 0xECB   => 122, 0xF71   => 129, 0xF72   => 130, 0xF7A   => 130
1482
                    ,0xF7B   => 130, 0xF7C   => 130, 0xF7D   => 130, 0xF80   => 130, 0xF74   => 132
1483
                    ,0x321   => 202, 0x322   => 202, 0x327   => 202, 0x328   => 202, 0x31B   => 216
1484
                    ,0xF39   => 216, 0x1D165 => 216, 0x1D166 => 216, 0x1D16E => 216, 0x1D16F => 216
1485
                    ,0x1D170 => 216, 0x1D171 => 216, 0x1D172 => 216, 0x302A  => 218, 0x316   => 220
1486
                    ,0x317   => 220, 0x318   => 220, 0x319   => 220, 0x31C   => 220, 0x31D   => 220
1487
                    ,0x31E   => 220, 0x31F   => 220, 0x320   => 220, 0x323   => 220, 0x324   => 220
1488
                    ,0x325   => 220, 0x326   => 220, 0x329   => 220, 0x32A   => 220, 0x32B   => 220
1489
                    ,0x32C   => 220, 0x32D   => 220, 0x32E   => 220, 0x32F   => 220, 0x330   => 220
1490
                    ,0x331   => 220, 0x332   => 220, 0x333   => 220, 0x339   => 220, 0x33A   => 220
1491
                    ,0x33B   => 220, 0x33C   => 220, 0x347   => 220, 0x348   => 220, 0x349   => 220
1492
                    ,0x34D   => 220, 0x34E   => 220, 0x353   => 220, 0x354   => 220, 0x355   => 220
1493
                    ,0x356   => 220, 0x591   => 220, 0x596   => 220, 0x59B   => 220, 0x5A3   => 220
1494
                    ,0x5A4   => 220, 0x5A5   => 220, 0x5A6   => 220, 0x5A7   => 220, 0x5AA   => 220
1495
                    ,0x655   => 220, 0x656   => 220, 0x6E3   => 220, 0x6EA   => 220, 0x6ED   => 220
1496
                    ,0x731   => 220, 0x734   => 220, 0x737   => 220, 0x738   => 220, 0x739   => 220
1497
                    ,0x73B   => 220, 0x73C   => 220, 0x73E   => 220, 0x742   => 220, 0x744   => 220
1498
                    ,0x746   => 220, 0x748   => 220, 0x952   => 220, 0xF18   => 220, 0xF19   => 220
1499
                    ,0xF35   => 220, 0xF37   => 220, 0xFC6   => 220, 0x193B  => 220, 0x20E8  => 220
1500
                    ,0x1D17B => 220, 0x1D17C => 220, 0x1D17D => 220, 0x1D17E => 220, 0x1D17F => 220
1501
                    ,0x1D180 => 220, 0x1D181 => 220, 0x1D182 => 220, 0x1D18A => 220, 0x1D18B => 220
1502
                    ,0x59A   => 222, 0x5AD   => 222, 0x1929  => 222, 0x302D  => 222, 0x302E  => 224
1503
                    ,0x302F  => 224, 0x1D16D => 226, 0x5AE   => 228, 0x18A9  => 228, 0x302B  => 228
1504
                    ,0x300   => 230, 0x301   => 230, 0x302   => 230, 0x303   => 230, 0x304   => 230
1505
                    ,0x305   => 230, 0x306   => 230, 0x307   => 230, 0x308   => 230, 0x309   => 230
1506
                    ,0x30A   => 230, 0x30B   => 230, 0x30C   => 230, 0x30D   => 230, 0x30E   => 230
1507
                    ,0x30F   => 230, 0x310   => 230, 0x311   => 230, 0x312   => 230, 0x313   => 230
1508
                    ,0x314   => 230, 0x33D   => 230, 0x33E   => 230, 0x33F   => 230, 0x340   => 230
1509
                    ,0x341   => 230, 0x342   => 230, 0x343   => 230, 0x344   => 230, 0x346   => 230
1510
                    ,0x34A   => 230, 0x34B   => 230, 0x34C   => 230, 0x350   => 230, 0x351   => 230
1511
                    ,0x352   => 230, 0x357   => 230, 0x363   => 230, 0x364   => 230, 0x365   => 230
1512
                    ,0x366   => 230, 0x367   => 230, 0x368   => 230, 0x369   => 230, 0x36A   => 230
1513
                    ,0x36B   => 230, 0x36C   => 230, 0x36D   => 230, 0x36E   => 230, 0x36F   => 230
1514
                    ,0x483   => 230, 0x484   => 230, 0x485   => 230, 0x486   => 230, 0x592   => 230
1515
                    ,0x593   => 230, 0x594   => 230, 0x595   => 230, 0x597   => 230, 0x598   => 230
1516
                    ,0x599   => 230, 0x59C   => 230, 0x59D   => 230, 0x59E   => 230, 0x59F   => 230
1517
                    ,0x5A0   => 230, 0x5A1   => 230, 0x5A8   => 230, 0x5A9   => 230, 0x5AB   => 230
1518
                    ,0x5AC   => 230, 0x5AF   => 230, 0x5C4   => 230, 0x610   => 230, 0x611   => 230
1519
                    ,0x612   => 230, 0x613   => 230, 0x614   => 230, 0x615   => 230, 0x653   => 230
1520
                    ,0x654   => 230, 0x657   => 230, 0x658   => 230, 0x6D6   => 230, 0x6D7   => 230
1521
                    ,0x6D8   => 230, 0x6D9   => 230, 0x6DA   => 230, 0x6DB   => 230, 0x6DC   => 230
1522
                    ,0x6DF   => 230, 0x6E0   => 230, 0x6E1   => 230, 0x6E2   => 230, 0x6E4   => 230
1523
                    ,0x6E7   => 230, 0x6E8   => 230, 0x6EB   => 230, 0x6EC   => 230, 0x730   => 230
1524
                    ,0x732   => 230, 0x733   => 230, 0x735   => 230, 0x736   => 230, 0x73A   => 230
1525
                    ,0x73D   => 230, 0x73F   => 230, 0x740   => 230, 0x741   => 230, 0x743   => 230
1526
                    ,0x745   => 230, 0x747   => 230, 0x749   => 230, 0x74A   => 230, 0x951   => 230
1527
                    ,0x953   => 230, 0x954   => 230, 0xF82   => 230, 0xF83   => 230, 0xF86   => 230
1528
                    ,0xF87   => 230, 0x170D  => 230, 0x193A  => 230, 0x20D0  => 230, 0x20D1  => 230
1529
                    ,0x20D4  => 230, 0x20D5  => 230, 0x20D6  => 230, 0x20D7  => 230, 0x20DB  => 230
1530
                    ,0x20DC  => 230, 0x20E1  => 230, 0x20E7  => 230, 0x20E9  => 230, 0xFE20  => 230
1531
                    ,0xFE21  => 230, 0xFE22  => 230, 0xFE23  => 230, 0x1D185 => 230, 0x1D186 => 230
1532
                    ,0x1D187 => 230, 0x1D189 => 230, 0x1D188 => 230, 0x1D1AA => 230, 0x1D1AB => 230
1533
                    ,0x1D1AC => 230, 0x1D1AD => 230, 0x315   => 232, 0x31A   => 232, 0x302C  => 232
1534
                    ,0x35F   => 233, 0x362   => 233, 0x35D   => 234, 0x35E   => 234, 0x360   => 234
1535
                    ,0x361   => 234, 0x345   => 240
1536
                    )
1537
            );
1538
}
1539
?>
(1-1/2)