xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Unicode/Collate.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gatepackage Unicode::Collate;
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gateBEGIN {
4*0Sstevel@tonic-gate    unless ("A" eq pack('U', 0x41)) {
5*0Sstevel@tonic-gate	die "Unicode::Collate cannot stringify a Unicode code point\n";
6*0Sstevel@tonic-gate    }
7*0Sstevel@tonic-gate}
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gateuse 5.006;
10*0Sstevel@tonic-gateuse strict;
11*0Sstevel@tonic-gateuse warnings;
12*0Sstevel@tonic-gateuse Carp;
13*0Sstevel@tonic-gateuse File::Spec;
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gateno warnings 'utf8';
16*0Sstevel@tonic-gate
17*0Sstevel@tonic-gaterequire Exporter;
18*0Sstevel@tonic-gate
19*0Sstevel@tonic-gateour $VERSION = '0.33';
20*0Sstevel@tonic-gateour $PACKAGE = __PACKAGE__;
21*0Sstevel@tonic-gate
22*0Sstevel@tonic-gateour @ISA = qw(Exporter);
23*0Sstevel@tonic-gate
24*0Sstevel@tonic-gateour %EXPORT_TAGS = ();
25*0Sstevel@tonic-gateour @EXPORT_OK = ();
26*0Sstevel@tonic-gateour @EXPORT = ();
27*0Sstevel@tonic-gate
28*0Sstevel@tonic-gate(our $Path = $INC{'Unicode/Collate.pm'}) =~ s/\.pm$//;
29*0Sstevel@tonic-gateour $KeyFile = "allkeys.txt";
30*0Sstevel@tonic-gate
31*0Sstevel@tonic-gate# Perl's boolean
32*0Sstevel@tonic-gateuse constant TRUE  => 1;
33*0Sstevel@tonic-gateuse constant FALSE => "";
34*0Sstevel@tonic-gateuse constant NOMATCHPOS => -1;
35*0Sstevel@tonic-gate
36*0Sstevel@tonic-gate# A coderef to get combining class imported from Unicode::Normalize
37*0Sstevel@tonic-gate# (i.e. \&Unicode::Normalize::getCombinClass).
38*0Sstevel@tonic-gate# This is also used as a HAS_UNICODE_NORMALIZE flag.
39*0Sstevel@tonic-gateour $CVgetCombinClass;
40*0Sstevel@tonic-gate
41*0Sstevel@tonic-gate# Supported Levels
42*0Sstevel@tonic-gateuse constant MinLevel => 1;
43*0Sstevel@tonic-gateuse constant MaxLevel => 4;
44*0Sstevel@tonic-gate
45*0Sstevel@tonic-gate# Minimum weights at level 2 and 3, respectively
46*0Sstevel@tonic-gateuse constant Min2Wt => 0x20;
47*0Sstevel@tonic-gateuse constant Min3Wt => 0x02;
48*0Sstevel@tonic-gate
49*0Sstevel@tonic-gate# Shifted weight at 4th level
50*0Sstevel@tonic-gateuse constant Shift4Wt => 0xFFFF;
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate# A boolean for Variable and 16-bit weights at 4 levels of Collation Element
53*0Sstevel@tonic-gate# PROBLEM: The Default Unicode Collation Element Table
54*0Sstevel@tonic-gate# has weights over 0xFFFF at the 4th level.
55*0Sstevel@tonic-gate# The tie-breaking in the variable weights
56*0Sstevel@tonic-gate# other than "shift" (as well as "shift-trimmed") is unreliable.
57*0Sstevel@tonic-gateuse constant VCE_TEMPLATE => 'Cn4';
58*0Sstevel@tonic-gate
59*0Sstevel@tonic-gate# A sort key: 16-bit weights
60*0Sstevel@tonic-gate# See also the PROBLEM on VCE_TEMPLATE above.
61*0Sstevel@tonic-gateuse constant KEY_TEMPLATE => 'n*';
62*0Sstevel@tonic-gate
63*0Sstevel@tonic-gate# Level separator in a sort key:
64*0Sstevel@tonic-gate# i.e. pack(KEY_TEMPLATE, 0)
65*0Sstevel@tonic-gateuse constant LEVEL_SEP => "\0\0";
66*0Sstevel@tonic-gate
67*0Sstevel@tonic-gate# As Unicode code point separator for hash keys.
68*0Sstevel@tonic-gate# A joined code point string (denoted by JCPS below)
69*0Sstevel@tonic-gate# like "65;768" is used for internal processing
70*0Sstevel@tonic-gate# instead of Perl's Unicode string like "\x41\x{300}",
71*0Sstevel@tonic-gate# as the native code point is different from the Unicode code point
72*0Sstevel@tonic-gate# on EBCDIC platform.
73*0Sstevel@tonic-gate# This character must not be included in any stringified
74*0Sstevel@tonic-gate# representation of an integer.
75*0Sstevel@tonic-gateuse constant CODE_SEP => ';';
76*0Sstevel@tonic-gate
77*0Sstevel@tonic-gate# boolean values of variable weights
78*0Sstevel@tonic-gateuse constant NON_VAR => 0; # Non-Variable character
79*0Sstevel@tonic-gateuse constant VAR     => 1; # Variable character
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gate# specific code points
82*0Sstevel@tonic-gateuse constant Hangul_LBase  => 0x1100;
83*0Sstevel@tonic-gateuse constant Hangul_LIni   => 0x1100;
84*0Sstevel@tonic-gateuse constant Hangul_LFin   => 0x1159;
85*0Sstevel@tonic-gateuse constant Hangul_LFill  => 0x115F;
86*0Sstevel@tonic-gateuse constant Hangul_VBase  => 0x1161;
87*0Sstevel@tonic-gateuse constant Hangul_VIni   => 0x1160;
88*0Sstevel@tonic-gateuse constant Hangul_VFin   => 0x11A2;
89*0Sstevel@tonic-gateuse constant Hangul_TBase  => 0x11A7;
90*0Sstevel@tonic-gateuse constant Hangul_TIni   => 0x11A8;
91*0Sstevel@tonic-gateuse constant Hangul_TFin   => 0x11F9;
92*0Sstevel@tonic-gateuse constant Hangul_TCount => 28;
93*0Sstevel@tonic-gateuse constant Hangul_NCount => 588;
94*0Sstevel@tonic-gateuse constant Hangul_SBase  => 0xAC00;
95*0Sstevel@tonic-gateuse constant Hangul_SIni   => 0xAC00;
96*0Sstevel@tonic-gateuse constant Hangul_SFin   => 0xD7A3;
97*0Sstevel@tonic-gateuse constant CJK_UidIni    => 0x4E00;
98*0Sstevel@tonic-gateuse constant CJK_UidFin    => 0x9FA5;
99*0Sstevel@tonic-gateuse constant CJK_ExtAIni   => 0x3400;
100*0Sstevel@tonic-gateuse constant CJK_ExtAFin   => 0x4DB5;
101*0Sstevel@tonic-gateuse constant CJK_ExtBIni   => 0x20000;
102*0Sstevel@tonic-gateuse constant CJK_ExtBFin   => 0x2A6D6;
103*0Sstevel@tonic-gateuse constant BMP_Max       => 0xFFFF;
104*0Sstevel@tonic-gate
105*0Sstevel@tonic-gate# Logical_Order_Exception in PropList.txt
106*0Sstevel@tonic-gate# TODO: synchronization with change of PropList.txt.
107*0Sstevel@tonic-gateour $DefaultRearrange = [ 0x0E40..0x0E44, 0x0EC0..0x0EC4 ];
108*0Sstevel@tonic-gate
109*0Sstevel@tonic-gatesub UCA_Version { "11" }
110*0Sstevel@tonic-gate
111*0Sstevel@tonic-gatesub Base_Unicode_Version { "4.0" }
112*0Sstevel@tonic-gate
113*0Sstevel@tonic-gate######
114*0Sstevel@tonic-gate
115*0Sstevel@tonic-gatesub pack_U {
116*0Sstevel@tonic-gate    return pack('U*', @_);
117*0Sstevel@tonic-gate}
118*0Sstevel@tonic-gate
119*0Sstevel@tonic-gatesub unpack_U {
120*0Sstevel@tonic-gate    return unpack('U*', pack('U*').shift);
121*0Sstevel@tonic-gate}
122*0Sstevel@tonic-gate
123*0Sstevel@tonic-gate######
124*0Sstevel@tonic-gate
125*0Sstevel@tonic-gatemy (%VariableOK);
126*0Sstevel@tonic-gate@VariableOK{ qw/
127*0Sstevel@tonic-gate    blanked  non-ignorable  shifted  shift-trimmed
128*0Sstevel@tonic-gate  / } = (); # keys lowercased
129*0Sstevel@tonic-gate
130*0Sstevel@tonic-gateour @ChangeOK = qw/
131*0Sstevel@tonic-gate    alternate backwards level normalization rearrange
132*0Sstevel@tonic-gate    katakana_before_hiragana upper_before_lower
133*0Sstevel@tonic-gate    overrideHangul overrideCJK preprocess UCA_Version
134*0Sstevel@tonic-gate    hangul_terminator variable
135*0Sstevel@tonic-gate  /;
136*0Sstevel@tonic-gate
137*0Sstevel@tonic-gateour @ChangeNG = qw/
138*0Sstevel@tonic-gate    entry mapping table maxlength
139*0Sstevel@tonic-gate    ignoreChar ignoreName undefChar undefName variableTable
140*0Sstevel@tonic-gate    versionTable alternateTable backwardsTable forwardsTable rearrangeTable
141*0Sstevel@tonic-gate    derivCode normCode rearrangeHash L3_ignorable
142*0Sstevel@tonic-gate    backwardsFlag
143*0Sstevel@tonic-gate  /;
144*0Sstevel@tonic-gate# The hash key 'ignored' is deleted at v 0.21.
145*0Sstevel@tonic-gate# The hash key 'isShift' is deleted at v 0.23.
146*0Sstevel@tonic-gate# The hash key 'combining' is deleted at v 0.24.
147*0Sstevel@tonic-gate# The hash key 'entries' is deleted at v 0.30.
148*0Sstevel@tonic-gate
149*0Sstevel@tonic-gatesub version {
150*0Sstevel@tonic-gate    my $self = shift;
151*0Sstevel@tonic-gate    return $self->{versionTable} || 'unknown';
152*0Sstevel@tonic-gate}
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gatemy (%ChangeOK, %ChangeNG);
155*0Sstevel@tonic-gate@ChangeOK{ @ChangeOK } = ();
156*0Sstevel@tonic-gate@ChangeNG{ @ChangeNG } = ();
157*0Sstevel@tonic-gate
158*0Sstevel@tonic-gatesub change {
159*0Sstevel@tonic-gate    my $self = shift;
160*0Sstevel@tonic-gate    my %hash = @_;
161*0Sstevel@tonic-gate    my %old;
162*0Sstevel@tonic-gate    if (exists $hash{variable} && exists $hash{alternate}) {
163*0Sstevel@tonic-gate	delete $hash{alternate};
164*0Sstevel@tonic-gate    }
165*0Sstevel@tonic-gate    elsif (!exists $hash{variable} && exists $hash{alternate}) {
166*0Sstevel@tonic-gate	$hash{variable} = $hash{alternate};
167*0Sstevel@tonic-gate    }
168*0Sstevel@tonic-gate    foreach my $k (keys %hash) {
169*0Sstevel@tonic-gate	if (exists $ChangeOK{$k}) {
170*0Sstevel@tonic-gate	    $old{$k} = $self->{$k};
171*0Sstevel@tonic-gate	    $self->{$k} = $hash{$k};
172*0Sstevel@tonic-gate	}
173*0Sstevel@tonic-gate	elsif (exists $ChangeNG{$k}) {
174*0Sstevel@tonic-gate	    croak "change of $k via change() is not allowed!";
175*0Sstevel@tonic-gate	}
176*0Sstevel@tonic-gate	# else => ignored
177*0Sstevel@tonic-gate    }
178*0Sstevel@tonic-gate    $self->checkCollator;
179*0Sstevel@tonic-gate    return wantarray ? %old : $self;
180*0Sstevel@tonic-gate}
181*0Sstevel@tonic-gate
182*0Sstevel@tonic-gatesub _checkLevel {
183*0Sstevel@tonic-gate    my $level = shift;
184*0Sstevel@tonic-gate    my $key   = shift; # 'level' or 'backwards'
185*0Sstevel@tonic-gate    MinLevel <= $level or croak sprintf
186*0Sstevel@tonic-gate	"Illegal level %d (in value for key '%s') lower than %d.",
187*0Sstevel@tonic-gate	    $level, $key, MinLevel;
188*0Sstevel@tonic-gate    $level <= MaxLevel or croak sprintf
189*0Sstevel@tonic-gate	"Unsupported level %d (in value for key '%s') higher than %d.",
190*0Sstevel@tonic-gate	    $level, $key, MaxLevel;
191*0Sstevel@tonic-gate}
192*0Sstevel@tonic-gate
193*0Sstevel@tonic-gatemy %DerivCode = (
194*0Sstevel@tonic-gate    8 => \&_derivCE_8,
195*0Sstevel@tonic-gate    9 => \&_derivCE_9,
196*0Sstevel@tonic-gate   11 => \&_derivCE_9, # 11 == 9
197*0Sstevel@tonic-gate);
198*0Sstevel@tonic-gate
199*0Sstevel@tonic-gatesub checkCollator {
200*0Sstevel@tonic-gate    my $self = shift;
201*0Sstevel@tonic-gate    _checkLevel($self->{level}, "level");
202*0Sstevel@tonic-gate
203*0Sstevel@tonic-gate    $self->{derivCode} = $DerivCode{ $self->{UCA_Version} }
204*0Sstevel@tonic-gate	or croak "Illegal UCA version (passed $self->{UCA_Version}).";
205*0Sstevel@tonic-gate
206*0Sstevel@tonic-gate    $self->{variable} ||= $self->{alternate} || $self->{variableTable} ||
207*0Sstevel@tonic-gate				$self->{alternateTable} || 'shifted';
208*0Sstevel@tonic-gate    $self->{variable} = $self->{alternate} = lc($self->{variable});
209*0Sstevel@tonic-gate    exists $VariableOK{ $self->{variable} }
210*0Sstevel@tonic-gate	or croak "$PACKAGE unknown variable tag name: $self->{variable}";
211*0Sstevel@tonic-gate
212*0Sstevel@tonic-gate    if (! defined $self->{backwards}) {
213*0Sstevel@tonic-gate	$self->{backwardsFlag} = 0;
214*0Sstevel@tonic-gate    }
215*0Sstevel@tonic-gate    elsif (! ref $self->{backwards}) {
216*0Sstevel@tonic-gate	_checkLevel($self->{backwards}, "backwards");
217*0Sstevel@tonic-gate	$self->{backwardsFlag} = 1 << $self->{backwards};
218*0Sstevel@tonic-gate    }
219*0Sstevel@tonic-gate    else {
220*0Sstevel@tonic-gate	my %level;
221*0Sstevel@tonic-gate	$self->{backwardsFlag} = 0;
222*0Sstevel@tonic-gate	for my $b (@{ $self->{backwards} }) {
223*0Sstevel@tonic-gate	    _checkLevel($b, "backwards");
224*0Sstevel@tonic-gate	    $level{$b} = 1;
225*0Sstevel@tonic-gate	}
226*0Sstevel@tonic-gate	for my $v (sort keys %level) {
227*0Sstevel@tonic-gate	    $self->{backwardsFlag} += 1 << $v;
228*0Sstevel@tonic-gate	}
229*0Sstevel@tonic-gate    }
230*0Sstevel@tonic-gate
231*0Sstevel@tonic-gate    defined $self->{rearrange} or $self->{rearrange} = [];
232*0Sstevel@tonic-gate    ref $self->{rearrange}
233*0Sstevel@tonic-gate	or croak "$PACKAGE: list for rearrangement must be store in ARRAYREF";
234*0Sstevel@tonic-gate
235*0Sstevel@tonic-gate    # keys of $self->{rearrangeHash} are $self->{rearrange}.
236*0Sstevel@tonic-gate    $self->{rearrangeHash} = undef;
237*0Sstevel@tonic-gate
238*0Sstevel@tonic-gate    if (@{ $self->{rearrange} }) {
239*0Sstevel@tonic-gate	@{ $self->{rearrangeHash} }{ @{ $self->{rearrange} } } = ();
240*0Sstevel@tonic-gate    }
241*0Sstevel@tonic-gate
242*0Sstevel@tonic-gate    $self->{normCode} = undef;
243*0Sstevel@tonic-gate
244*0Sstevel@tonic-gate    if (defined $self->{normalization}) {
245*0Sstevel@tonic-gate	eval { require Unicode::Normalize };
246*0Sstevel@tonic-gate	$@ and croak "Unicode::Normalize is required to normalize strings";
247*0Sstevel@tonic-gate
248*0Sstevel@tonic-gate	$CVgetCombinClass ||= \&Unicode::Normalize::getCombinClass;
249*0Sstevel@tonic-gate
250*0Sstevel@tonic-gate	if ($self->{normalization} =~ /^(?:NF)D\z/) { # tweak for default
251*0Sstevel@tonic-gate	    $self->{normCode} = \&Unicode::Normalize::NFD;
252*0Sstevel@tonic-gate	}
253*0Sstevel@tonic-gate	elsif ($self->{normalization} ne 'prenormalized') {
254*0Sstevel@tonic-gate	    my $norm = $self->{normalization};
255*0Sstevel@tonic-gate	    $self->{normCode} = sub {
256*0Sstevel@tonic-gate		Unicode::Normalize::normalize($norm, shift);
257*0Sstevel@tonic-gate	    };
258*0Sstevel@tonic-gate	    eval { $self->{normCode}->("") }; # try
259*0Sstevel@tonic-gate	    $@ and croak "$PACKAGE unknown normalization form name: $norm";
260*0Sstevel@tonic-gate	}
261*0Sstevel@tonic-gate    }
262*0Sstevel@tonic-gate    return;
263*0Sstevel@tonic-gate}
264*0Sstevel@tonic-gate
265*0Sstevel@tonic-gatesub new
266*0Sstevel@tonic-gate{
267*0Sstevel@tonic-gate    my $class = shift;
268*0Sstevel@tonic-gate    my $self = bless { @_ }, $class;
269*0Sstevel@tonic-gate
270*0Sstevel@tonic-gate    # If undef is passed explicitly, no file is read.
271*0Sstevel@tonic-gate    $self->{table} = $KeyFile if ! exists $self->{table};
272*0Sstevel@tonic-gate    $self->read_table if defined $self->{table};
273*0Sstevel@tonic-gate
274*0Sstevel@tonic-gate    if ($self->{entry}) {
275*0Sstevel@tonic-gate	$self->parseEntry($_) foreach split /\n/, $self->{entry};
276*0Sstevel@tonic-gate    }
277*0Sstevel@tonic-gate
278*0Sstevel@tonic-gate    $self->{level} ||= MaxLevel;
279*0Sstevel@tonic-gate    $self->{UCA_Version} ||= UCA_Version();
280*0Sstevel@tonic-gate
281*0Sstevel@tonic-gate    $self->{overrideHangul} = FALSE
282*0Sstevel@tonic-gate	if ! exists $self->{overrideHangul};
283*0Sstevel@tonic-gate    $self->{overrideCJK} = FALSE
284*0Sstevel@tonic-gate	if ! exists $self->{overrideCJK};
285*0Sstevel@tonic-gate    $self->{normalization} = 'NFD'
286*0Sstevel@tonic-gate	if ! exists $self->{normalization};
287*0Sstevel@tonic-gate    $self->{rearrange} = $self->{rearrangeTable} || $DefaultRearrange
288*0Sstevel@tonic-gate	if ! exists $self->{rearrange};
289*0Sstevel@tonic-gate    $self->{backwards} = $self->{backwardsTable}
290*0Sstevel@tonic-gate	if ! exists $self->{backwards};
291*0Sstevel@tonic-gate
292*0Sstevel@tonic-gate    $self->checkCollator;
293*0Sstevel@tonic-gate
294*0Sstevel@tonic-gate    return $self;
295*0Sstevel@tonic-gate}
296*0Sstevel@tonic-gate
297*0Sstevel@tonic-gatesub read_table {
298*0Sstevel@tonic-gate    my $self = shift;
299*0Sstevel@tonic-gate
300*0Sstevel@tonic-gate    my $filepath = File::Spec->catfile($Path, $self->{table});
301*0Sstevel@tonic-gate    open my $fk, "<$filepath"
302*0Sstevel@tonic-gate	or croak "File does not exist at $filepath";
303*0Sstevel@tonic-gate
304*0Sstevel@tonic-gate    while (<$fk>) {
305*0Sstevel@tonic-gate	next if /^\s*#/;
306*0Sstevel@tonic-gate	unless (s/^\s*\@//) {
307*0Sstevel@tonic-gate	    $self->parseEntry($_);
308*0Sstevel@tonic-gate	    next;
309*0Sstevel@tonic-gate	}
310*0Sstevel@tonic-gate
311*0Sstevel@tonic-gate	if (/^version\s*(\S*)/) {
312*0Sstevel@tonic-gate	    $self->{versionTable} ||= $1;
313*0Sstevel@tonic-gate	}
314*0Sstevel@tonic-gate	elsif (/^variable\s+(\S*)/) { # since UTS #10-9
315*0Sstevel@tonic-gate	    $self->{variableTable} ||= $1;
316*0Sstevel@tonic-gate	}
317*0Sstevel@tonic-gate	elsif (/^alternate\s+(\S*)/) { # till UTS #10-8
318*0Sstevel@tonic-gate	    $self->{alternateTable} ||= $1;
319*0Sstevel@tonic-gate	}
320*0Sstevel@tonic-gate	elsif (/^backwards\s+(\S*)/) {
321*0Sstevel@tonic-gate	    push @{ $self->{backwardsTable} }, $1;
322*0Sstevel@tonic-gate	}
323*0Sstevel@tonic-gate	elsif (/^forwards\s+(\S*)/) { # parhaps no use
324*0Sstevel@tonic-gate	    push @{ $self->{forwardsTable} }, $1;
325*0Sstevel@tonic-gate	}
326*0Sstevel@tonic-gate	elsif (/^rearrange\s+(.*)/) { # (\S*) is NG
327*0Sstevel@tonic-gate	    push @{ $self->{rearrangeTable} }, _getHexArray($1);
328*0Sstevel@tonic-gate	}
329*0Sstevel@tonic-gate    }
330*0Sstevel@tonic-gate    close $fk;
331*0Sstevel@tonic-gate}
332*0Sstevel@tonic-gate
333*0Sstevel@tonic-gate
334*0Sstevel@tonic-gate##
335*0Sstevel@tonic-gate## get $line, parse it, and write an entry in $self
336*0Sstevel@tonic-gate##
337*0Sstevel@tonic-gatesub parseEntry
338*0Sstevel@tonic-gate{
339*0Sstevel@tonic-gate    my $self = shift;
340*0Sstevel@tonic-gate    my $line = shift;
341*0Sstevel@tonic-gate    my($name, $entry, @uv, @key);
342*0Sstevel@tonic-gate
343*0Sstevel@tonic-gate    return if $line !~ /^\s*[0-9A-Fa-f]/;
344*0Sstevel@tonic-gate
345*0Sstevel@tonic-gate    # removes comment and gets name
346*0Sstevel@tonic-gate    $name = $1
347*0Sstevel@tonic-gate	if $line =~ s/[#%]\s*(.*)//;
348*0Sstevel@tonic-gate    return if defined $self->{undefName} && $name =~ /$self->{undefName}/;
349*0Sstevel@tonic-gate
350*0Sstevel@tonic-gate    # gets element
351*0Sstevel@tonic-gate    my($e, $k) = split /;/, $line;
352*0Sstevel@tonic-gate    croak "Wrong Entry: <charList> must be separated by ';' from <collElement>"
353*0Sstevel@tonic-gate	if ! $k;
354*0Sstevel@tonic-gate
355*0Sstevel@tonic-gate    @uv = _getHexArray($e);
356*0Sstevel@tonic-gate    return if !@uv;
357*0Sstevel@tonic-gate
358*0Sstevel@tonic-gate    $entry = join(CODE_SEP, @uv); # in JCPS
359*0Sstevel@tonic-gate
360*0Sstevel@tonic-gate    if (defined $self->{undefChar} || defined $self->{ignoreChar}) {
361*0Sstevel@tonic-gate	my $ele = pack_U(@uv);
362*0Sstevel@tonic-gate
363*0Sstevel@tonic-gate	# regarded as if it were not entried in the table
364*0Sstevel@tonic-gate	return
365*0Sstevel@tonic-gate	    if defined $self->{undefChar} && $ele =~ /$self->{undefChar}/;
366*0Sstevel@tonic-gate
367*0Sstevel@tonic-gate	# replaced as completely ignorable
368*0Sstevel@tonic-gate	$k = '[.0000.0000.0000.0000]'
369*0Sstevel@tonic-gate	    if defined $self->{ignoreChar} && $ele =~ /$self->{ignoreChar}/;
370*0Sstevel@tonic-gate    }
371*0Sstevel@tonic-gate
372*0Sstevel@tonic-gate    # replaced as completely ignorable
373*0Sstevel@tonic-gate    $k = '[.0000.0000.0000.0000]'
374*0Sstevel@tonic-gate	if defined $self->{ignoreName} && $name =~ /$self->{ignoreName}/;
375*0Sstevel@tonic-gate
376*0Sstevel@tonic-gate    my $is_L3_ignorable = TRUE;
377*0Sstevel@tonic-gate
378*0Sstevel@tonic-gate    foreach my $arr ($k =~ /\[([^\[\]]+)\]/g) { # SPACEs allowed
379*0Sstevel@tonic-gate	my $var = $arr =~ /\*/; # exactly /^\*/ but be lenient.
380*0Sstevel@tonic-gate	my @wt = _getHexArray($arr);
381*0Sstevel@tonic-gate	push @key, pack(VCE_TEMPLATE, $var, @wt);
382*0Sstevel@tonic-gate	$is_L3_ignorable = FALSE
383*0Sstevel@tonic-gate	    if $wt[0] + $wt[1] + $wt[2] != 0;
384*0Sstevel@tonic-gate	  # if $arr !~ /[1-9A-Fa-f]/; NG
385*0Sstevel@tonic-gate	  # Conformance Test shows L3-ignorable is completely ignorable.
386*0Sstevel@tonic-gate	# For expansion, an entry $is_L3_ignorable
387*0Sstevel@tonic-gate	# if and only if "all" CEs are [.0000.0000.0000].
388*0Sstevel@tonic-gate    }
389*0Sstevel@tonic-gate
390*0Sstevel@tonic-gate    $self->{mapping}{$entry} = \@key;
391*0Sstevel@tonic-gate
392*0Sstevel@tonic-gate    if (@uv > 1) {
393*0Sstevel@tonic-gate	(!$self->{maxlength}{$uv[0]} || $self->{maxlength}{$uv[0]} < @uv)
394*0Sstevel@tonic-gate	    and $self->{maxlength}{$uv[0]} = @uv;
395*0Sstevel@tonic-gate    }
396*0Sstevel@tonic-gate    else {
397*0Sstevel@tonic-gate	$is_L3_ignorable
398*0Sstevel@tonic-gate	    ? ($self->{L3_ignorable}{$uv[0]} = TRUE)
399*0Sstevel@tonic-gate	    : ($self->{L3_ignorable}{$uv[0]} and
400*0Sstevel@tonic-gate	       $self->{L3_ignorable}{$uv[0]} = FALSE); # &&= stores key.
401*0Sstevel@tonic-gate    }
402*0Sstevel@tonic-gate}
403*0Sstevel@tonic-gate
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate##
406*0Sstevel@tonic-gate## VCE = _varCE(variable term, VCE)
407*0Sstevel@tonic-gate##
408*0Sstevel@tonic-gatesub _varCE
409*0Sstevel@tonic-gate{
410*0Sstevel@tonic-gate    my $vbl = shift;
411*0Sstevel@tonic-gate    my $vce = shift;
412*0Sstevel@tonic-gate    if ($vbl eq 'non-ignorable') {
413*0Sstevel@tonic-gate	return $vce;
414*0Sstevel@tonic-gate    }
415*0Sstevel@tonic-gate    my ($var, @wt) = unpack VCE_TEMPLATE, $vce;
416*0Sstevel@tonic-gate
417*0Sstevel@tonic-gate    if ($var) {
418*0Sstevel@tonic-gate	return pack(VCE_TEMPLATE, $var, 0, 0, 0,
419*0Sstevel@tonic-gate		$vbl eq 'blanked' ? $wt[3] : $wt[0]);
420*0Sstevel@tonic-gate    }
421*0Sstevel@tonic-gate    elsif ($vbl eq 'blanked') {
422*0Sstevel@tonic-gate	return $vce;
423*0Sstevel@tonic-gate    }
424*0Sstevel@tonic-gate    else {
425*0Sstevel@tonic-gate	return pack(VCE_TEMPLATE, $var, @wt[0..2],
426*0Sstevel@tonic-gate	    $vbl eq 'shifted' && $wt[0]+$wt[1]+$wt[2] ? Shift4Wt : 0);
427*0Sstevel@tonic-gate    }
428*0Sstevel@tonic-gate}
429*0Sstevel@tonic-gate
430*0Sstevel@tonic-gatesub viewSortKey
431*0Sstevel@tonic-gate{
432*0Sstevel@tonic-gate    my $self = shift;
433*0Sstevel@tonic-gate    $self->visualizeSortKey($self->getSortKey(@_));
434*0Sstevel@tonic-gate}
435*0Sstevel@tonic-gate
436*0Sstevel@tonic-gatesub visualizeSortKey
437*0Sstevel@tonic-gate{
438*0Sstevel@tonic-gate    my $self = shift;
439*0Sstevel@tonic-gate    my $view = join " ", map sprintf("%04X", $_), unpack(KEY_TEMPLATE, shift);
440*0Sstevel@tonic-gate
441*0Sstevel@tonic-gate    if ($self->{UCA_Version} <= 8) {
442*0Sstevel@tonic-gate	$view =~ s/ ?0000 ?/|/g;
443*0Sstevel@tonic-gate    } else {
444*0Sstevel@tonic-gate	$view =~ s/\b0000\b/|/g;
445*0Sstevel@tonic-gate    }
446*0Sstevel@tonic-gate    return "[$view]";
447*0Sstevel@tonic-gate}
448*0Sstevel@tonic-gate
449*0Sstevel@tonic-gate
450*0Sstevel@tonic-gate##
451*0Sstevel@tonic-gate## arrayref of JCPS   = splitEnt(string to be collated)
452*0Sstevel@tonic-gate## arrayref of arrayref[JCPS, ini_pos, fin_pos] = splitEnt(string, true)
453*0Sstevel@tonic-gate##
454*0Sstevel@tonic-gatesub splitEnt
455*0Sstevel@tonic-gate{
456*0Sstevel@tonic-gate    my $self = shift;
457*0Sstevel@tonic-gate    my $wLen = $_[1];
458*0Sstevel@tonic-gate
459*0Sstevel@tonic-gate    my $code = $self->{preprocess};
460*0Sstevel@tonic-gate    my $norm = $self->{normCode};
461*0Sstevel@tonic-gate    my $map  = $self->{mapping};
462*0Sstevel@tonic-gate    my $max  = $self->{maxlength};
463*0Sstevel@tonic-gate    my $reH  = $self->{rearrangeHash};
464*0Sstevel@tonic-gate    my $ign  = $self->{L3_ignorable};
465*0Sstevel@tonic-gate    my $ver9 = $self->{UCA_Version} >= 9;
466*0Sstevel@tonic-gate
467*0Sstevel@tonic-gate    my ($str, @buf);
468*0Sstevel@tonic-gate
469*0Sstevel@tonic-gate    if ($wLen) {
470*0Sstevel@tonic-gate	$code and croak "Preprocess breaks character positions. "
471*0Sstevel@tonic-gate			. "Don't use with index(), match(), etc.";
472*0Sstevel@tonic-gate	$norm and croak "Normalization breaks character positions. "
473*0Sstevel@tonic-gate			. "Don't use with index(), match(), etc.";
474*0Sstevel@tonic-gate	$str = $_[0];
475*0Sstevel@tonic-gate    }
476*0Sstevel@tonic-gate    else {
477*0Sstevel@tonic-gate	$str = $_[0];
478*0Sstevel@tonic-gate	$str = &$code($str) if ref $code;
479*0Sstevel@tonic-gate	$str = &$norm($str) if ref $norm;
480*0Sstevel@tonic-gate    }
481*0Sstevel@tonic-gate
482*0Sstevel@tonic-gate    # get array of Unicode code point of string.
483*0Sstevel@tonic-gate    my @src = unpack_U($str);
484*0Sstevel@tonic-gate
485*0Sstevel@tonic-gate    # rearrangement:
486*0Sstevel@tonic-gate    # Character positions are not kept if rearranged,
487*0Sstevel@tonic-gate    # then neglected if $wLen is true.
488*0Sstevel@tonic-gate    if ($reH && ! $wLen) {
489*0Sstevel@tonic-gate	for (my $i = 0; $i < @src; $i++) {
490*0Sstevel@tonic-gate	    if (exists $reH->{ $src[$i] } && $i + 1 < @src) {
491*0Sstevel@tonic-gate		($src[$i], $src[$i+1]) = ($src[$i+1], $src[$i]);
492*0Sstevel@tonic-gate		$i++;
493*0Sstevel@tonic-gate	    }
494*0Sstevel@tonic-gate	}
495*0Sstevel@tonic-gate    }
496*0Sstevel@tonic-gate
497*0Sstevel@tonic-gate    # To remove a character marked as a completely ignorable.
498*0Sstevel@tonic-gate    for (my $i = 0; $i < @src; $i++) {
499*0Sstevel@tonic-gate	$src[$i] = undef
500*0Sstevel@tonic-gate	    if _isIllegal($src[$i]) || ($ver9 && $ign->{ $src[$i] });
501*0Sstevel@tonic-gate    }
502*0Sstevel@tonic-gate
503*0Sstevel@tonic-gate    for (my $i = 0; $i < @src; $i++) {
504*0Sstevel@tonic-gate	my $jcps = $src[$i];
505*0Sstevel@tonic-gate	next if ! defined $jcps;
506*0Sstevel@tonic-gate	my $i_orig = $i;
507*0Sstevel@tonic-gate
508*0Sstevel@tonic-gate	if ($max->{$jcps}) { # contract
509*0Sstevel@tonic-gate	    my $temp_jcps = $jcps;
510*0Sstevel@tonic-gate	    my $jcpsLen = 1;
511*0Sstevel@tonic-gate	    my $maxLen = $max->{$jcps};
512*0Sstevel@tonic-gate
513*0Sstevel@tonic-gate	    for (my $p = $i + 1; $jcpsLen < $maxLen && $p < @src; $p++) {
514*0Sstevel@tonic-gate		next if ! defined $src[$p];
515*0Sstevel@tonic-gate		$temp_jcps .= CODE_SEP . $src[$p];
516*0Sstevel@tonic-gate		$jcpsLen++;
517*0Sstevel@tonic-gate		if ($map->{$temp_jcps}) {
518*0Sstevel@tonic-gate		    $jcps = $temp_jcps;
519*0Sstevel@tonic-gate		    $i = $p;
520*0Sstevel@tonic-gate		}
521*0Sstevel@tonic-gate	    }
522*0Sstevel@tonic-gate
523*0Sstevel@tonic-gate	# not-contiguous contraction with Combining Char (cf. UTS#10, S2.1).
524*0Sstevel@tonic-gate	# This process requires Unicode::Normalize.
525*0Sstevel@tonic-gate	# If "normalization" is undef, here should be skipped *always*
526*0Sstevel@tonic-gate	# (in spite of bool value of $CVgetCombinClass),
527*0Sstevel@tonic-gate	# since canonical ordering cannot be expected.
528*0Sstevel@tonic-gate	# Blocked combining character should not be contracted.
529*0Sstevel@tonic-gate
530*0Sstevel@tonic-gate	    if ($self->{normalization})
531*0Sstevel@tonic-gate	    # $self->{normCode} is false in the case of "prenormalized".
532*0Sstevel@tonic-gate	    {
533*0Sstevel@tonic-gate		my $preCC = 0;
534*0Sstevel@tonic-gate		my $curCC = 0;
535*0Sstevel@tonic-gate
536*0Sstevel@tonic-gate		for (my $p = $i + 1; $p < @src; $p++) {
537*0Sstevel@tonic-gate		    next if ! defined $src[$p];
538*0Sstevel@tonic-gate		    $curCC = $CVgetCombinClass->($src[$p]);
539*0Sstevel@tonic-gate		    last unless $curCC;
540*0Sstevel@tonic-gate		    my $tail = CODE_SEP . $src[$p];
541*0Sstevel@tonic-gate		    if ($preCC != $curCC && $map->{$jcps.$tail}) {
542*0Sstevel@tonic-gate			$jcps .= $tail;
543*0Sstevel@tonic-gate			$src[$p] = undef;
544*0Sstevel@tonic-gate		    } else {
545*0Sstevel@tonic-gate			$preCC = $curCC;
546*0Sstevel@tonic-gate		    }
547*0Sstevel@tonic-gate		}
548*0Sstevel@tonic-gate	    }
549*0Sstevel@tonic-gate	}
550*0Sstevel@tonic-gate
551*0Sstevel@tonic-gate	if ($wLen) {
552*0Sstevel@tonic-gate	    for (; $i + 1 < @src; $i++) {
553*0Sstevel@tonic-gate		last if defined $src[$i + 1];
554*0Sstevel@tonic-gate	    }
555*0Sstevel@tonic-gate	}
556*0Sstevel@tonic-gate
557*0Sstevel@tonic-gate	push @buf, $wLen ? [$jcps, $i_orig, $i + 1] : $jcps;
558*0Sstevel@tonic-gate    }
559*0Sstevel@tonic-gate    return \@buf;
560*0Sstevel@tonic-gate}
561*0Sstevel@tonic-gate
562*0Sstevel@tonic-gate
563*0Sstevel@tonic-gate##
564*0Sstevel@tonic-gate## list of VCE = getWt(JCPS)
565*0Sstevel@tonic-gate##
566*0Sstevel@tonic-gatesub getWt
567*0Sstevel@tonic-gate{
568*0Sstevel@tonic-gate    my $self = shift;
569*0Sstevel@tonic-gate    my $u    = shift;
570*0Sstevel@tonic-gate    my $vbl  = $self->{variable};
571*0Sstevel@tonic-gate    my $map  = $self->{mapping};
572*0Sstevel@tonic-gate    my $der  = $self->{derivCode};
573*0Sstevel@tonic-gate
574*0Sstevel@tonic-gate    return if !defined $u;
575*0Sstevel@tonic-gate    return map(_varCE($vbl, $_), @{ $map->{$u} })
576*0Sstevel@tonic-gate	if $map->{$u};
577*0Sstevel@tonic-gate
578*0Sstevel@tonic-gate    # JCPS must not be a contraction, then it's a code point.
579*0Sstevel@tonic-gate    if (Hangul_SIni <= $u && $u <= Hangul_SFin) {
580*0Sstevel@tonic-gate	my $hang = $self->{overrideHangul};
581*0Sstevel@tonic-gate	my @hangulCE;
582*0Sstevel@tonic-gate	if ($hang) {
583*0Sstevel@tonic-gate	    @hangulCE = map(pack(VCE_TEMPLATE, NON_VAR, @$_), &$hang($u));
584*0Sstevel@tonic-gate	}
585*0Sstevel@tonic-gate	elsif (!defined $hang) {
586*0Sstevel@tonic-gate	    @hangulCE = $der->($u);
587*0Sstevel@tonic-gate	}
588*0Sstevel@tonic-gate	else {
589*0Sstevel@tonic-gate	    my $max  = $self->{maxlength};
590*0Sstevel@tonic-gate	    my @decH = _decompHangul($u);
591*0Sstevel@tonic-gate
592*0Sstevel@tonic-gate	    if (@decH == 2) {
593*0Sstevel@tonic-gate		my $contract = join(CODE_SEP, @decH);
594*0Sstevel@tonic-gate		@decH = ($contract) if $map->{$contract};
595*0Sstevel@tonic-gate	    } else { # must be <@decH == 3>
596*0Sstevel@tonic-gate		if ($max->{$decH[0]}) {
597*0Sstevel@tonic-gate		    my $contract = join(CODE_SEP, @decH);
598*0Sstevel@tonic-gate		    if ($map->{$contract}) {
599*0Sstevel@tonic-gate			@decH = ($contract);
600*0Sstevel@tonic-gate		    } else {
601*0Sstevel@tonic-gate			$contract = join(CODE_SEP, @decH[0,1]);
602*0Sstevel@tonic-gate			$map->{$contract} and @decH = ($contract, $decH[2]);
603*0Sstevel@tonic-gate		    }
604*0Sstevel@tonic-gate		    # even if V's ignorable, LT contraction is not supported.
605*0Sstevel@tonic-gate		    # If such a situatution were required, NFD should be used.
606*0Sstevel@tonic-gate		}
607*0Sstevel@tonic-gate		if (@decH == 3 && $max->{$decH[1]}) {
608*0Sstevel@tonic-gate		    my $contract = join(CODE_SEP, @decH[1,2]);
609*0Sstevel@tonic-gate		    $map->{$contract} and @decH = ($decH[0], $contract);
610*0Sstevel@tonic-gate		}
611*0Sstevel@tonic-gate	    }
612*0Sstevel@tonic-gate
613*0Sstevel@tonic-gate	    @hangulCE = map({
614*0Sstevel@tonic-gate		    $map->{$_} ? @{ $map->{$_} } : $der->($_);
615*0Sstevel@tonic-gate		} @decH);
616*0Sstevel@tonic-gate	}
617*0Sstevel@tonic-gate	return map _varCE($vbl, $_), @hangulCE;
618*0Sstevel@tonic-gate    }
619*0Sstevel@tonic-gate    elsif (CJK_UidIni  <= $u && $u <= CJK_UidFin  ||
620*0Sstevel@tonic-gate	   CJK_ExtAIni <= $u && $u <= CJK_ExtAFin ||
621*0Sstevel@tonic-gate	   CJK_ExtBIni <= $u && $u <= CJK_ExtBFin) {
622*0Sstevel@tonic-gate	my $cjk  = $self->{overrideCJK};
623*0Sstevel@tonic-gate	return map _varCE($vbl, $_),
624*0Sstevel@tonic-gate	    $cjk
625*0Sstevel@tonic-gate		? map(pack(VCE_TEMPLATE, NON_VAR, @$_), &$cjk($u))
626*0Sstevel@tonic-gate		: defined $cjk && $self->{UCA_Version} <= 8 && $u <= BMP_Max
627*0Sstevel@tonic-gate		    ? pack(VCE_TEMPLATE, NON_VAR, $u, Min2Wt, Min3Wt, $u)
628*0Sstevel@tonic-gate		    : $der->($u);
629*0Sstevel@tonic-gate    }
630*0Sstevel@tonic-gate    else {
631*0Sstevel@tonic-gate	return map _varCE($vbl, $_), $der->($u);
632*0Sstevel@tonic-gate    }
633*0Sstevel@tonic-gate}
634*0Sstevel@tonic-gate
635*0Sstevel@tonic-gate
636*0Sstevel@tonic-gate##
637*0Sstevel@tonic-gate## string sortkey = getSortKey(string arg)
638*0Sstevel@tonic-gate##
639*0Sstevel@tonic-gatesub getSortKey
640*0Sstevel@tonic-gate{
641*0Sstevel@tonic-gate    my $self = shift;
642*0Sstevel@tonic-gate    my $lev  = $self->{level};
643*0Sstevel@tonic-gate    my $rEnt = $self->splitEnt(shift); # get an arrayref of JCPS
644*0Sstevel@tonic-gate    my $ver9 = $self->{UCA_Version} >= 9;
645*0Sstevel@tonic-gate    my $v2i  = $ver9 && $self->{variable} ne 'non-ignorable';
646*0Sstevel@tonic-gate
647*0Sstevel@tonic-gate    my @buf; # weight arrays
648*0Sstevel@tonic-gate    if ($self->{hangul_terminator}) {
649*0Sstevel@tonic-gate	my $preHST = '';
650*0Sstevel@tonic-gate	foreach my $jcps (@$rEnt) {
651*0Sstevel@tonic-gate	    # weird things like VL, TL-contraction are not considered!
652*0Sstevel@tonic-gate	    my $curHST = '';
653*0Sstevel@tonic-gate	    foreach my $u (split /;/, $jcps) {
654*0Sstevel@tonic-gate		$curHST .= getHST($u);
655*0Sstevel@tonic-gate	    }
656*0Sstevel@tonic-gate	    if ($preHST && !$curHST || # hangul before non-hangul
657*0Sstevel@tonic-gate		$preHST =~ /L\z/ && $curHST =~ /^T/ ||
658*0Sstevel@tonic-gate		$preHST =~ /V\z/ && $curHST =~ /^L/ ||
659*0Sstevel@tonic-gate		$preHST =~ /T\z/ && $curHST =~ /^[LV]/) {
660*0Sstevel@tonic-gate
661*0Sstevel@tonic-gate		push @buf, $self->getWtHangulTerm();
662*0Sstevel@tonic-gate	    }
663*0Sstevel@tonic-gate	    $preHST = $curHST;
664*0Sstevel@tonic-gate
665*0Sstevel@tonic-gate	    push @buf, $self->getWt($jcps);
666*0Sstevel@tonic-gate	}
667*0Sstevel@tonic-gate	$preHST # end at hangul
668*0Sstevel@tonic-gate	    and push @buf, $self->getWtHangulTerm();
669*0Sstevel@tonic-gate    }
670*0Sstevel@tonic-gate    else {
671*0Sstevel@tonic-gate	foreach my $jcps (@$rEnt) {
672*0Sstevel@tonic-gate	    push @buf, $self->getWt($jcps);
673*0Sstevel@tonic-gate	}
674*0Sstevel@tonic-gate    }
675*0Sstevel@tonic-gate
676*0Sstevel@tonic-gate    # make sort key
677*0Sstevel@tonic-gate    my @ret = ([],[],[],[]);
678*0Sstevel@tonic-gate    my $last_is_variable;
679*0Sstevel@tonic-gate
680*0Sstevel@tonic-gate    foreach my $vwt (@buf) {
681*0Sstevel@tonic-gate	my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
682*0Sstevel@tonic-gate	if ($v2i) {
683*0Sstevel@tonic-gate	    if ($var) {
684*0Sstevel@tonic-gate		$last_is_variable = TRUE;
685*0Sstevel@tonic-gate	    }
686*0Sstevel@tonic-gate	    elsif (!$wt[0]) { # ignorable
687*0Sstevel@tonic-gate		next if $last_is_variable;
688*0Sstevel@tonic-gate	    }
689*0Sstevel@tonic-gate	    else {
690*0Sstevel@tonic-gate		$last_is_variable = FALSE;
691*0Sstevel@tonic-gate	    }
692*0Sstevel@tonic-gate	}
693*0Sstevel@tonic-gate	foreach my $v (0..$lev-1) {
694*0Sstevel@tonic-gate	    0 < $wt[$v] and push @{ $ret[$v] }, $wt[$v];
695*0Sstevel@tonic-gate	}
696*0Sstevel@tonic-gate    }
697*0Sstevel@tonic-gate
698*0Sstevel@tonic-gate    # modification of tertiary weights
699*0Sstevel@tonic-gate    if ($self->{upper_before_lower}) {
700*0Sstevel@tonic-gate	foreach (@{ $ret[2] }) {
701*0Sstevel@tonic-gate	    if    (0x8 <= $_ && $_ <= 0xC) { $_ -= 6 } # lower
702*0Sstevel@tonic-gate	    elsif (0x2 <= $_ && $_ <= 0x6) { $_ += 6 } # upper
703*0Sstevel@tonic-gate	    elsif ($_ == 0x1C)             { $_ += 1 } # square upper
704*0Sstevel@tonic-gate	    elsif ($_ == 0x1D)             { $_ -= 1 } # square lower
705*0Sstevel@tonic-gate	}
706*0Sstevel@tonic-gate    }
707*0Sstevel@tonic-gate    if ($self->{katakana_before_hiragana}) {
708*0Sstevel@tonic-gate	foreach (@{ $ret[2] }) {
709*0Sstevel@tonic-gate	    if    (0x0F <= $_ && $_ <= 0x13) { $_ -= 2 } # katakana
710*0Sstevel@tonic-gate	    elsif (0x0D <= $_ && $_ <= 0x0E) { $_ += 5 } # hiragana
711*0Sstevel@tonic-gate	}
712*0Sstevel@tonic-gate    }
713*0Sstevel@tonic-gate
714*0Sstevel@tonic-gate    if ($self->{backwardsFlag}) {
715*0Sstevel@tonic-gate	for (my $v = MinLevel; $v <= MaxLevel; $v++) {
716*0Sstevel@tonic-gate	    if ($self->{backwardsFlag} & (1 << $v)) {
717*0Sstevel@tonic-gate		@{ $ret[$v-1] } = reverse @{ $ret[$v-1] };
718*0Sstevel@tonic-gate	    }
719*0Sstevel@tonic-gate	}
720*0Sstevel@tonic-gate    }
721*0Sstevel@tonic-gate
722*0Sstevel@tonic-gate    join LEVEL_SEP, map pack(KEY_TEMPLATE, @$_), @ret;
723*0Sstevel@tonic-gate}
724*0Sstevel@tonic-gate
725*0Sstevel@tonic-gate
726*0Sstevel@tonic-gate##
727*0Sstevel@tonic-gate## int compare = cmp(string a, string b)
728*0Sstevel@tonic-gate##
729*0Sstevel@tonic-gatesub cmp { $_[0]->getSortKey($_[1]) cmp $_[0]->getSortKey($_[2]) }
730*0Sstevel@tonic-gatesub eq  { $_[0]->getSortKey($_[1]) eq  $_[0]->getSortKey($_[2]) }
731*0Sstevel@tonic-gatesub ne  { $_[0]->getSortKey($_[1]) ne  $_[0]->getSortKey($_[2]) }
732*0Sstevel@tonic-gatesub lt  { $_[0]->getSortKey($_[1]) lt  $_[0]->getSortKey($_[2]) }
733*0Sstevel@tonic-gatesub le  { $_[0]->getSortKey($_[1]) le  $_[0]->getSortKey($_[2]) }
734*0Sstevel@tonic-gatesub gt  { $_[0]->getSortKey($_[1]) gt  $_[0]->getSortKey($_[2]) }
735*0Sstevel@tonic-gatesub ge  { $_[0]->getSortKey($_[1]) ge  $_[0]->getSortKey($_[2]) }
736*0Sstevel@tonic-gate
737*0Sstevel@tonic-gate##
738*0Sstevel@tonic-gate## list[strings] sorted = sort(list[strings] arg)
739*0Sstevel@tonic-gate##
740*0Sstevel@tonic-gatesub sort {
741*0Sstevel@tonic-gate    my $obj = shift;
742*0Sstevel@tonic-gate    return
743*0Sstevel@tonic-gate	map { $_->[1] }
744*0Sstevel@tonic-gate	    sort{ $a->[0] cmp $b->[0] }
745*0Sstevel@tonic-gate		map [ $obj->getSortKey($_), $_ ], @_;
746*0Sstevel@tonic-gate}
747*0Sstevel@tonic-gate
748*0Sstevel@tonic-gate
749*0Sstevel@tonic-gatesub _derivCE_9 {
750*0Sstevel@tonic-gate    my $u = shift;
751*0Sstevel@tonic-gate    my $base =
752*0Sstevel@tonic-gate	(CJK_UidIni  <= $u && $u <= CJK_UidFin)
753*0Sstevel@tonic-gate	    ? 0xFB40 : # CJK
754*0Sstevel@tonic-gate	(CJK_ExtAIni <= $u && $u <= CJK_ExtAFin ||
755*0Sstevel@tonic-gate	 CJK_ExtBIni <= $u && $u <= CJK_ExtBFin)
756*0Sstevel@tonic-gate	    ? 0xFB80   # CJK ext.
757*0Sstevel@tonic-gate	    : 0xFBC0;  # others
758*0Sstevel@tonic-gate
759*0Sstevel@tonic-gate    my $aaaa = $base + ($u >> 15);
760*0Sstevel@tonic-gate    my $bbbb = ($u & 0x7FFF) | 0x8000;
761*0Sstevel@tonic-gate    return
762*0Sstevel@tonic-gate	pack(VCE_TEMPLATE, NON_VAR, $aaaa, Min2Wt, Min3Wt, $u),
763*0Sstevel@tonic-gate	pack(VCE_TEMPLATE, NON_VAR, $bbbb,      0,      0, $u);
764*0Sstevel@tonic-gate}
765*0Sstevel@tonic-gate
766*0Sstevel@tonic-gatesub _derivCE_8 {
767*0Sstevel@tonic-gate    my $code = shift;
768*0Sstevel@tonic-gate    my $aaaa =  0xFF80 + ($code >> 15);
769*0Sstevel@tonic-gate    my $bbbb = ($code & 0x7FFF) | 0x8000;
770*0Sstevel@tonic-gate    return
771*0Sstevel@tonic-gate	pack(VCE_TEMPLATE, NON_VAR, $aaaa, 2, 1, $code),
772*0Sstevel@tonic-gate	pack(VCE_TEMPLATE, NON_VAR, $bbbb, 0, 0, $code);
773*0Sstevel@tonic-gate}
774*0Sstevel@tonic-gate
775*0Sstevel@tonic-gate
776*0Sstevel@tonic-gatesub getWtHangulTerm {
777*0Sstevel@tonic-gate    my $self = shift;
778*0Sstevel@tonic-gate    return _varCE($self->{variable},
779*0Sstevel@tonic-gate	pack(VCE_TEMPLATE, NON_VAR, $self->{hangul_terminator}, 0,0,0));
780*0Sstevel@tonic-gate}
781*0Sstevel@tonic-gate
782*0Sstevel@tonic-gate
783*0Sstevel@tonic-gate##
784*0Sstevel@tonic-gate## "hhhh hhhh hhhh" to (dddd, dddd, dddd)
785*0Sstevel@tonic-gate##
786*0Sstevel@tonic-gatesub _getHexArray { map hex, $_[0] =~ /([0-9a-fA-F]+)/g }
787*0Sstevel@tonic-gate
788*0Sstevel@tonic-gate#
789*0Sstevel@tonic-gate# $code *must* be in Hangul syllable.
790*0Sstevel@tonic-gate# Check it before you enter here.
791*0Sstevel@tonic-gate#
792*0Sstevel@tonic-gatesub _decompHangul {
793*0Sstevel@tonic-gate    my $code = shift;
794*0Sstevel@tonic-gate    my $SIndex = $code - Hangul_SBase;
795*0Sstevel@tonic-gate    my $LIndex = int( $SIndex / Hangul_NCount);
796*0Sstevel@tonic-gate    my $VIndex = int(($SIndex % Hangul_NCount) / Hangul_TCount);
797*0Sstevel@tonic-gate    my $TIndex =      $SIndex % Hangul_TCount;
798*0Sstevel@tonic-gate    return (
799*0Sstevel@tonic-gate	Hangul_LBase + $LIndex,
800*0Sstevel@tonic-gate	Hangul_VBase + $VIndex,
801*0Sstevel@tonic-gate	$TIndex ? (Hangul_TBase + $TIndex) : (),
802*0Sstevel@tonic-gate    );
803*0Sstevel@tonic-gate}
804*0Sstevel@tonic-gate
805*0Sstevel@tonic-gatesub _isIllegal {
806*0Sstevel@tonic-gate    my $code = shift;
807*0Sstevel@tonic-gate    return ! defined $code                      # removed
808*0Sstevel@tonic-gate	|| ($code < 0 || 0x10FFFF < $code)      # out of range
809*0Sstevel@tonic-gate	|| (($code & 0xFFFE) == 0xFFFE)         # ??FFF[EF] (cf. utf8.c)
810*0Sstevel@tonic-gate	|| (0xD800 <= $code && $code <= 0xDFFF) # unpaired surrogates
811*0Sstevel@tonic-gate	|| (0xFDD0 <= $code && $code <= 0xFDEF) # other non-characters
812*0Sstevel@tonic-gate    ;
813*0Sstevel@tonic-gate}
814*0Sstevel@tonic-gate
815*0Sstevel@tonic-gate# Hangul Syllable Type
816*0Sstevel@tonic-gatesub getHST {
817*0Sstevel@tonic-gate    my $u = shift;
818*0Sstevel@tonic-gate    return
819*0Sstevel@tonic-gate	Hangul_LIni <= $u && $u <= Hangul_LFin || $u == Hangul_LFill ? "L" :
820*0Sstevel@tonic-gate	Hangul_VIni <= $u && $u <= Hangul_VFin	     ? "V" :
821*0Sstevel@tonic-gate	Hangul_TIni <= $u && $u <= Hangul_TFin	     ? "T" :
822*0Sstevel@tonic-gate	Hangul_SIni <= $u && $u <= Hangul_SFin ?
823*0Sstevel@tonic-gate	    ($u - Hangul_SBase) % Hangul_TCount ? "LVT" : "LV" : "";
824*0Sstevel@tonic-gate}
825*0Sstevel@tonic-gate
826*0Sstevel@tonic-gate
827*0Sstevel@tonic-gate##
828*0Sstevel@tonic-gate## bool _nonIgnorAtLevel(arrayref weights, int level)
829*0Sstevel@tonic-gate##
830*0Sstevel@tonic-gatesub _nonIgnorAtLevel($$)
831*0Sstevel@tonic-gate{
832*0Sstevel@tonic-gate    my $wt = shift;
833*0Sstevel@tonic-gate    return if ! defined $wt;
834*0Sstevel@tonic-gate    my $lv = shift;
835*0Sstevel@tonic-gate    return grep($wt->[$_-1] != 0, MinLevel..$lv) ? TRUE : FALSE;
836*0Sstevel@tonic-gate}
837*0Sstevel@tonic-gate
838*0Sstevel@tonic-gate##
839*0Sstevel@tonic-gate## bool _eqArray(
840*0Sstevel@tonic-gate##    arrayref of arrayref[weights] source,
841*0Sstevel@tonic-gate##    arrayref of arrayref[weights] substr,
842*0Sstevel@tonic-gate##    int level)
843*0Sstevel@tonic-gate## * comparison of graphemes vs graphemes.
844*0Sstevel@tonic-gate##   @$source >= @$substr must be true (check it before call this);
845*0Sstevel@tonic-gate##
846*0Sstevel@tonic-gatesub _eqArray($$$)
847*0Sstevel@tonic-gate{
848*0Sstevel@tonic-gate    my $source = shift;
849*0Sstevel@tonic-gate    my $substr = shift;
850*0Sstevel@tonic-gate    my $lev = shift;
851*0Sstevel@tonic-gate
852*0Sstevel@tonic-gate    for my $g (0..@$substr-1){
853*0Sstevel@tonic-gate	# Do the $g'th graphemes have the same number of AV weigths?
854*0Sstevel@tonic-gate	return if @{ $source->[$g] } != @{ $substr->[$g] };
855*0Sstevel@tonic-gate
856*0Sstevel@tonic-gate	for my $w (0..@{ $substr->[$g] }-1) {
857*0Sstevel@tonic-gate	    for my $v (0..$lev-1) {
858*0Sstevel@tonic-gate		return if $source->[$g][$w][$v] != $substr->[$g][$w][$v];
859*0Sstevel@tonic-gate	    }
860*0Sstevel@tonic-gate	}
861*0Sstevel@tonic-gate    }
862*0Sstevel@tonic-gate    return 1;
863*0Sstevel@tonic-gate}
864*0Sstevel@tonic-gate
865*0Sstevel@tonic-gate##
866*0Sstevel@tonic-gate## (int position, int length)
867*0Sstevel@tonic-gate## int position = index(string, substring, position, [undoc'ed grobal])
868*0Sstevel@tonic-gate##
869*0Sstevel@tonic-gate## With "grobal" (only for the list context),
870*0Sstevel@tonic-gate##  returns list of arrayref[position, length].
871*0Sstevel@tonic-gate##
872*0Sstevel@tonic-gatesub index
873*0Sstevel@tonic-gate{
874*0Sstevel@tonic-gate    my $self = shift;
875*0Sstevel@tonic-gate    my $str  = shift;
876*0Sstevel@tonic-gate    my $len  = length($str);
877*0Sstevel@tonic-gate    my $subE = $self->splitEnt(shift);
878*0Sstevel@tonic-gate    my $pos  = @_ ? shift : 0;
879*0Sstevel@tonic-gate       $pos  = 0 if $pos < 0;
880*0Sstevel@tonic-gate    my $grob = shift;
881*0Sstevel@tonic-gate
882*0Sstevel@tonic-gate    my $lev  = $self->{level};
883*0Sstevel@tonic-gate    my $ver9 = $self->{UCA_Version} >= 9;
884*0Sstevel@tonic-gate    my $v2i  = $self->{variable} ne 'non-ignorable';
885*0Sstevel@tonic-gate
886*0Sstevel@tonic-gate    if (! @$subE) {
887*0Sstevel@tonic-gate	my $temp = $pos <= 0 ? 0 : $len <= $pos ? $len : $pos;
888*0Sstevel@tonic-gate	return $grob
889*0Sstevel@tonic-gate	    ? map([$_, 0], $temp..$len)
890*0Sstevel@tonic-gate	    : wantarray ? ($temp,0) : $temp;
891*0Sstevel@tonic-gate    }
892*0Sstevel@tonic-gate    $len < $pos
893*0Sstevel@tonic-gate	and return wantarray ? () : NOMATCHPOS;
894*0Sstevel@tonic-gate    my $strE = $self->splitEnt($pos ? substr($str, $pos) : $str, TRUE);
895*0Sstevel@tonic-gate    @$strE
896*0Sstevel@tonic-gate	or return wantarray ? () : NOMATCHPOS;
897*0Sstevel@tonic-gate
898*0Sstevel@tonic-gate    my(@strWt, @iniPos, @finPos, @subWt, @g_ret);
899*0Sstevel@tonic-gate
900*0Sstevel@tonic-gate    my $last_is_variable;
901*0Sstevel@tonic-gate    for my $vwt (map $self->getWt($_), @$subE) {
902*0Sstevel@tonic-gate	my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
903*0Sstevel@tonic-gate	my $to_be_pushed = _nonIgnorAtLevel(\@wt,$lev);
904*0Sstevel@tonic-gate
905*0Sstevel@tonic-gate	if ($v2i && $ver9) {
906*0Sstevel@tonic-gate	    if ($var) {
907*0Sstevel@tonic-gate		$last_is_variable = TRUE;
908*0Sstevel@tonic-gate	    }
909*0Sstevel@tonic-gate	    elsif (!$wt[0]) { # ignorable
910*0Sstevel@tonic-gate		$to_be_pushed = FALSE if $last_is_variable;
911*0Sstevel@tonic-gate	    }
912*0Sstevel@tonic-gate	    else {
913*0Sstevel@tonic-gate		$last_is_variable = FALSE;
914*0Sstevel@tonic-gate	    }
915*0Sstevel@tonic-gate	}
916*0Sstevel@tonic-gate
917*0Sstevel@tonic-gate	if (@subWt && !$var && !$wt[0]) {
918*0Sstevel@tonic-gate	    push @{ $subWt[-1] }, \@wt if $to_be_pushed;
919*0Sstevel@tonic-gate	} else {
920*0Sstevel@tonic-gate	    push @subWt, [ \@wt ];
921*0Sstevel@tonic-gate	}
922*0Sstevel@tonic-gate    }
923*0Sstevel@tonic-gate
924*0Sstevel@tonic-gate    my $count = 0;
925*0Sstevel@tonic-gate    my $end = @$strE - 1;
926*0Sstevel@tonic-gate
927*0Sstevel@tonic-gate    $last_is_variable = FALSE; # reuse
928*0Sstevel@tonic-gate    for (my $i = 0; $i <= $end; ) { # no $i++
929*0Sstevel@tonic-gate	my $found_base = 0;
930*0Sstevel@tonic-gate
931*0Sstevel@tonic-gate	# fetch a grapheme
932*0Sstevel@tonic-gate	while ($i <= $end && $found_base == 0) {
933*0Sstevel@tonic-gate	    for my $vwt ($self->getWt($strE->[$i][0])) {
934*0Sstevel@tonic-gate		my($var, @wt) = unpack(VCE_TEMPLATE, $vwt);
935*0Sstevel@tonic-gate		my $to_be_pushed = _nonIgnorAtLevel(\@wt,$lev);
936*0Sstevel@tonic-gate
937*0Sstevel@tonic-gate		if ($v2i && $ver9) {
938*0Sstevel@tonic-gate		    if ($var) {
939*0Sstevel@tonic-gate			$last_is_variable = TRUE;
940*0Sstevel@tonic-gate		    }
941*0Sstevel@tonic-gate		    elsif (!$wt[0]) { # ignorable
942*0Sstevel@tonic-gate			$to_be_pushed = FALSE if $last_is_variable;
943*0Sstevel@tonic-gate		    }
944*0Sstevel@tonic-gate		    else {
945*0Sstevel@tonic-gate			$last_is_variable = FALSE;
946*0Sstevel@tonic-gate		    }
947*0Sstevel@tonic-gate		}
948*0Sstevel@tonic-gate
949*0Sstevel@tonic-gate		if (@strWt && !$var && !$wt[0]) {
950*0Sstevel@tonic-gate		    push @{ $strWt[-1] }, \@wt if $to_be_pushed;
951*0Sstevel@tonic-gate		    $finPos[-1] = $strE->[$i][2];
952*0Sstevel@tonic-gate		} elsif ($to_be_pushed) {
953*0Sstevel@tonic-gate		    push @strWt, [ \@wt ];
954*0Sstevel@tonic-gate		    push @iniPos, $found_base ? NOMATCHPOS : $strE->[$i][1];
955*0Sstevel@tonic-gate		    $finPos[-1] = NOMATCHPOS if $found_base;
956*0Sstevel@tonic-gate		    push @finPos, $strE->[$i][2];
957*0Sstevel@tonic-gate		    $found_base++;
958*0Sstevel@tonic-gate		}
959*0Sstevel@tonic-gate		# else ===> no-op
960*0Sstevel@tonic-gate	    }
961*0Sstevel@tonic-gate	    $i++;
962*0Sstevel@tonic-gate	}
963*0Sstevel@tonic-gate
964*0Sstevel@tonic-gate	# try to match
965*0Sstevel@tonic-gate	while ( @strWt > @subWt || (@strWt == @subWt && $i > $end) ) {
966*0Sstevel@tonic-gate	    if ($iniPos[0] != NOMATCHPOS &&
967*0Sstevel@tonic-gate		    $finPos[$#subWt] != NOMATCHPOS &&
968*0Sstevel@tonic-gate			_eqArray(\@strWt, \@subWt, $lev)) {
969*0Sstevel@tonic-gate		my $temp = $iniPos[0] + $pos;
970*0Sstevel@tonic-gate
971*0Sstevel@tonic-gate		if ($grob) {
972*0Sstevel@tonic-gate		    push @g_ret, [$temp, $finPos[$#subWt] - $iniPos[0]];
973*0Sstevel@tonic-gate		    splice @strWt,  0, $#subWt;
974*0Sstevel@tonic-gate		    splice @iniPos, 0, $#subWt;
975*0Sstevel@tonic-gate		    splice @finPos, 0, $#subWt;
976*0Sstevel@tonic-gate		}
977*0Sstevel@tonic-gate		else {
978*0Sstevel@tonic-gate		    return wantarray
979*0Sstevel@tonic-gate			? ($temp, $finPos[$#subWt] - $iniPos[0])
980*0Sstevel@tonic-gate			:  $temp;
981*0Sstevel@tonic-gate		}
982*0Sstevel@tonic-gate	    }
983*0Sstevel@tonic-gate	    shift @strWt;
984*0Sstevel@tonic-gate	    shift @iniPos;
985*0Sstevel@tonic-gate	    shift @finPos;
986*0Sstevel@tonic-gate	}
987*0Sstevel@tonic-gate    }
988*0Sstevel@tonic-gate
989*0Sstevel@tonic-gate    return $grob
990*0Sstevel@tonic-gate	? @g_ret
991*0Sstevel@tonic-gate	: wantarray ? () : NOMATCHPOS;
992*0Sstevel@tonic-gate}
993*0Sstevel@tonic-gate
994*0Sstevel@tonic-gate##
995*0Sstevel@tonic-gate## scalarref to matching part = match(string, substring)
996*0Sstevel@tonic-gate##
997*0Sstevel@tonic-gatesub match
998*0Sstevel@tonic-gate{
999*0Sstevel@tonic-gate    my $self = shift;
1000*0Sstevel@tonic-gate    if (my($pos,$len) = $self->index($_[0], $_[1])) {
1001*0Sstevel@tonic-gate	my $temp = substr($_[0], $pos, $len);
1002*0Sstevel@tonic-gate	return wantarray ? $temp : \$temp;
1003*0Sstevel@tonic-gate	# An lvalue ref \substr should be avoided,
1004*0Sstevel@tonic-gate	# since its value is affected by modification of its referent.
1005*0Sstevel@tonic-gate    }
1006*0Sstevel@tonic-gate    else {
1007*0Sstevel@tonic-gate	return;
1008*0Sstevel@tonic-gate    }
1009*0Sstevel@tonic-gate}
1010*0Sstevel@tonic-gate
1011*0Sstevel@tonic-gate##
1012*0Sstevel@tonic-gate## arrayref matching parts = gmatch(string, substring)
1013*0Sstevel@tonic-gate##
1014*0Sstevel@tonic-gatesub gmatch
1015*0Sstevel@tonic-gate{
1016*0Sstevel@tonic-gate    my $self = shift;
1017*0Sstevel@tonic-gate    my $str  = shift;
1018*0Sstevel@tonic-gate    my $sub  = shift;
1019*0Sstevel@tonic-gate    return map substr($str, $_->[0], $_->[1]),
1020*0Sstevel@tonic-gate		$self->index($str, $sub, 0, 'g');
1021*0Sstevel@tonic-gate}
1022*0Sstevel@tonic-gate
1023*0Sstevel@tonic-gate##
1024*0Sstevel@tonic-gate## bool subst'ed = subst(string, substring, replace)
1025*0Sstevel@tonic-gate##
1026*0Sstevel@tonic-gatesub subst
1027*0Sstevel@tonic-gate{
1028*0Sstevel@tonic-gate    my $self = shift;
1029*0Sstevel@tonic-gate    my $code = ref $_[2] eq 'CODE' ? $_[2] : FALSE;
1030*0Sstevel@tonic-gate
1031*0Sstevel@tonic-gate    if (my($pos,$len) = $self->index($_[0], $_[1])) {
1032*0Sstevel@tonic-gate	if ($code) {
1033*0Sstevel@tonic-gate	    my $mat = substr($_[0], $pos, $len);
1034*0Sstevel@tonic-gate	    substr($_[0], $pos, $len, $code->($mat));
1035*0Sstevel@tonic-gate	} else {
1036*0Sstevel@tonic-gate	    substr($_[0], $pos, $len, $_[2]);
1037*0Sstevel@tonic-gate	}
1038*0Sstevel@tonic-gate	return TRUE;
1039*0Sstevel@tonic-gate    }
1040*0Sstevel@tonic-gate    else {
1041*0Sstevel@tonic-gate	return FALSE;
1042*0Sstevel@tonic-gate    }
1043*0Sstevel@tonic-gate}
1044*0Sstevel@tonic-gate
1045*0Sstevel@tonic-gate##
1046*0Sstevel@tonic-gate## int count = gsubst(string, substring, replace)
1047*0Sstevel@tonic-gate##
1048*0Sstevel@tonic-gatesub gsubst
1049*0Sstevel@tonic-gate{
1050*0Sstevel@tonic-gate    my $self = shift;
1051*0Sstevel@tonic-gate    my $code = ref $_[2] eq 'CODE' ? $_[2] : FALSE;
1052*0Sstevel@tonic-gate    my $cnt = 0;
1053*0Sstevel@tonic-gate
1054*0Sstevel@tonic-gate    # Replacement is carried out from the end, then use reverse.
1055*0Sstevel@tonic-gate    for my $pos_len (reverse $self->index($_[0], $_[1], 0, 'g')) {
1056*0Sstevel@tonic-gate	if ($code) {
1057*0Sstevel@tonic-gate	    my $mat = substr($_[0], $pos_len->[0], $pos_len->[1]);
1058*0Sstevel@tonic-gate	    substr($_[0], $pos_len->[0], $pos_len->[1], $code->($mat));
1059*0Sstevel@tonic-gate	} else {
1060*0Sstevel@tonic-gate	    substr($_[0], $pos_len->[0], $pos_len->[1], $_[2]);
1061*0Sstevel@tonic-gate	}
1062*0Sstevel@tonic-gate	$cnt++;
1063*0Sstevel@tonic-gate    }
1064*0Sstevel@tonic-gate    return $cnt;
1065*0Sstevel@tonic-gate}
1066*0Sstevel@tonic-gate
1067*0Sstevel@tonic-gate1;
1068*0Sstevel@tonic-gate__END__
1069*0Sstevel@tonic-gate
1070*0Sstevel@tonic-gate=head1 NAME
1071*0Sstevel@tonic-gate
1072*0Sstevel@tonic-gateUnicode::Collate - Unicode Collation Algorithm
1073*0Sstevel@tonic-gate
1074*0Sstevel@tonic-gate=head1 SYNOPSIS
1075*0Sstevel@tonic-gate
1076*0Sstevel@tonic-gate  use Unicode::Collate;
1077*0Sstevel@tonic-gate
1078*0Sstevel@tonic-gate  #construct
1079*0Sstevel@tonic-gate  $Collator = Unicode::Collate->new(%tailoring);
1080*0Sstevel@tonic-gate
1081*0Sstevel@tonic-gate  #sort
1082*0Sstevel@tonic-gate  @sorted = $Collator->sort(@not_sorted);
1083*0Sstevel@tonic-gate
1084*0Sstevel@tonic-gate  #compare
1085*0Sstevel@tonic-gate  $result = $Collator->cmp($a, $b); # returns 1, 0, or -1.
1086*0Sstevel@tonic-gate
1087*0Sstevel@tonic-gate  # If %tailoring is false (i.e. empty),
1088*0Sstevel@tonic-gate  # $Collator should do the default collation.
1089*0Sstevel@tonic-gate
1090*0Sstevel@tonic-gate=head1 DESCRIPTION
1091*0Sstevel@tonic-gate
1092*0Sstevel@tonic-gateThis module is an implementation
1093*0Sstevel@tonic-gateof Unicode Technical Standard #10 (UTS #10)
1094*0Sstevel@tonic-gate"Unicode Collation Algorithm."
1095*0Sstevel@tonic-gate
1096*0Sstevel@tonic-gate=head2 Constructor and Tailoring
1097*0Sstevel@tonic-gate
1098*0Sstevel@tonic-gateThe C<new> method returns a collator object.
1099*0Sstevel@tonic-gate
1100*0Sstevel@tonic-gate   $Collator = Unicode::Collate->new(
1101*0Sstevel@tonic-gate      UCA_Version => $UCA_Version,
1102*0Sstevel@tonic-gate      alternate => $alternate, # deprecated: use of 'variable' is recommended.
1103*0Sstevel@tonic-gate      backwards => $levelNumber, # or \@levelNumbers
1104*0Sstevel@tonic-gate      entry => $element,
1105*0Sstevel@tonic-gate      hangul_terminator => $term_primary_weight,
1106*0Sstevel@tonic-gate      ignoreName => qr/$ignoreName/,
1107*0Sstevel@tonic-gate      ignoreChar => qr/$ignoreChar/,
1108*0Sstevel@tonic-gate      katakana_before_hiragana => $bool,
1109*0Sstevel@tonic-gate      level => $collationLevel,
1110*0Sstevel@tonic-gate      normalization  => $normalization_form,
1111*0Sstevel@tonic-gate      overrideCJK => \&overrideCJK,
1112*0Sstevel@tonic-gate      overrideHangul => \&overrideHangul,
1113*0Sstevel@tonic-gate      preprocess => \&preprocess,
1114*0Sstevel@tonic-gate      rearrange => \@charList,
1115*0Sstevel@tonic-gate      table => $filename,
1116*0Sstevel@tonic-gate      undefName => qr/$undefName/,
1117*0Sstevel@tonic-gate      undefChar => qr/$undefChar/,
1118*0Sstevel@tonic-gate      upper_before_lower => $bool,
1119*0Sstevel@tonic-gate      variable => $variable,
1120*0Sstevel@tonic-gate   );
1121*0Sstevel@tonic-gate
1122*0Sstevel@tonic-gate=over 4
1123*0Sstevel@tonic-gate
1124*0Sstevel@tonic-gate=item UCA_Version
1125*0Sstevel@tonic-gate
1126*0Sstevel@tonic-gateIf the tracking version number of the older UCA is given,
1127*0Sstevel@tonic-gatethe older behavior of that tracking version is emulated on collating.
1128*0Sstevel@tonic-gateIf omitted, the return value of C<UCA_Version()> is used.
1129*0Sstevel@tonic-gate
1130*0Sstevel@tonic-gateThe supported tracking version: 8, 9, or 11.
1131*0Sstevel@tonic-gate
1132*0Sstevel@tonic-gateB<This parameter may be removed in the future version,
1133*0Sstevel@tonic-gateas switching the algorithm would affect the performance.>
1134*0Sstevel@tonic-gate
1135*0Sstevel@tonic-gate=item backwards
1136*0Sstevel@tonic-gate
1137*0Sstevel@tonic-gate-- see 3.1.2 French Accents, UTS #10.
1138*0Sstevel@tonic-gate
1139*0Sstevel@tonic-gate     backwards => $levelNumber or \@levelNumbers
1140*0Sstevel@tonic-gate
1141*0Sstevel@tonic-gateWeights in reverse order; ex. level 2 (diacritic ordering) in French.
1142*0Sstevel@tonic-gateIf omitted, forwards at all the levels.
1143*0Sstevel@tonic-gate
1144*0Sstevel@tonic-gate=item entry
1145*0Sstevel@tonic-gate
1146*0Sstevel@tonic-gate-- see 3.1 Linguistic Features; 3.2.1 File Format, UTS #10.
1147*0Sstevel@tonic-gate
1148*0Sstevel@tonic-gateIf the same character (or a sequence of characters) exists
1149*0Sstevel@tonic-gatein the collation element table through C<table>,
1150*0Sstevel@tonic-gatemapping to collation elements is overrided.
1151*0Sstevel@tonic-gateIf it does not exist, the mapping is defined additionally.
1152*0Sstevel@tonic-gate
1153*0Sstevel@tonic-gate    entry => <<'ENTRY', # for DUCET v4.0.0 (allkeys-4.0.0.txt)
1154*0Sstevel@tonic-gate0063 0068 ; [.0E6A.0020.0002.0063] # ch
1155*0Sstevel@tonic-gate0043 0068 ; [.0E6A.0020.0007.0043] # Ch
1156*0Sstevel@tonic-gate0043 0048 ; [.0E6A.0020.0008.0043] # CH
1157*0Sstevel@tonic-gate006C 006C ; [.0F4C.0020.0002.006C] # ll
1158*0Sstevel@tonic-gate004C 006C ; [.0F4C.0020.0007.004C] # Ll
1159*0Sstevel@tonic-gate004C 004C ; [.0F4C.0020.0008.004C] # LL
1160*0Sstevel@tonic-gate006E 0303 ; [.0F7B.0020.0002.006E] # n-tilde
1161*0Sstevel@tonic-gate004E 0303 ; [.0F7B.0020.0008.004E] # N-tilde
1162*0Sstevel@tonic-gateENTRY
1163*0Sstevel@tonic-gate
1164*0Sstevel@tonic-gate    entry => <<'ENTRY', # for DUCET v4.0.0 (allkeys-4.0.0.txt)
1165*0Sstevel@tonic-gate00E6 ; [.0E33.0020.0002.00E6][.0E8B.0020.0002.00E6] # ae ligature as <a><e>
1166*0Sstevel@tonic-gate00C6 ; [.0E33.0020.0008.00C6][.0E8B.0020.0008.00C6] # AE ligature as <A><E>
1167*0Sstevel@tonic-gateENTRY
1168*0Sstevel@tonic-gate
1169*0Sstevel@tonic-gateB<NOTE:> The code point in the UCA file format (before C<';'>)
1170*0Sstevel@tonic-gateB<must> be a Unicode code point (defined as hexadecimal),
1171*0Sstevel@tonic-gatebut not a native code point.
1172*0Sstevel@tonic-gateSo C<0063> must always denote C<U+0063>,
1173*0Sstevel@tonic-gatebut not a character of C<"\x63">.
1174*0Sstevel@tonic-gate
1175*0Sstevel@tonic-gateWeighting may vary depending on collation element table.
1176*0Sstevel@tonic-gateSo ensure the weights defined in C<entry> will be consistent with
1177*0Sstevel@tonic-gatethose in the collation element table loaded via C<table>.
1178*0Sstevel@tonic-gate
1179*0Sstevel@tonic-gateIn DUCET v4.0.0, primary weight of C<C> is C<0E60>
1180*0Sstevel@tonic-gateand that of C<D> is C<0E6D>. So setting primary weight of C<CH> to C<0E6A>
1181*0Sstevel@tonic-gate(as a value between C<0E60> and C<0E6D>)
1182*0Sstevel@tonic-gatemakes ordering as C<C E<lt> CH E<lt> D>.
1183*0Sstevel@tonic-gateExactly speaking DUCET already has some characters between C<C> and C<D>:
1184*0Sstevel@tonic-gateC<small capital C> (C<U+1D04>) with primary weight C<0E64>,
1185*0Sstevel@tonic-gateC<c-hook/C-hook> (C<U+0188/U+0187>) with C<0E65>,
1186*0Sstevel@tonic-gateand C<c-curl> (C<U+0255>) with C<0E69>.
1187*0Sstevel@tonic-gateThen primary weight C<0E6A> for C<CH> makes C<CH>
1188*0Sstevel@tonic-gateordered between C<c-curl> and C<D>.
1189*0Sstevel@tonic-gate
1190*0Sstevel@tonic-gate=item hangul_terminator
1191*0Sstevel@tonic-gate
1192*0Sstevel@tonic-gate-- see Condition B.2. in 7.1.4 Trailing Weights, UTS #10.
1193*0Sstevel@tonic-gate
1194*0Sstevel@tonic-gateIf a true value is given (non-zero but should be positive),
1195*0Sstevel@tonic-gateit will be added as a terminator primary weight to the end of
1196*0Sstevel@tonic-gateevery standard Hangul syllable. Secondary and any higher weights
1197*0Sstevel@tonic-gatefor terminator are set to zero.
1198*0Sstevel@tonic-gateIf the value is false or C<hangul_terminator> key does not exist,
1199*0Sstevel@tonic-gateinsertion of terminator weights will not be performed.
1200*0Sstevel@tonic-gate
1201*0Sstevel@tonic-gateBoundaries of Hangul syllables are determined
1202*0Sstevel@tonic-gateaccording to conjoining Jamo behavior in F<the Unicode Standard>
1203*0Sstevel@tonic-gateand F<HangulSyllableType.txt>.
1204*0Sstevel@tonic-gate
1205*0Sstevel@tonic-gateB<Implementation Note:>
1206*0Sstevel@tonic-gate(1) For expansion mapping (Unicode character mapped
1207*0Sstevel@tonic-gateto a sequence of collation elements), a terminator will not be added
1208*0Sstevel@tonic-gatebetween collation elements, even if Hangul syllable boundary exists there.
1209*0Sstevel@tonic-gateAddition of terminator is restricted to the next position
1210*0Sstevel@tonic-gateto the last collation element.
1211*0Sstevel@tonic-gate
1212*0Sstevel@tonic-gate(2) Non-conjoining Hangul letters
1213*0Sstevel@tonic-gate(Compatibility Jamo, halfwidth Jamo, and enclosed letters) are not
1214*0Sstevel@tonic-gateautomatically terminated with a terminator primary weight.
1215*0Sstevel@tonic-gateThese characters may need terminator included in a collation element
1216*0Sstevel@tonic-gatetable beforehand.
1217*0Sstevel@tonic-gate
1218*0Sstevel@tonic-gate=item ignoreName
1219*0Sstevel@tonic-gate
1220*0Sstevel@tonic-gate=item ignoreChar
1221*0Sstevel@tonic-gate
1222*0Sstevel@tonic-gate-- see Completely Ignorable, 3.2.2 Variable Weighting, UTS #10.
1223*0Sstevel@tonic-gate
1224*0Sstevel@tonic-gateMakes the entry in the table completely ignorable;
1225*0Sstevel@tonic-gatei.e. as if the weights were zero at all level.
1226*0Sstevel@tonic-gate
1227*0Sstevel@tonic-gateE.g. when 'a' and 'e' are ignorable,
1228*0Sstevel@tonic-gate'element' is equal to 'lament' (or 'lmnt').
1229*0Sstevel@tonic-gate
1230*0Sstevel@tonic-gate=item level
1231*0Sstevel@tonic-gate
1232*0Sstevel@tonic-gate-- see 4.3 Form a sort key for each string, UTS #10.
1233*0Sstevel@tonic-gate
1234*0Sstevel@tonic-gateSet the maximum level.
1235*0Sstevel@tonic-gateAny higher levels than the specified one are ignored.
1236*0Sstevel@tonic-gate
1237*0Sstevel@tonic-gate  Level 1: alphabetic ordering
1238*0Sstevel@tonic-gate  Level 2: diacritic ordering
1239*0Sstevel@tonic-gate  Level 3: case ordering
1240*0Sstevel@tonic-gate  Level 4: tie-breaking (e.g. in the case when variable is 'shifted')
1241*0Sstevel@tonic-gate
1242*0Sstevel@tonic-gate  ex.level => 2,
1243*0Sstevel@tonic-gate
1244*0Sstevel@tonic-gateIf omitted, the maximum is the 4th.
1245*0Sstevel@tonic-gate
1246*0Sstevel@tonic-gate=item normalization
1247*0Sstevel@tonic-gate
1248*0Sstevel@tonic-gate-- see 4.1 Normalize each input string, UTS #10.
1249*0Sstevel@tonic-gate
1250*0Sstevel@tonic-gateIf specified, strings are normalized before preparation of sort keys
1251*0Sstevel@tonic-gate(the normalization is executed after preprocess).
1252*0Sstevel@tonic-gate
1253*0Sstevel@tonic-gateA form name C<Unicode::Normalize::normalize()> accepts will be applied
1254*0Sstevel@tonic-gateas C<$normalization_form>.
1255*0Sstevel@tonic-gateAcceptable names include C<'NFD'>, C<'NFC'>, C<'NFKD'>, and C<'NFKC'>.
1256*0Sstevel@tonic-gateSee C<Unicode::Normalize::normalize()> for detail.
1257*0Sstevel@tonic-gateIf omitted, C<'NFD'> is used.
1258*0Sstevel@tonic-gate
1259*0Sstevel@tonic-gateC<normalization> is performed after C<preprocess> (if defined).
1260*0Sstevel@tonic-gate
1261*0Sstevel@tonic-gateFurthermore, special values, C<undef> and C<"prenormalized">, can be used,
1262*0Sstevel@tonic-gatethough they are not concerned with C<Unicode::Normalize::normalize()>.
1263*0Sstevel@tonic-gate
1264*0Sstevel@tonic-gateIf C<undef> (not a string C<"undef">) is passed explicitly
1265*0Sstevel@tonic-gateas the value for this key,
1266*0Sstevel@tonic-gateany normalization is not carried out (this may make tailoring easier
1267*0Sstevel@tonic-gateif any normalization is not desired). Under C<(normalization =E<gt> undef)>,
1268*0Sstevel@tonic-gateonly contiguous contractions are resolved;
1269*0Sstevel@tonic-gatee.g. even if C<A-ring> (and C<A-ring-cedilla>) is ordered after C<Z>,
1270*0Sstevel@tonic-gateC<A-cedilla-ring> would be primary equal to C<A>.
1271*0Sstevel@tonic-gateIn this point,
1272*0Sstevel@tonic-gateC<(normalization =E<gt> undef, preprocess =E<gt> sub { NFD(shift) })>
1273*0Sstevel@tonic-gateB<is not> equivalent to C<(normalization =E<gt> 'NFD')>.
1274*0Sstevel@tonic-gate
1275*0Sstevel@tonic-gateIn the case of C<(normalization =E<gt> "prenormalized")>,
1276*0Sstevel@tonic-gateany normalization is not performed, but
1277*0Sstevel@tonic-gatenon-contiguous contractions with combining characters are performed.
1278*0Sstevel@tonic-gateTherefore
1279*0Sstevel@tonic-gateC<(normalization =E<gt> 'prenormalized', preprocess =E<gt> sub { NFD(shift) })>
1280*0Sstevel@tonic-gateB<is> equivalent to C<(normalization =E<gt> 'NFD')>.
1281*0Sstevel@tonic-gateIf source strings are finely prenormalized,
1282*0Sstevel@tonic-gateC<(normalization =E<gt> 'prenormalized')> may save time for normalization.
1283*0Sstevel@tonic-gate
1284*0Sstevel@tonic-gateExcept C<(normalization =E<gt> undef)>,
1285*0Sstevel@tonic-gateB<Unicode::Normalize> is required (see also B<CAVEAT>).
1286*0Sstevel@tonic-gate
1287*0Sstevel@tonic-gate=item overrideCJK
1288*0Sstevel@tonic-gate
1289*0Sstevel@tonic-gate-- see 7.1 Derived Collation Elements, UTS #10.
1290*0Sstevel@tonic-gate
1291*0Sstevel@tonic-gateBy default, CJK Unified Ideographs are ordered in Unicode codepoint order
1292*0Sstevel@tonic-gate(but C<CJK Unified Ideographs> [C<U+4E00> to C<U+9FA5>]  are lesser than
1293*0Sstevel@tonic-gateC<CJK Unified Ideographs Extension> [C<U+3400> to C<U+4DB5> and
1294*0Sstevel@tonic-gateC<U+20000> to C<U+2A6D6>].
1295*0Sstevel@tonic-gate
1296*0Sstevel@tonic-gateThrough C<overrideCJK>, ordering of CJK Unified Ideographs can be overrided.
1297*0Sstevel@tonic-gate
1298*0Sstevel@tonic-gateex. CJK Unified Ideographs in the JIS code point order.
1299*0Sstevel@tonic-gate
1300*0Sstevel@tonic-gate  overrideCJK => sub {
1301*0Sstevel@tonic-gate      my $u = shift;             # get a Unicode codepoint
1302*0Sstevel@tonic-gate      my $b = pack('n', $u);     # to UTF-16BE
1303*0Sstevel@tonic-gate      my $s = your_unicode_to_sjis_converter($b); # convert
1304*0Sstevel@tonic-gate      my $n = unpack('n', $s);   # convert sjis to short
1305*0Sstevel@tonic-gate      [ $n, 0x20, 0x2, $u ];     # return the collation element
1306*0Sstevel@tonic-gate  },
1307*0Sstevel@tonic-gate
1308*0Sstevel@tonic-gateex. ignores all CJK Unified Ideographs.
1309*0Sstevel@tonic-gate
1310*0Sstevel@tonic-gate  overrideCJK => sub {()}, # CODEREF returning empty list
1311*0Sstevel@tonic-gate
1312*0Sstevel@tonic-gate   # where ->eq("Pe\x{4E00}rl", "Perl") is true
1313*0Sstevel@tonic-gate   # as U+4E00 is a CJK Unified Ideograph and to be ignorable.
1314*0Sstevel@tonic-gate
1315*0Sstevel@tonic-gateIf C<undef> is passed explicitly as the value for this key,
1316*0Sstevel@tonic-gateweights for CJK Unified Ideographs are treated as undefined.
1317*0Sstevel@tonic-gateBut assignment of weight for CJK Unified Ideographs
1318*0Sstevel@tonic-gatein table or C<entry> is still valid.
1319*0Sstevel@tonic-gate
1320*0Sstevel@tonic-gate=item overrideHangul
1321*0Sstevel@tonic-gate
1322*0Sstevel@tonic-gate-- see 7.1 Derived Collation Elements, UTS #10.
1323*0Sstevel@tonic-gate
1324*0Sstevel@tonic-gateBy default, Hangul Syllables are decomposed into Hangul Jamo,
1325*0Sstevel@tonic-gateeven if C<(normalization =E<gt> undef)>.
1326*0Sstevel@tonic-gateBut the mapping of Hangul Syllables may be overrided.
1327*0Sstevel@tonic-gate
1328*0Sstevel@tonic-gateThis tag works like C<overrideCJK>, so see there for examples.
1329*0Sstevel@tonic-gate
1330*0Sstevel@tonic-gateIf you want to override the mapping of Hangul Syllables,
1331*0Sstevel@tonic-gateNFD, NFKD, and FCD are not appropriate,
1332*0Sstevel@tonic-gatesince they will decompose Hangul Syllables before overriding.
1333*0Sstevel@tonic-gate
1334*0Sstevel@tonic-gateIf C<undef> is passed explicitly as the value for this key,
1335*0Sstevel@tonic-gateweight for Hangul Syllables is treated as undefined
1336*0Sstevel@tonic-gatewithout decomposition into Hangul Jamo.
1337*0Sstevel@tonic-gateBut definition of weight for Hangul Syllables
1338*0Sstevel@tonic-gatein table or C<entry> is still valid.
1339*0Sstevel@tonic-gate
1340*0Sstevel@tonic-gate=item preprocess
1341*0Sstevel@tonic-gate
1342*0Sstevel@tonic-gate-- see 5.1 Preprocessing, UTS #10.
1343*0Sstevel@tonic-gate
1344*0Sstevel@tonic-gateIf specified, the coderef is used to preprocess
1345*0Sstevel@tonic-gatebefore the formation of sort keys.
1346*0Sstevel@tonic-gate
1347*0Sstevel@tonic-gateex. dropping English articles, such as "a" or "the".
1348*0Sstevel@tonic-gateThen, "the pen" is before "a pencil".
1349*0Sstevel@tonic-gate
1350*0Sstevel@tonic-gate     preprocess => sub {
1351*0Sstevel@tonic-gate           my $str = shift;
1352*0Sstevel@tonic-gate           $str =~ s/\b(?:an?|the)\s+//gi;
1353*0Sstevel@tonic-gate           return $str;
1354*0Sstevel@tonic-gate        },
1355*0Sstevel@tonic-gate
1356*0Sstevel@tonic-gateC<preprocess> is performed before C<normalization> (if defined).
1357*0Sstevel@tonic-gate
1358*0Sstevel@tonic-gate=item rearrange
1359*0Sstevel@tonic-gate
1360*0Sstevel@tonic-gate-- see 3.1.3 Rearrangement, UTS #10.
1361*0Sstevel@tonic-gate
1362*0Sstevel@tonic-gateCharacters that are not coded in logical order and to be rearranged.
1363*0Sstevel@tonic-gateBy default,
1364*0Sstevel@tonic-gate
1365*0Sstevel@tonic-gate    rearrange => [ 0x0E40..0x0E44, 0x0EC0..0x0EC4 ],
1366*0Sstevel@tonic-gate
1367*0Sstevel@tonic-gateIf you want to disallow any rearrangement,
1368*0Sstevel@tonic-gatepass C<undef> or C<[]> (a reference to an empty list)
1369*0Sstevel@tonic-gateas the value for this key.
1370*0Sstevel@tonic-gate
1371*0Sstevel@tonic-gateB<According to the version 9 of UCA, this parameter shall not be used;
1372*0Sstevel@tonic-gatebut it is not warned at present.>
1373*0Sstevel@tonic-gate
1374*0Sstevel@tonic-gate=item table
1375*0Sstevel@tonic-gate
1376*0Sstevel@tonic-gate-- see 3.2 Default Unicode Collation Element Table, UTS #10.
1377*0Sstevel@tonic-gate
1378*0Sstevel@tonic-gateYou can use another collation element table if desired.
1379*0Sstevel@tonic-gateThe table file must be put into a directory
1380*0Sstevel@tonic-gatewhere F<Unicode/Collate.pm> is installed; e.g. into
1381*0Sstevel@tonic-gateF<perl/lib/Unicode/Collate/> if you have F<perl/lib/Unicode/Collate.pm>.
1382*0Sstevel@tonic-gate
1383*0Sstevel@tonic-gateBy default, the filename F<allkeys.txt> is used.
1384*0Sstevel@tonic-gate
1385*0Sstevel@tonic-gateIf C<undef> is passed explicitly as the value for this key,
1386*0Sstevel@tonic-gateno file is read (but you can define collation elements via C<entry>).
1387*0Sstevel@tonic-gate
1388*0Sstevel@tonic-gateA typical way to define a collation element table
1389*0Sstevel@tonic-gatewithout any file of table:
1390*0Sstevel@tonic-gate
1391*0Sstevel@tonic-gate   $onlyABC = Unicode::Collate->new(
1392*0Sstevel@tonic-gate       table => undef,
1393*0Sstevel@tonic-gate       entry => << 'ENTRIES',
1394*0Sstevel@tonic-gate0061 ; [.0101.0020.0002.0061] # LATIN SMALL LETTER A
1395*0Sstevel@tonic-gate0041 ; [.0101.0020.0008.0041] # LATIN CAPITAL LETTER A
1396*0Sstevel@tonic-gate0062 ; [.0102.0020.0002.0062] # LATIN SMALL LETTER B
1397*0Sstevel@tonic-gate0042 ; [.0102.0020.0008.0042] # LATIN CAPITAL LETTER B
1398*0Sstevel@tonic-gate0063 ; [.0103.0020.0002.0063] # LATIN SMALL LETTER C
1399*0Sstevel@tonic-gate0043 ; [.0103.0020.0008.0043] # LATIN CAPITAL LETTER C
1400*0Sstevel@tonic-gateENTRIES
1401*0Sstevel@tonic-gate    );
1402*0Sstevel@tonic-gate
1403*0Sstevel@tonic-gate=item undefName
1404*0Sstevel@tonic-gate
1405*0Sstevel@tonic-gate=item undefChar
1406*0Sstevel@tonic-gate
1407*0Sstevel@tonic-gate-- see 6.3.4 Reducing the Repertoire, UTS #10.
1408*0Sstevel@tonic-gate
1409*0Sstevel@tonic-gateUndefines the collation element as if it were unassigned in the table.
1410*0Sstevel@tonic-gateThis reduces the size of the table.
1411*0Sstevel@tonic-gateIf an unassigned character appears in the string to be collated,
1412*0Sstevel@tonic-gatethe sort key is made from its codepoint
1413*0Sstevel@tonic-gateas a single-character collation element,
1414*0Sstevel@tonic-gateas it is greater than any other assigned collation elements
1415*0Sstevel@tonic-gate(in the codepoint order among the unassigned characters).
1416*0Sstevel@tonic-gateBut, it'd be better to ignore characters
1417*0Sstevel@tonic-gateunfamiliar to you and maybe never used.
1418*0Sstevel@tonic-gate
1419*0Sstevel@tonic-gate=item katakana_before_hiragana
1420*0Sstevel@tonic-gate
1421*0Sstevel@tonic-gate=item upper_before_lower
1422*0Sstevel@tonic-gate
1423*0Sstevel@tonic-gate-- see 6.6 Case Comparisons; 7.3.1 Tertiary Weight Table, UTS #10.
1424*0Sstevel@tonic-gate
1425*0Sstevel@tonic-gateBy default, lowercase is before uppercase
1426*0Sstevel@tonic-gateand hiragana is before katakana.
1427*0Sstevel@tonic-gate
1428*0Sstevel@tonic-gateIf the tag is made true, this is reversed.
1429*0Sstevel@tonic-gate
1430*0Sstevel@tonic-gateB<NOTE>: These tags simplemindedly assume
1431*0Sstevel@tonic-gateany lowercase/uppercase or hiragana/katakana distinctions
1432*0Sstevel@tonic-gatemust occur in level 3, and their weights at level 3
1433*0Sstevel@tonic-gatemust be same as those mentioned in 7.3.1, UTS #10.
1434*0Sstevel@tonic-gateIf you define your collation elements which violate this requirement,
1435*0Sstevel@tonic-gatethese tags don't work validly.
1436*0Sstevel@tonic-gate
1437*0Sstevel@tonic-gate=item variable
1438*0Sstevel@tonic-gate
1439*0Sstevel@tonic-gate=item alternate
1440*0Sstevel@tonic-gate
1441*0Sstevel@tonic-gate-- see 3.2.2 Variable Weighting, UTS #10.
1442*0Sstevel@tonic-gate
1443*0Sstevel@tonic-gate(the title in UCA version 8: Alternate Weighting)
1444*0Sstevel@tonic-gate
1445*0Sstevel@tonic-gateThis key allows to variable weighting for variable collation elements,
1446*0Sstevel@tonic-gatewhich are marked with an ASTERISK in the table
1447*0Sstevel@tonic-gate(NOTE: Many punction marks and symbols are variable in F<allkeys.txt>).
1448*0Sstevel@tonic-gate
1449*0Sstevel@tonic-gate   variable => 'blanked', 'non-ignorable', 'shifted', or 'shift-trimmed'.
1450*0Sstevel@tonic-gate
1451*0Sstevel@tonic-gateThese names are case-insensitive.
1452*0Sstevel@tonic-gateBy default (if specification is omitted), 'shifted' is adopted.
1453*0Sstevel@tonic-gate
1454*0Sstevel@tonic-gate   'Blanked'        Variable elements are made ignorable at levels 1 through 3;
1455*0Sstevel@tonic-gate                    considered at the 4th level.
1456*0Sstevel@tonic-gate
1457*0Sstevel@tonic-gate   'Non-Ignorable'  Variable elements are not reset to ignorable.
1458*0Sstevel@tonic-gate
1459*0Sstevel@tonic-gate   'Shifted'        Variable elements are made ignorable at levels 1 through 3
1460*0Sstevel@tonic-gate                    their level 4 weight is replaced by the old level 1 weight.
1461*0Sstevel@tonic-gate                    Level 4 weight for Non-Variable elements is 0xFFFF.
1462*0Sstevel@tonic-gate
1463*0Sstevel@tonic-gate   'Shift-Trimmed'  Same as 'shifted', but all FFFF's at the 4th level
1464*0Sstevel@tonic-gate                    are trimmed.
1465*0Sstevel@tonic-gate
1466*0Sstevel@tonic-gateFor backward compatibility, C<alternate> can be used as an alias
1467*0Sstevel@tonic-gatefor C<variable>.
1468*0Sstevel@tonic-gate
1469*0Sstevel@tonic-gate=back
1470*0Sstevel@tonic-gate
1471*0Sstevel@tonic-gate=head2 Methods for Collation
1472*0Sstevel@tonic-gate
1473*0Sstevel@tonic-gate=over 4
1474*0Sstevel@tonic-gate
1475*0Sstevel@tonic-gate=item C<@sorted = $Collator-E<gt>sort(@not_sorted)>
1476*0Sstevel@tonic-gate
1477*0Sstevel@tonic-gateSorts a list of strings.
1478*0Sstevel@tonic-gate
1479*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>cmp($a, $b)>
1480*0Sstevel@tonic-gate
1481*0Sstevel@tonic-gateReturns 1 (when C<$a> is greater than C<$b>)
1482*0Sstevel@tonic-gateor 0 (when C<$a> is equal to C<$b>)
1483*0Sstevel@tonic-gateor -1 (when C<$a> is lesser than C<$b>).
1484*0Sstevel@tonic-gate
1485*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>eq($a, $b)>
1486*0Sstevel@tonic-gate
1487*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>ne($a, $b)>
1488*0Sstevel@tonic-gate
1489*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>lt($a, $b)>
1490*0Sstevel@tonic-gate
1491*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>le($a, $b)>
1492*0Sstevel@tonic-gate
1493*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>gt($a, $b)>
1494*0Sstevel@tonic-gate
1495*0Sstevel@tonic-gate=item C<$result = $Collator-E<gt>ge($a, $b)>
1496*0Sstevel@tonic-gate
1497*0Sstevel@tonic-gateThey works like the same name operators as theirs.
1498*0Sstevel@tonic-gate
1499*0Sstevel@tonic-gate   eq : whether $a is equal to $b.
1500*0Sstevel@tonic-gate   ne : whether $a is not equal to $b.
1501*0Sstevel@tonic-gate   lt : whether $a is lesser than $b.
1502*0Sstevel@tonic-gate   le : whether $a is lesser than $b or equal to $b.
1503*0Sstevel@tonic-gate   gt : whether $a is greater than $b.
1504*0Sstevel@tonic-gate   ge : whether $a is greater than $b or equal to $b.
1505*0Sstevel@tonic-gate
1506*0Sstevel@tonic-gate=item C<$sortKey = $Collator-E<gt>getSortKey($string)>
1507*0Sstevel@tonic-gate
1508*0Sstevel@tonic-gate-- see 4.3 Form a sort key for each string, UTS #10.
1509*0Sstevel@tonic-gate
1510*0Sstevel@tonic-gateReturns a sort key.
1511*0Sstevel@tonic-gate
1512*0Sstevel@tonic-gateYou compare the sort keys using a binary comparison
1513*0Sstevel@tonic-gateand get the result of the comparison of the strings using UCA.
1514*0Sstevel@tonic-gate
1515*0Sstevel@tonic-gate   $Collator->getSortKey($a) cmp $Collator->getSortKey($b)
1516*0Sstevel@tonic-gate
1517*0Sstevel@tonic-gate      is equivalent to
1518*0Sstevel@tonic-gate
1519*0Sstevel@tonic-gate   $Collator->cmp($a, $b)
1520*0Sstevel@tonic-gate
1521*0Sstevel@tonic-gate=item C<$sortKeyForm = $Collator-E<gt>viewSortKey($string)>
1522*0Sstevel@tonic-gate
1523*0Sstevel@tonic-gate   use Unicode::Collate;
1524*0Sstevel@tonic-gate   my $c = Unicode::Collate->new();
1525*0Sstevel@tonic-gate   print $c->viewSortKey("Perl"),"\n";
1526*0Sstevel@tonic-gate
1527*0Sstevel@tonic-gate   # output:
1528*0Sstevel@tonic-gate   # [0B67 0A65 0B7F 0B03 | 0020 0020 0020 0020 | 0008 0002 0002 0002 | FFFF FFFF FFFF FFFF]
1529*0Sstevel@tonic-gate   #  Level 1               Level 2               Level 3               Level 4
1530*0Sstevel@tonic-gate
1531*0Sstevel@tonic-gate    (If C<UCA_Version> is 8, the output is slightly different.)
1532*0Sstevel@tonic-gate
1533*0Sstevel@tonic-gate=back
1534*0Sstevel@tonic-gate
1535*0Sstevel@tonic-gate=head2 Methods for Searching
1536*0Sstevel@tonic-gate
1537*0Sstevel@tonic-gateB<DISCLAIMER:> If C<preprocess> or C<normalization> tag is true
1538*0Sstevel@tonic-gatefor C<$Collator>, calling these methods (C<index>, C<match>, C<gmatch>,
1539*0Sstevel@tonic-gateC<subst>, C<gsubst>) is croaked,
1540*0Sstevel@tonic-gateas the position and the length might differ
1541*0Sstevel@tonic-gatefrom those on the specified string.
1542*0Sstevel@tonic-gate(And C<rearrange> and C<hangul_terminator> tags are neglected.)
1543*0Sstevel@tonic-gate
1544*0Sstevel@tonic-gateThe C<match>, C<gmatch>, C<subst>, C<gsubst> methods work
1545*0Sstevel@tonic-gatelike C<m//>, C<m//g>, C<s///>, C<s///g>, respectively,
1546*0Sstevel@tonic-gatebut they are not aware of any pattern, but only a literal substring.
1547*0Sstevel@tonic-gate
1548*0Sstevel@tonic-gate=over 4
1549*0Sstevel@tonic-gate
1550*0Sstevel@tonic-gate=item C<$position = $Collator-E<gt>index($string, $substring[, $position])>
1551*0Sstevel@tonic-gate
1552*0Sstevel@tonic-gate=item C<($position, $length) = $Collator-E<gt>index($string, $substring[, $position])>
1553*0Sstevel@tonic-gate
1554*0Sstevel@tonic-gateIf C<$substring> matches a part of C<$string>, returns
1555*0Sstevel@tonic-gatethe position of the first occurrence of the matching part in scalar context;
1556*0Sstevel@tonic-gatein list context, returns a two-element list of
1557*0Sstevel@tonic-gatethe position and the length of the matching part.
1558*0Sstevel@tonic-gate
1559*0Sstevel@tonic-gateIf C<$substring> does not match any part of C<$string>,
1560*0Sstevel@tonic-gatereturns C<-1> in scalar context and
1561*0Sstevel@tonic-gatean empty list in list context.
1562*0Sstevel@tonic-gate
1563*0Sstevel@tonic-gatee.g. you say
1564*0Sstevel@tonic-gate
1565*0Sstevel@tonic-gate  my $Collator = Unicode::Collate->new( normalization => undef, level => 1 );
1566*0Sstevel@tonic-gate                                     # (normalization => undef) is REQUIRED.
1567*0Sstevel@tonic-gate  my $str = "Ich mu� studieren Perl.";
1568*0Sstevel@tonic-gate  my $sub = "M�SS";
1569*0Sstevel@tonic-gate  my $match;
1570*0Sstevel@tonic-gate  if (my($pos,$len) = $Collator->index($str, $sub)) {
1571*0Sstevel@tonic-gate      $match = substr($str, $pos, $len);
1572*0Sstevel@tonic-gate  }
1573*0Sstevel@tonic-gate
1574*0Sstevel@tonic-gateand get C<"mu�"> in C<$match> since C<"mu�">
1575*0Sstevel@tonic-gateis primary equal to C<"M�SS">.
1576*0Sstevel@tonic-gate
1577*0Sstevel@tonic-gate=item C<$match_ref = $Collator-E<gt>match($string, $substring)>
1578*0Sstevel@tonic-gate
1579*0Sstevel@tonic-gate=item C<($match)   = $Collator-E<gt>match($string, $substring)>
1580*0Sstevel@tonic-gate
1581*0Sstevel@tonic-gateIf C<$substring> matches a part of C<$string>, in scalar context, returns
1582*0Sstevel@tonic-gateB<a reference to> the first occurrence of the matching part
1583*0Sstevel@tonic-gate(C<$match_ref> is always true if matches,
1584*0Sstevel@tonic-gatesince every reference is B<true>);
1585*0Sstevel@tonic-gatein list context, returns the first occurrence of the matching part.
1586*0Sstevel@tonic-gate
1587*0Sstevel@tonic-gateIf C<$substring> does not match any part of C<$string>,
1588*0Sstevel@tonic-gatereturns C<undef> in scalar context and
1589*0Sstevel@tonic-gatean empty list in list context.
1590*0Sstevel@tonic-gate
1591*0Sstevel@tonic-gatee.g.
1592*0Sstevel@tonic-gate
1593*0Sstevel@tonic-gate    if ($match_ref = $Collator->match($str, $sub)) { # scalar context
1594*0Sstevel@tonic-gate	print "matches [$$match_ref].\n";
1595*0Sstevel@tonic-gate    } else {
1596*0Sstevel@tonic-gate	print "doesn't match.\n";
1597*0Sstevel@tonic-gate    }
1598*0Sstevel@tonic-gate
1599*0Sstevel@tonic-gate     or
1600*0Sstevel@tonic-gate
1601*0Sstevel@tonic-gate    if (($match) = $Collator->match($str, $sub)) { # list context
1602*0Sstevel@tonic-gate	print "matches [$match].\n";
1603*0Sstevel@tonic-gate    } else {
1604*0Sstevel@tonic-gate	print "doesn't match.\n";
1605*0Sstevel@tonic-gate    }
1606*0Sstevel@tonic-gate
1607*0Sstevel@tonic-gate=item C<@match = $Collator-E<gt>gmatch($string, $substring)>
1608*0Sstevel@tonic-gate
1609*0Sstevel@tonic-gateIf C<$substring> matches a part of C<$string>, returns
1610*0Sstevel@tonic-gateall the matching parts (or matching count in scalar context).
1611*0Sstevel@tonic-gate
1612*0Sstevel@tonic-gateIf C<$substring> does not match any part of C<$string>,
1613*0Sstevel@tonic-gatereturns an empty list.
1614*0Sstevel@tonic-gate
1615*0Sstevel@tonic-gate=item C<$count = $Collator-E<gt>subst($string, $substring, $replacement)>
1616*0Sstevel@tonic-gate
1617*0Sstevel@tonic-gateIf C<$substring> matches a part of C<$string>,
1618*0Sstevel@tonic-gatethe first occurrence of the matching part is replaced by C<$replacement>
1619*0Sstevel@tonic-gate(C<$string> is modified) and return C<$count> (always equals to C<1>).
1620*0Sstevel@tonic-gate
1621*0Sstevel@tonic-gateC<$replacement> can be a C<CODEREF>,
1622*0Sstevel@tonic-gatetaking the matching part as an argument,
1623*0Sstevel@tonic-gateand returning a string to replace the matching part
1624*0Sstevel@tonic-gate(a bit similar to C<s/(..)/$coderef-E<gt>($1)/e>).
1625*0Sstevel@tonic-gate
1626*0Sstevel@tonic-gate=item C<$count = $Collator-E<gt>gsubst($string, $substring, $replacement)>
1627*0Sstevel@tonic-gate
1628*0Sstevel@tonic-gateIf C<$substring> matches a part of C<$string>,
1629*0Sstevel@tonic-gateall the occurrences of the matching part is replaced by C<$replacement>
1630*0Sstevel@tonic-gate(C<$string> is modified) and return C<$count>.
1631*0Sstevel@tonic-gate
1632*0Sstevel@tonic-gateC<$replacement> can be a C<CODEREF>,
1633*0Sstevel@tonic-gatetaking the matching part as an argument,
1634*0Sstevel@tonic-gateand returning a string to replace the matching part
1635*0Sstevel@tonic-gate(a bit similar to C<s/(..)/$coderef-E<gt>($1)/eg>).
1636*0Sstevel@tonic-gate
1637*0Sstevel@tonic-gatee.g.
1638*0Sstevel@tonic-gate
1639*0Sstevel@tonic-gate  my $Collator = Unicode::Collate->new( normalization => undef, level => 1 );
1640*0Sstevel@tonic-gate                                     # (normalization => undef) is REQUIRED.
1641*0Sstevel@tonic-gate  my $str = "Camel ass came\x{301}l CAMEL horse cAm\0E\0L...";
1642*0Sstevel@tonic-gate  $Collator->gsubst($str, "camel", sub { "<b>$_[0]</b>" });
1643*0Sstevel@tonic-gate
1644*0Sstevel@tonic-gate  # now $str is "<b>Camel</b> ass <b>came\x{301}l</b> <b>CAMEL</b> horse <b>cAm\0E\0L</b>...";
1645*0Sstevel@tonic-gate  # i.e., all the camels are made bold-faced.
1646*0Sstevel@tonic-gate
1647*0Sstevel@tonic-gate=back
1648*0Sstevel@tonic-gate
1649*0Sstevel@tonic-gate=head2 Other Methods
1650*0Sstevel@tonic-gate
1651*0Sstevel@tonic-gate=over 4
1652*0Sstevel@tonic-gate
1653*0Sstevel@tonic-gate=item C<%old_tailoring = $Collator-E<gt>change(%new_tailoring)>
1654*0Sstevel@tonic-gate
1655*0Sstevel@tonic-gateChange the value of specified keys and returns the changed part.
1656*0Sstevel@tonic-gate
1657*0Sstevel@tonic-gate    $Collator = Unicode::Collate->new(level => 4);
1658*0Sstevel@tonic-gate
1659*0Sstevel@tonic-gate    $Collator->eq("perl", "PERL"); # false
1660*0Sstevel@tonic-gate
1661*0Sstevel@tonic-gate    %old = $Collator->change(level => 2); # returns (level => 4).
1662*0Sstevel@tonic-gate
1663*0Sstevel@tonic-gate    $Collator->eq("perl", "PERL"); # true
1664*0Sstevel@tonic-gate
1665*0Sstevel@tonic-gate    $Collator->change(%old); # returns (level => 2).
1666*0Sstevel@tonic-gate
1667*0Sstevel@tonic-gate    $Collator->eq("perl", "PERL"); # false
1668*0Sstevel@tonic-gate
1669*0Sstevel@tonic-gateNot all C<(key,value)>s are allowed to be changed.
1670*0Sstevel@tonic-gateSee also C<@Unicode::Collate::ChangeOK> and C<@Unicode::Collate::ChangeNG>.
1671*0Sstevel@tonic-gate
1672*0Sstevel@tonic-gateIn the scalar context, returns the modified collator
1673*0Sstevel@tonic-gate(but it is B<not> a clone from the original).
1674*0Sstevel@tonic-gate
1675*0Sstevel@tonic-gate    $Collator->change(level => 2)->eq("perl", "PERL"); # true
1676*0Sstevel@tonic-gate
1677*0Sstevel@tonic-gate    $Collator->eq("perl", "PERL"); # true; now max level is 2nd.
1678*0Sstevel@tonic-gate
1679*0Sstevel@tonic-gate    $Collator->change(level => 4)->eq("perl", "PERL"); # false
1680*0Sstevel@tonic-gate
1681*0Sstevel@tonic-gate=item C<$version = $Collator-E<gt>version()>
1682*0Sstevel@tonic-gate
1683*0Sstevel@tonic-gateReturns the version number (a string) of the Unicode Standard
1684*0Sstevel@tonic-gatewhich the C<table> file used by the collator object is based on.
1685*0Sstevel@tonic-gateIf the table does not include a version line (starting with C<@version>),
1686*0Sstevel@tonic-gatereturns C<"unknown">.
1687*0Sstevel@tonic-gate
1688*0Sstevel@tonic-gate=item C<UCA_Version()>
1689*0Sstevel@tonic-gate
1690*0Sstevel@tonic-gateReturns the tracking version number of UTS #10 this module consults.
1691*0Sstevel@tonic-gate
1692*0Sstevel@tonic-gate=item C<Base_Unicode_Version()>
1693*0Sstevel@tonic-gate
1694*0Sstevel@tonic-gateReturns the version number of UTS #10 this module consults.
1695*0Sstevel@tonic-gate
1696*0Sstevel@tonic-gate=back
1697*0Sstevel@tonic-gate
1698*0Sstevel@tonic-gate=head2 EXPORT
1699*0Sstevel@tonic-gate
1700*0Sstevel@tonic-gateNone by default.
1701*0Sstevel@tonic-gate
1702*0Sstevel@tonic-gate=head2 CAVEAT
1703*0Sstevel@tonic-gate
1704*0Sstevel@tonic-gateUse of the C<normalization> parameter requires
1705*0Sstevel@tonic-gatethe B<Unicode::Normalize> module.
1706*0Sstevel@tonic-gate
1707*0Sstevel@tonic-gateIf you need not it (say, in the case when you need not
1708*0Sstevel@tonic-gatehandle any combining characters),
1709*0Sstevel@tonic-gateassign C<normalization =E<gt> undef> explicitly.
1710*0Sstevel@tonic-gate
1711*0Sstevel@tonic-gate-- see 6.5 Avoiding Normalization, UTS #10.
1712*0Sstevel@tonic-gate
1713*0Sstevel@tonic-gate=head2 Conformance Test
1714*0Sstevel@tonic-gate
1715*0Sstevel@tonic-gateThe Conformance Test for the UCA is available
1716*0Sstevel@tonic-gateunder L<http://www.unicode.org/Public/UCA/>.
1717*0Sstevel@tonic-gate
1718*0Sstevel@tonic-gateFor F<CollationTest_SHIFTED.txt>,
1719*0Sstevel@tonic-gatea collator via C<Unicode::Collate-E<gt>new( )> should be used;
1720*0Sstevel@tonic-gatefor F<CollationTest_NON_IGNORABLE.txt>, a collator via
1721*0Sstevel@tonic-gateC<Unicode::Collate-E<gt>new(variable =E<gt> "non-ignorable", level =E<gt> 3)>.
1722*0Sstevel@tonic-gate
1723*0Sstevel@tonic-gateB<Unicode::Normalize is required to try The Conformance Test.>
1724*0Sstevel@tonic-gate
1725*0Sstevel@tonic-gate=head1 AUTHOR
1726*0Sstevel@tonic-gate
1727*0Sstevel@tonic-gateSADAHIRO Tomoyuki <SADAHIRO@cpan.org>
1728*0Sstevel@tonic-gate
1729*0Sstevel@tonic-gate  http://homepage1.nifty.com/nomenclator/perl/
1730*0Sstevel@tonic-gate
1731*0Sstevel@tonic-gate  Copyright(C) 2001-2003, SADAHIRO Tomoyuki. Japan. All rights reserved.
1732*0Sstevel@tonic-gate
1733*0Sstevel@tonic-gate  This library is free software; you can redistribute it
1734*0Sstevel@tonic-gate  and/or modify it under the same terms as Perl itself.
1735*0Sstevel@tonic-gate
1736*0Sstevel@tonic-gate=head1 SEE ALSO
1737*0Sstevel@tonic-gate
1738*0Sstevel@tonic-gate=over 4
1739*0Sstevel@tonic-gate
1740*0Sstevel@tonic-gate=item Unicode Collation Algorithm - UTS #10
1741*0Sstevel@tonic-gate
1742*0Sstevel@tonic-gateL<http://www.unicode.org/reports/tr10/>
1743*0Sstevel@tonic-gate
1744*0Sstevel@tonic-gate=item The Default Unicode Collation Element Table (DUCET)
1745*0Sstevel@tonic-gate
1746*0Sstevel@tonic-gateL<http://www.unicode.org/Public/UCA/latest/allkeys.txt>
1747*0Sstevel@tonic-gate
1748*0Sstevel@tonic-gate=item The conformance test for the UCA
1749*0Sstevel@tonic-gate
1750*0Sstevel@tonic-gateL<http://www.unicode.org/Public/UCA/latest/CollationTest.html>
1751*0Sstevel@tonic-gate
1752*0Sstevel@tonic-gateL<http://www.unicode.org/Public/UCA/latest/CollationTest.zip>
1753*0Sstevel@tonic-gate
1754*0Sstevel@tonic-gate=item Hangul Syllable Type
1755*0Sstevel@tonic-gate
1756*0Sstevel@tonic-gateL<http://www.unicode.org/Public/UNIDATA/HangulSyllableType.txt>
1757*0Sstevel@tonic-gate
1758*0Sstevel@tonic-gate=item Unicode Normalization Forms - UAX #15
1759*0Sstevel@tonic-gate
1760*0Sstevel@tonic-gateL<http://www.unicode.org/reports/tr15/>
1761*0Sstevel@tonic-gate
1762*0Sstevel@tonic-gate=item L<Unicode::Normalize>
1763*0Sstevel@tonic-gate
1764*0Sstevel@tonic-gate=back
1765*0Sstevel@tonic-gate
1766*0Sstevel@tonic-gate=cut
1767