Supporting SJIS as a database encoding
Hello,
I'd like to propose adding SJIS as a database encoding. You may wonder why SJIS is still necessary in the world of Unicode. The purpose is to achieve comparable performance when migrating legacy database systems from other DBMSs without little modification of applications.
Recently, we failed to migrate some customer's legacy database from DBMS-X to PostgreSQL. That customer wished for PostgreSQL, but PostgreSQL couldn't meet the performance requirement.
The system uses DBMS-X with the database character set being SJIS. The main applications are written in embedded SQL, which require SJIS in their host variables. They insisted they cannot use UTF8 for the host variables because that would require large modification of applications due to character handling. So no character set conversion is necessary between the clients and the server.
On the other hand, PostgreSQL doesn't support SJIS as a database encoding. Therefore, character set conversion from UTF-8 to SJIS has to be performed. The batch application runs millions of SELECTS each of which retrieves more than 100 columns. And many of those columns are of character type.
If PostgreSQL supports SJIS, PostgreSQL will match or outperform the performance of DBMS-X with regard to the applications. We confirmed it by using psql to run a subset of the batch processing. When the client encoding is SJIS, one FETCH of 10,000 rows took about 500ms. When the client encoding is UTF8 (the same as the database encoding), the same FETCH took 270ms.
Supporting SJIS may somewhat regain attention to PostgreSQL here in Japan, in the context of database migration. BTW, MySQL supports SJIS as a database encoding. PostgreSQL used to be the most popular open source database in Japan, but MySQL is now more popular.
But what I'm wondering is why PostgreSQL doesn't support SJIS. Was there any technical difficulty? Is there anything you are worried about if adding SJIS?
I'd like to write a patch for adding SJIS if there's no strong objection. I'd appreciate it if you could let me know good design information to add a server encoding (e.g. the URL of the most recent patch to add a new server encoding)
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
But what I'm wondering is why PostgreSQL doesn't support SJIS. Was there any technical difficulty? Is there anything you are worried about if adding SJIS?
Yes, there's a technical difficulty with backend code. In many places
it is assumed that any string is "ASCII compatible", which means no
ASCII character is used as a part of multi byte string. Here is such a
random example from src/backend/util/adt/varlena.c:
/* Else, it's the traditional escaped style */
for (bc = 0, tp = inputText; *tp != '\0'; bc++)
{
if (tp[0] != '\\')
tp++;
Sometimes SJIS uses '\' as the second byte of it.
Best regards,
--
Tatsuo Ishii
SRA OSS, Inc. Japan
English: http://www.sraoss.co.jp/index_en.php
Japanese:http://www.sraoss.co.jp
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Tatsuo IshiiBut what I'm wondering is why PostgreSQL doesn't support SJIS. Was there
any technical difficulty? Is there anything you are worried about if adding
SJIS?Yes, there's a technical difficulty with backend code. In many places it
is assumed that any string is "ASCII compatible", which means no ASCII
character is used as a part of multi byte string. Here is such a random
example from src/backend/util/adt/varlena.c:/* Else, it's the traditional escaped style */
for (bc = 0, tp = inputText; *tp != '\0'; bc++)
{
if (tp[0] != '\\')
tp++;Sometimes SJIS uses '\' as the second byte of it.
Thanks, I'll try to understand the seriousness of the problem as I don't have good knowledge of character sets. But your example seems to be telling everything about the difficulty...
Before digging into the problem, could you share your impression on whether PostgreSQL can support SJIS? Would it be hopeless? Can't we find any direction to go? Can I find relevant source code by searching specific words like "ASCII", "HIGH_BIT", "\\" etc?
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Before digging into the problem, could you share your impression on whether PostgreSQL can support SJIS? Would it be hopeless? Can't we find any direction to go? Can I find relevant source code by searching specific words like "ASCII", "HIGH_BIT", "\\" etc?
For starters, you could grep "multibyte".
Best regards,
--
Tatsuo Ishii
SRA OSS, Inc. Japan
English: http://www.sraoss.co.jp/index_en.php
Japanese:http://www.sraoss.co.jp
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> writes:
Before digging into the problem, could you share your impression on
whether PostgreSQL can support SJIS? Would it be hopeless?
I think it's pretty much hopeless. Even if we were willing to make every
bit of code that looks for '\' and other specific at-risk characters
multi-byte aware (with attendant speed penalties), we could expect that
third-party extensions would still contain vulnerable code. More, we
could expect that new bugs of the same ilk would get introduced all the
time. Many such bugs would amount to security problems. So the amount of
effort and vigilance required seems out of proportion to the benefits.
Most of the recent discussion about allowed backend encodings has run
more in the other direction, ie, "why don't we disallow everything but
UTF8 and get rid of all the infrastructure for multiple backend
encodings?". I'm not personally in favor of that, but there are very
few hackers who want to add any more overhead in this area.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 09/05/2016 05:47 PM, Tom Lane wrote:
"Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> writes:
Before digging into the problem, could you share your impression on
whether PostgreSQL can support SJIS? Would it be hopeless?I think it's pretty much hopeless.
Agreed.
But one thing that would help a little, would be to optimize the UTF-8
-> SJIS conversion. It uses a very generic routine, with a binary search
over a large array of mappings. I bet you could do better than that,
maybe using a hash table or a radix tree instead of the large
binary-searched array.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
"Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> writes:
Before digging into the problem, could you share your impression on
whether PostgreSQL can support SJIS? Would it be hopeless?I think it's pretty much hopeless. Even if we were willing to make every
bit of code that looks for '\' and other specific at-risk characters
multi-byte aware (with attendant speed penalties), we could expect that
third-party extensions would still contain vulnerable code. More, we could
expect that new bugs of the same ilk would get introduced all the time.
Many such bugs would amount to security problems. So the amount of effort
and vigilance required seems out of proportion to the benefits.
Hmm, this sounds like a death sentence. But as I don't have good knowledge of character set handling yet, I'm not completely convinced about why PostgreSQL cannot support SJIS. I wonder why and how other DBMSs support SJIS and what's the difference of the implementation. Using multibyte-functions like mb... to process characters would solve the problem? Isn't the current implementation blocking the support of other character sets that have similar characteristics? I'll learn the character set handling...
Most of the recent discussion about allowed backend encodings has run more
in the other direction, ie, "why don't we disallow everything but
UTF8 and get rid of all the infrastructure for multiple backend encodings?".
I'm not personally in favor of that, but there are very few hackers who
want to add any more overhead in this area.
Personally, I totally agree. I want non-Unicode character sets to disappear from the world. But the real business doesn't seem to forgive the lack of SJIS...
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Heikki
But one thing that would help a little, would be to optimize the UTF-8
-> SJIS conversion. It uses a very generic routine, with a binary search
over a large array of mappings. I bet you could do better than that, maybe
using a hash table or a radix tree instead of the large binary-searched
array.
That sounds worth pursuing. Thanks!
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
"Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> writes:
Using multibyte-functions like mb... to process characters would solve
the problem?
Well, sure. The problem is (1) finding all the places that need that
(I'd estimate dozens to hundreds of places in the core code, and then
there's the question of extensions); (2) preventing new
non-multibyte-aware code from being introduced after you've fixed those
places; and (3) the performance penalties you'd take, because a lot of
those places are bottlenecks and it's much cheaper to not worry about
character lengths in an inner loop.
Isn't the current implementation blocking the support of
other character sets that have similar characteristics?
Sure, SJIS is not the only encoding that we consider frontend-only.
See
https://www.postgresql.org/docs/devel/static/multibyte.html#MULTIBYTE-CHARSET-SUPPORTED
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Mon, 5 Sep 2016 19:38:33 +0300, Heikki Linnakangas <hlinnaka@iki.fi> wrote in <529db688-72fc-1ca2-f898-b0b99e30076f@iki.fi>
On 09/05/2016 05:47 PM, Tom Lane wrote:
"Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> writes:
Before digging into the problem, could you share your impression on
whether PostgreSQL can support SJIS? Would it be hopeless?I think it's pretty much hopeless.
Agreed.
+1, even as a user of SJIS:)
But one thing that would help a little, would be to optimize the UTF-8
-> SJIS conversion. It uses a very generic routine, with a binary
search over a large array of mappings. I bet you could do better than
that, maybe using a hash table or a radix tree instead of the large
binary-searched array.
I'm very impressed by the idea. Mean number of iterations for
binsearch on current conversion table with 8000 characters is
about 13 and the table size is under 100kBytes (maybe).
A three-level array with 2 byte values will take about 1.6~2MB of memory.
A radix tree for UTF-8->some-encoding conversion requires about,
or up to.. (using 1 byte index to point the next level)
(1 * ((7f + 1) +
(df - c2 + 1) * (bf - 80 + 1) +
(ef - e0 + 1) * (bf - 80 + 1)^2)) = 67 kbytes.
SJIS characters are 2byte length at longest so about 8000
characters takes extra 16 k Bytes. And some padding space will be
added on them.
As the result, radix tree seems to be promising because of small
requirement of additional memory and far less comparisons. Also
Big5 and other encodings including EUC-* will get benefit from
it.
Implementing radix tree code, then redefining the format of
mapping table to suppot radix tree, then modifying mapping
generator script are needed.
If no one oppse to this, I'll do that.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kyotaro
HORIGUCHI
Implementing radix tree code, then redefining the format of mapping table
to suppot radix tree, then modifying mapping generator script are needed.
If no one oppse to this, I'll do that.
+100
Great analysis and your guts. I very much appreciate your trial!
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Tue, 6 Sep 2016 03:43:46 +0000, "Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> wrote in <0A3221C70F24FB45833433255569204D1F5E66CE@G01JPEXMBYT05>
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kyotaro
HORIGUCHIImplementing radix tree code, then redefining the format of mapping table
to suppot radix tree, then modifying mapping generator script are needed.
If no one oppse to this, I'll do that.
+100
Great analysis and your guts. I very much appreciate your trial!
Thanks, by the way, there's another issue related to SJIS
conversion. MS932 has several characters that have multiple code
points. By converting texts in this encoding to and from Unicode
causes a round-trop problem. For example,
8754(ROMAN NUMERICAL I in NEC specials)
=> U+2160(ROMAN NUMERICAL I)
=> FA4A (ROMAN NUMERICA I in IBM extension)
My counting said that 398 characters are affected by this kind of
replacement. Addition to that, "GAIJI" (Private usage area) is
not allowed. Is this meet your purpose?
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kyotaro
Thanks, by the way, there's another issue related to SJIS conversion. MS932
has several characters that have multiple code points. By converting texts
in this encoding to and from Unicode causes a round-trop problem. For
example,8754(ROMAN NUMERICAL I in NEC specials)
=> U+2160(ROMAN NUMERICAL I)
=> FA4A (ROMAN NUMERICA I in IBM extension)My counting said that 398 characters are affected by this kind of replacement.
Addition to that, "GAIJI" (Private usage area) is not allowed. Is this meet
your purpose?
Supporting GAIJI is not a requirement as far as I know. Thank you for sharing information.
# I realize my lack of knowledge about character sets...
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Wed, 07 Sep 2016 16:13:04 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in <20160907.161304.112519789.horiguchi.kyotaro@lab.ntt.co.jp>
Implementing radix tree code, then redefining the format of mapping table
to suppot radix tree, then modifying mapping generator script are needed.
If no one oppse to this, I'll do that.
So, I did that as a PoC. The radix tree takes a little less than
100k bytes (far smaller than expected:) and it is defnitely
faster than binsearch.
The attached patch does the following things.
- Defines a struct for static radix tree
(utf_radix_tree). Currently it supports up to 3-byte encodings.
- Adds a map generator script UCS_to_SJIS_radix.pl, which
generates utf8_to_sjis_radix.map from utf8_to_sjis.map.
- Adds a new conversion function utf8_to_sjis_radix.
- Modifies UtfToLocal so as to allow map to be NULL.
- Modifies utf8_to_sjis to use the new conversion function
instead of ULmapSJIS.
The followings are to be done.
- utf8_to_sjis_radix could be more generic.
- SJIS->UTF8 is not implemented but it would be easily done since
there's no difference in using the radix tree mechanism.
(but the output character is currently assumed to be 2-byte long)
- It doesn't support 4-byte codes so this is not applicable to
sjis_2004. Extending the radix tree to support 4-byte wouldn't
be hard.
The following is the result of a simple test.
=# create table t (a text); alter table t alter column a storage plain;
=# insert into t values ('... 7130 cahracters containing (I believe) all characters in SJIS encoding');
=# insert into t values ('... 7130 cahracters containing (I believe) all characters in SJIS encoding');
# Doing that twice is just my mistake.
$ export PGCLIENTENCODING=SJIS
$ time psql postgres -c '
$ psql -c '\encoding' postgres
SJIS
<Using radix tree>
$ time psql postgres -c 'select t.a from t, generate_series(0, 9999)' > /dev/null
real 0m22.696s
user 0m16.991s
sys 0m0.182s>
Using binsearch the result for the same operation was
real 0m35.296s
user 0m17.166s
sys 0m0.216s
Returning in UTF-8 bloats the result string by about 1.5 times so
it doesn't seem to make sense comparing with it. But it takes
real = 47.35s.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
Attachments:
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kyotaro
HORIGUCHI
<Using radix tree>
$ time psql postgres -c 'select t.a from t, generate_series(0, 9999)' >
/dev/nullreal 0m22.696s
user 0m16.991s
sys 0m0.182s>Using binsearch the result for the same operation was
real 0m35.296s
user 0m17.166s
sys 0m0.216sReturning in UTF-8 bloats the result string by about 1.5 times so it doesn't
seem to make sense comparing with it. But it takes real = 47.35s.
Cool, 36% speedup! Does this difference vary depending on the actual characters used, e.g. the speedup would be greater if most of the characters are ASCII?
Regards
Takayuki Tsunakawa
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
At Thu, 8 Sep 2016 07:09:51 +0000, "Tsunakawa, Takayuki" <tsunakawa.takay@jp.fujitsu.com> wrote in <0A3221C70F24FB45833433255569204D1F5E7D4A@G01JPEXMBYT05>
From: pgsql-hackers-owner@postgresql.org
[mailto:pgsql-hackers-owner@postgresql.org] On Behalf Of Kyotaro
HORIGUCHI
<Using radix tree>
$ time psql postgres -c 'select t.a from t, generate_series(0, 9999)' >
/dev/nullreal 0m22.696s
user 0m16.991s
sys 0m0.182s>Using binsearch the result for the same operation was
real 0m35.296s
user 0m17.166s
sys 0m0.216sReturning in UTF-8 bloats the result string by about 1.5 times so it doesn't
seem to make sense comparing with it. But it takes real = 47.35s.Cool, 36% speedup! Does this difference vary depending on the actual characters used, e.g. the speedup would be greater if most of the characters are ASCII?
Binsearch on JIS X 0208 always needs about 10 times of comparison
and bisecting and the radix tree requires three hops on arrays
for most of the characters and two hops for some. In sort, this
effect won't be differ among 2 and 3 byte characters in UTF-8.
The translation speed of ASCII cahracters (U+20 - U+7f) is not
affected by the character conversion mechanism. They are just
copied without conversion.
As the result, there's no speedup if the output consists only of
ASCII characters and maximum speedup when the output consists
only of 2 byte UTF-8 characters.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 09/08/2016 09:35 AM, Kyotaro HORIGUCHI wrote:
Returning in UTF-8 bloats the result string by about 1.5 times so
it doesn't seem to make sense comparing with it. But it takes
real = 47.35s.
Nice!
I was hoping that this would also make the binaries smaller. A few dozen
kB of storage is perhaps not a big deal these days, but still. And
smaller tables would also consume less memory and CPU cache.
I removed the #include "../../Unicode/utf8_to_sjis.map" line, so that
the old table isn't included anymore, compiled, and ran "strip
utf8_and_sjis.so". Without this patch, it's 126 kB, and with it, it's
160 kB. So the radix tree takes a little bit more space.
That's not too bad, and I'm sure we could live with that, but with a few
simple tricks, we could do better. First, since all the values we store
in the tree are < 0xffff, we could store them in int16 instead of int32,
and halve the size of the table right off the bat. That won't work for
all encodings, of course, but it might be worth it to have two versions
of the code, one for int16 and another for int32.
Another trick is to eliminate redundancies in the tables. Many of the
tables contain lots of zeros, as in:
/* c3xx */{
/* c380 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* c388 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* c390 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x817e,
/* c398 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* c3a0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* c3a8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* c3b0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8180,
/* c3b8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
},
and
/* e388xx */{
/* e38880 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e38888 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e38890 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e38898 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e388a0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e388a8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e388b0 */ 0x0000, 0xfa58, 0x878b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* e388b8 */ 0x0000, 0x878c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
},
You could overlay the last row of the first table, which is all zeros,
with the first row of the second table, which is also all zeros. (Many
of the tables have a lot more zero-rows than this example.)
But yes, this patch looks very promising in general. I think we should
switch over to radix trees for the all the encodings.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello,
At Tue, 13 Sep 2016 11:44:01 +0300, Heikki Linnakangas <hlinnaka@iki.fi> wrote in <7ff67a45-a53e-4d38-e25d-3a121afea47c@iki.fi>
On 09/08/2016 09:35 AM, Kyotaro HORIGUCHI wrote:
Returning in UTF-8 bloats the result string by about 1.5 times so
it doesn't seem to make sense comparing with it. But it takes
real = 47.35s.Nice!
Thanks!
I was hoping that this would also make the binaries smaller. A few
dozen kB of storage is perhaps not a big deal these days, but
still. And smaller tables would also consume less memory and CPU
cache.
Agreed.
I removed the #include "../../Unicode/utf8_to_sjis.map" line, so that
the old table isn't included anymore, compiled, and ran "strip
utf8_and_sjis.so". Without this patch, it's 126 kB, and with it, it's
160 kB. So the radix tree takes a little bit more space.That's not too bad, and I'm sure we could live with that, but with a
few simple tricks, we could do better. First, since all the values we
store in the tree are < 0xffff, we could store them in int16 instead
of int32, and halve the size of the table right off the bat. won't work
for all encodings, of course, but it might be worth it to
have two versions of the code, one for int16 and another for int32.
That's right. I used int imprudently. All of the character in the
patch, and most of characters in other than Unicode-related
encodings are in 2 bytes. 3 bytes characters can be in separate
table in the struct for the case. Othersise two or more versions
of the structs is possible since currently the radix struct is
utf8_and_sjis's own in spite of the fact that it is in pg_wchar.h
in the patch.
Another trick is to eliminate redundancies in the tables. Many of the
tables contain lots of zeros, as in:/* c3xx */{
...
0x817e,
/* c398 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* c3a0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* c3a8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* c3b0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x8180,
/* c3b8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000
},and
/* e388xx */{
/* e38880 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* e38888 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* e38890 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* e38898 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
/* e388a0 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000,
...
},
You could overlay the last row of the first table, which is all zeros,
with the first row of the second table, which is also all zeros. (Many
of the tables have a lot more zero-rows than this example.)
Yes, the bunch of zeros was annoyance. Several or many
compression techniques are available in exchange for some
additional CPU time. But the technique you suggested doesn't
need such sacrifice, sounds nice.
But yes, this patch looks very promising in general. I think we should
switch over to radix trees for the all the encodings.
The result was more than I expected for a character set with
about 7000 characters. We can expect certain amount of advangate
even for character sets that have less than a hundred of
characters.
I'll work on this for the next CF.
Thanks.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hello, did this.
As a result, radix tree is about 1.5 times faster and needs a
half memory.
At Wed, 21 Sep 2016 15:14:27 +0900 (Tokyo Standard Time), Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp> wrote in <20160921.151427.265121484.horiguchi.kyotaro@lab.ntt.co.jp>
I'll work on this for the next CF.
The radix conversion function and map conversion script became
more generic than the previous state. So I could easily added
radix conversion of EUC_JP in addition to SjiftJIS.
nm -S said that the size of radix tree data for sjis->utf8
conversion is 34kB and that for utf8->sjis is 46kB. (eucjp->utf8
57kB, utf8->eucjp 93kB) LUmapSJIS and ULmapSJIS was 62kB and
59kB, and LUmapEUC_JP and ULmapEUC_JP was 106kB and 105kB. If I'm
not missing something, radix tree is faster and require less
memory.
A simple test where 'select '7070 sjis chars' x 100' (I'm not
sure, but the size is 1404kB) on local connection shows that this
is fast enough.
radix: real 0m0.285s / user 0m0.199s / sys 0m0.006s
master: real 0m0.418s / user 0m0.180s / sys 0m0.004s
To make sure, the result of a test of sending the same amount of
ASCII string (1404kB) on SJIS and UTF8(no-conversion) encoding is
as follows.
ascii/utf8-sjis: real 0m0.220s / user 0m0.176s / sys 0m0.011s
ascii/utf8-utf8: real 0m0.137s / user 0m0.111s / sys 0m0.008s
======
Random discussions -
Currently the tree structure is devided into several elements,
One for 2-byte, other ones for 3-byte and 4-byte codes and output
table. The other than the last one is logically and technically
merged into single table but it makes the generator script far
complex than the current complexity. I no longer want to play
hide'n seek with complex perl object..
It might be better that combining this as a native feature of the
core. Currently the helper function is in core but that function
is given as conv_func on calling LocalToUtf.
Current implement uses *.map files of pg_utf_to_local as
input. It seems not good but the radix tree files is completely
uneditable. Provide custom made loading functions for every
source instead of load_chartable() would be the way to go.
# However, for example utf8_to_sjis.map, it doesn't seem to have
# generated from the source mentioned in UCS_to_SJIS.pl
I'm not sure that compilers other than gcc accepts generated map
file content.
The RADIXTREE.pm is in rather older style but seem no problem.
I haven't tried this for charsets that contains 4-byte code.
I haven't consider charset with conbined characters. I don't
think it is needed so immediately.
Though I believe that this is easily applied to other
conversions, I tried this only with character sets that I know
about it.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
Attachments:
0001-Radix-tree-infrastructure-for-character-encoding.patchtext/x-patch; charset=us-asciiDownload
From b37d4a85df2799351a870b4811945a2b63dde032 Mon Sep 17 00:00:00 2001
From: Kyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Date: Thu, 6 Oct 2016 19:21:38 +0900
Subject: [PATCH 1/3] Radix tree infrastructure for character encoding
Current character conversion mechanism is based on binary search over
flat tables. Especially for some languages like Japanese, which has so
many as several thousand characters requires over 10 times comparison
for each character. Radix-tree grately alleviates this pain. This
patch adds a generic conversion function, datat types, and a perl
module for converting *.map files into radix tree.
---
src/backend/utils/mb/Unicode/RADIXCONV.pm | 726 ++++++++++++++++++++++++++++++
src/backend/utils/mb/conv.c | 107 ++++-
src/include/mb/pg_wchar.h | 21 +
3 files changed, 848 insertions(+), 6 deletions(-)
create mode 100644 src/backend/utils/mb/Unicode/RADIXCONV.pm
diff --git a/src/backend/utils/mb/Unicode/RADIXCONV.pm b/src/backend/utils/mb/Unicode/RADIXCONV.pm
new file mode 100644
index 0000000..e7621a9
--- /dev/null
+++ b/src/backend/utils/mb/Unicode/RADIXCONV.pm
@@ -0,0 +1,726 @@
+#! /usr/bin/perl
+#
+# Copyright (c) 2016, PostgreSQL Global Development Group
+#
+# src/backend/utils/mb/Unicode/RADIXCONV.pl
+#
+# Helper routinges for generating radix tree file from code mapping files
+
+use strict;
+
+my $radix_type = "pg_mb_radix_tree";
+my $radix_node_type = "pg_mb_radix_node";
+
+#########################################
+# load_chartable(<map file name>)
+#
+# extract data from map files and returns a character table.
+# returns a reference to a hash <in code> => <out code>
+sub load_chartable
+{
+ my($fname) = @_;
+ my %c;
+
+ open(my $in, $fname) || die("cannot open $fname");
+
+ while(<$in>)
+ {
+ if (/^[ \t]*{0x([0-9a-f]+), *0x([0-9a-f]+)},?/)
+ {
+ $c{hex($1)} = hex($2);
+ }
+ }
+
+ return \%c;
+}
+
+#########################################
+# generate_index(<charmap hash ref>)
+#
+# generate a radix tree data from a character table
+# returns a hashref to an index data.
+# {
+# csegs => <character segment index>
+# b2idx => [<tree index of 1st byte of 2-byte code>]
+# b3idx => [<idx for 1st byte for 3-byte code>, <2nd>]
+# b4idx => [<idx for 1st byte for 4-byte code>, <2nd>, <3rd>]
+# }
+#
+# Tables are in two forms, flat and segmented. a segmented table is
+# logically a two-dimentional table but physically a sequence of
+# segments, fixed length block of items. This structure allows us to
+# shrink table size by overlapping a shared sequence of zeros between
+# successive two segments. overlap_segments does that step.
+#
+# A flat table is simple set of key and value pairs. The value is a
+# segment id of the next segmented table. The next table is referenced
+# using the segment id and the next byte of a code.
+#
+# flat table (b2idx, b3idx1, b4idx1)
+# {
+# attr => {
+# segmented => true(1) if this index is segmented>
+# min => <minimum value of index key>
+# max => <maximum value of index key>
+# nextidx => <hash reference to the next level table>
+# }
+# i => { # index data
+# <byte> => <pointer value> # pointer to the next index
+# ...
+# }
+#
+# Each segments in segmented table is equivalent to a flat table
+# above.
+#
+# segmented table (csegs, b3idx2, b4idx2, b4idx3)
+# {
+# attr => {
+# segmented => true(1) if this index is segmented>
+# min => <minimum value of index key>
+# max => <maximum value of index key>
+# is32bit => true if values are 32bit width, false means 16bit.
+# next => <hash reference to the next level table, if any>
+# }
+# i => { # segment data
+# <segid> => { # key for this segment
+# lower => <minimum value>
+# upper => <maximum value>
+# offset => <position of this segment in the whole table>
+# label => <label string of this segment>
+# d => { # segment data
+# <byte> => { # pointer to the next index
+# label => <label string for this item>
+# segid => <target segid of next level>
+# segoffset => <offset of the target segid>
+# }
+# ...
+# }
+# }
+# }
+# }
+
+sub generate_index
+{
+ my ($c) = @_;
+ my (%csegs, %b2idx, %b3idx1, %b3idx2, %b4idx1, %b4idx2, %b4idx3);
+ my @all_tables =
+ (\%csegs, \%b2idx, \%b3idx1, \%b3idx2, \%b4idx1, \%b4idx2, \%b4idx3);
+ my $si;
+
+ # initialize attributes of index tables
+ $csegs{attr} = {name=> "csegs", chartbl => 1, segmented => 1, is32bit => 0};
+ $b2idx{attr} = {name => "b2idx", segmented => 0, nextidx => \%csegs};
+ $b3idx1{attr} = {name => "b3idx1", segmented => 0, nextidx => \%b3idx2};
+ $b3idx2{attr} = {name => "b3idx2", segmented => 1, nextidx => \%csegs};
+ $b4idx1{attr} = {name => "b4idx1", segmented => 0, nextidx => \%b4idx2};
+ $b4idx2{attr} = {name => "b4idx2", segmented => 1, nextidx => \%b4idx3};
+ $b4idx3{attr} = {name => "b4idx3", segmented => 1, nextidx => \%csegs};
+
+ foreach my $in (keys %$c)
+ {
+ if ($in < 0x100)
+ {
+ my $b1 = $in;
+ # 1 byte code doesn't have index. the first segment #0 of
+ # character table stores them
+ $si = {segid => 0, off => $in, label => "1b-", char => $$c{$in}};
+ }
+ elsif ($in < 0x10000)
+ {
+ # 2-byte code index consists of just one flat table
+ my $b1 = $in >> 8;
+ my $b2 = $in & 0xff;
+ my $csegid = $in >> 8;
+
+ if (! defined $b2idx{i}{$b1})
+ {
+ &set_min_max($b2idx{attr}, $b1);
+ $b2idx{i}{$b1}{segid} = $csegid;
+ }
+ $si = {
+ segid => $csegid,
+ off => $b2,
+ label => sprintf("%02x", $b1),
+ char => $$c{$in}
+ };
+ }
+ elsif ($in < 0x1000000)
+ {
+ # 3-byte code index consists of one flat table and one
+ # segmented table
+ my $b1 = $in >> 16;
+ my $b2 = ($in >> 8) & 0xff;
+ my $b3 = $in & 0xff;
+ my $l1id = $in >> 16;
+ my $csegid = $in >> 8;
+
+ if (! defined $b3idx1{i}{$b1})
+ {
+ &set_min_max($b3idx1{attr}, $b1);
+ $b3idx1{i}{$b1}{segid} = $l1id;
+ }
+ if (! defined $b3idx2{i}{$l1id}{d}{$b2})
+ {
+ &set_min_max($b3idx2{attr}, $b2);
+ $b3idx2{i}{$l1id}{label} = sprintf("%02x", $b1);
+ $b3idx2{i}{$l1id}{d}{$b2} = {
+ segid => $csegid,
+ label => sprintf("%02x%02x", $b1, $b2)
+ }
+ }
+
+ $si = {
+ segid => $csegid,
+ off => $b3,
+ label => sprintf("%02x%02x", $b1, $b2),
+ char => $$c{$in}
+ };
+ }
+ elsif ($in < 0x100000000)
+ {
+ # 4-byte code index consists of one flat table, and two
+ # segmented tables
+ my $b1 = $in >> 24;
+ my $b2 = ($in >> 16) & 0xff;
+ my $b3 = ($in >> 8) & 0xff;
+ my $b4 = $in & 0xff;
+ my $l1id = $in >> 24;
+ my $l2id = $in >> 16;
+ my $csegid = $in >> 8;
+
+ if (! defined $b4idx1{i}{$b1})
+ {
+ &set_min_max($b4idx1{attr}, $b1);
+ $b4idx1{i}{$b1}{segid} = $l1id;
+ }
+
+ if (! defined $b4idx2{i}{$l1id}{d}{$b2})
+ {
+ &set_min_max($b4idx2{attr}, $b2);
+ $b4idx2{i}{$l1id}{d}{$b2} = {
+ segid => $l2id,
+ label => sprintf("%02x", $b1)
+ }
+ }
+ if (! defined $b4idx3{i}{$l2id}{d}{$b3})
+ {
+ &set_min_max($b4idx3{attr}, $b3);
+ $b4idx3{i}{$l2id}{d}{$b3} = {
+ segid => $csegid,
+ label => sprintf("%02x%02x", $b1, $b2)
+ }
+ }
+
+ $si = {
+ segid => $csegid,
+ off => $b4,
+ label => sprintf("%02x%02x%02x", $b1, $b2, $b3),
+ char => $$c{$in}
+ };
+ }
+ else
+ {
+ die sprintf("up to 4 byte code is supported: %x", $in);
+ }
+
+ &set_min_max($csegs{attr}, $$si{off});
+ $csegs{i}{$$si{segid}}{d}{$$si{off}} = $$si{char};
+ $csegs{i}{$$si{segid}}{label} = $$si{label};
+ $csegs{attr}{is32bit} = 1 if ($$si{char} >= 0x10000);
+ if ($$si{char} >= 0x100000000)
+ {
+ die "character width is over 32bit. abort.";
+ }
+ }
+
+ # calcualte segment attributes
+ foreach my $t (@all_tables)
+ {
+ next if (! defined $$t{i} || ! $$t{attr}{segmented});
+
+ # segments are to be aligned in the numerical order of segment id
+ my @keylist = sort {$a <=> $b} keys $$t{i};
+ next if ($#keylist < 0);
+ my $offset = 1;
+ my $segsize = $$t{attr}{max} - $$t{attr}{min} + 1;
+
+ for my $k (@keylist)
+ {
+ my $seg = $$t{i}{$k};
+ $$seg{lower} = $$t{attr}{min};
+ $$seg{upper} = $$t{attr}{max};
+ $$seg{offset} = $offset;
+ $offset += $segsize;
+ }
+ # overlapping successive zeros between segments
+ &overlap_segments($t);
+ }
+
+ # make link among tables
+ foreach my $t (@all_tables)
+ {
+ &make_index_link($t, $$t{attr}{nextidx});
+ }
+
+ return { name_prefix => "",
+ csegs => \%csegs,
+ b2idx => [\%b2idx],
+ b3idx => [\%b3idx1, \%b3idx2],
+ b4idx => [\%b4idx1, \%b4idx2, \%b4idx3],
+ all => \@all_tables};
+}
+
+
+#########################################
+# set_min_max - internal routine to maintain min and max value of a table
+sub set_min_max
+{
+ my ($a, $v) = @_;
+
+ $$a{min} = $v if (! defined $$a{min} || $v < $$a{min});
+ $$a{max} = $v if (! defined $$a{max} || $v > $$a{max});
+}
+
+#########################################
+# overlap_segments
+#
+# removes duplicate regeion between two successive segments.
+
+sub overlap_segments
+{
+ my ($h) = @_;
+
+ # don't touch if undefined
+ return if (! defined $$h{i} || !$$h{attr}{segmented});
+ my $index = $$h{i};
+ my ($min, $max) = ($$h{attr}{min}, $$h{attr}{max});
+ my ($prev, $first);
+ my @segids = sort {$a <=> $b} keys $index;
+ return if ($#segids < 1);
+
+ $first = 1;
+ undef $prev;
+
+ for my $segid (@segids)
+ {
+ my $seg = $$index{$segid};
+
+ # smin and smax is range excluded preceeding and trailing zeros
+ my @keys = sort {$a <=> $b} keys $$seg{d};
+ my $smin = $keys[0];
+ my $smax = $keys[-1];
+
+ if ($first)
+ {
+ # first segment doesn't have a preceding segment
+ $$seg{offset} = 1;
+ $$seg{lower} = $min;
+ $$seg{upper} = $smax;
+ }
+ else
+ {
+ # calculate overlap and shift segment location
+ my $prefix = $smin - $min;
+ my $postfix = $max - $smax;
+ my $prevpostfix = $max - $$prev{upper};
+ my $overlap = $prevpostfix < $prefix ? $prevpostfix : $prefix;
+
+ $$seg{lower} = $min + $overlap;
+ $$seg{upper} = $smax;
+ $$seg{offset} = $$prev{offset} + ($max - $min + 1) - $overlap;
+ $$prev{upper} = $max;
+ }
+ $prev = $seg;
+ $first = 0;
+ }
+
+ return $h;
+}
+
+######################################################
+# make_index_link(from_table, to_table)
+#
+# Fills out target pointers in non-leaf index tables.
+#
+# from_table : table to set links
+# to_table : target table of from_table
+
+sub make_index_link
+{
+ my ($s, $t) = @_;
+ return if (! defined $$s{i} || ! defined $$t{i});
+
+ my @tkeys = sort {$a <=> $b} keys $$t{i};
+
+ if ($$s{attr}{segmented})
+ {
+ foreach my $k1 (keys $$s{i})
+ {
+ foreach my $k2 (keys $$s{i}{$k1}{d})
+ {
+ my $tsegid = $$s{i}{$k1}{d}{$k2}{segid};
+ if (! defined $tsegid)
+ {
+ die sprintf("segid is not set in %s{i}{%x}{d}{%x}{segid}",
+ $$s{attr}{name}, $k1, $k2);
+ }
+ $$s{i}{$k1}{d}{$k2}{segoffset} = $$t{i}{$tsegid}{offset};
+ }
+ }
+ }
+ else
+ {
+ foreach my $k (keys $$s{i})
+ {
+ my $tsegid = $$s{i}{$k}{segid};
+ if (! defined $tsegid)
+ {
+ die sprintf("segid is not set in %s{i}{%x}{segid}",
+ $$s{attr}{name}, $k);
+ }
+ $$s{i}{$k}{segoffset} = $$t{i}{$tsegid}{offset};
+ }
+ }
+}
+
+#########################################
+# set_name_prefix - set name prefix string of C struct
+sub set_name_prefix
+{
+ my ($trie, $p) = @_;
+
+ $$trie{name_prefix} = $p;
+}
+
+
+
+
+###############################################
+# write_table - output index table as C-struct
+#
+# write_table(hd, table, tblname, width)
+# returns 1 if the table is written
+#
+# hd : file handle to write
+# table : ref to an index table
+# tblname : C symbol name for the table
+# width : width in characters of this table
+
+sub write_table
+{
+ my($hd, $table, $tblname, $width) = @_;
+
+ return 0 if (! defined $$table{i});
+
+ if ($$table{attr}{chartbl})
+ {
+ &write_chars_table($hd, $table, $tblname, $width);
+ }
+ elsif ($$table{attr}{segmented})
+ {
+ &write_segmented_table($hd, $table, $tblname, $width);
+ }
+ else
+ {
+ &write_flat_table($hd, $table, $tblname, $width);
+ }
+ return 1;
+}
+
+#########################################
+# write_chars_table
+#
+# write_chars_table(hd, table, tblname, width)
+# this is usually called via writ_table
+#
+# hd : file handle to write
+# table : ref to an index table
+# tblname : C symbol name for the table
+# tblwidth: width in characters of this table
+
+sub write_chars_table
+{
+ my($hd, $table, $tblname, $width) = @_;
+ my($st, $ed) = ($$table{attr}{min}, $$table{attr}{max});
+ my($type) = $$table{attr}{is32bit} ? "uint32" : "uint16";
+
+ printf(OUT "static const %s %s[] =\n{", $type, $tblname);
+ printf(OUT " /* chars content - index range = [%02x, %02x] */", $st, $ed);
+
+ # values in character table are written in fixedwidth
+ # hexadecimals. calculate the number of columns in a line. 13 is
+ # the length of line header.
+
+ my $colwidth = $$table{attr}{is32bit} ? 8 : 4;
+ my $colseplen = 4; # the length of ", 0x"
+ my $headerlength = 13;
+ my $colnum = int(($width - $headerlength) / ($colwidth + $colseplen));
+
+ # round down to multiples of 4. don't bother by too small table width
+ my $colnum = int($colnum / 4) * 4;
+ my $line = "";
+ my $first0 = 1;
+ # output all segments in segment id order
+ foreach my $k (sort {$a <=> $b} keys $$table{i})
+ {
+ my $s = $$table{i}{$k};
+ if (!$first0)
+ {
+ $line =~ s/\s+$//; # remove trailing space
+ print $hd $line, ",\n";
+ $line = "";
+ }
+ $first0 = 0;
+
+ # write segment header
+ printf($hd "\n /*** %4sxx - offset 0x%05x ***/",
+ $$s{label}, $$s{offset});
+
+ # write segment content
+ my $first1 = 1;
+ my ($segstart, $segend) = ($$s{lower}, $$s{upper});
+ my($xpos, $nocomma) = (0, 0);
+
+ foreach my $j (($segstart - ($segstart % $colnum)) .. $segend)
+ {
+ $line .= "," if (!$first1 && !$nocomma);
+
+ # write the previous line and put a line header for the
+ # new line if this is the first time or this line is full
+ if ($xpos >= $colnum || $first1)
+ {
+ $line =~ s/\s+$//; # remove trailing space
+ print $hd $line, "\n";
+ $line = sprintf(" /* %02x */ ", $j);
+ $xpos = 0;
+ }
+ else
+ {
+ $line .= " ";
+ }
+ $first1 = 0;
+
+ # write each column
+ if ($j >= $segstart)
+ {
+ $line .= sprintf("0x%0*x", $colwidth, $$s{d}{$j});
+ $nocomma = 0;
+ }
+ else
+ {
+ # adjust column position
+ $line .= " " x ($colwidth + 3);
+ $nocomma = 1;
+ }
+ $xpos++;
+ }
+
+ }
+
+ $line =~ s/\s+$//;
+ print $hd $line, "\n};\n";
+}
+
+######################################################
+# write_flat_table - output nonsegmented index table
+#
+# write_flat_table(hd, table, tblname, width)
+# this is usually called via writ_table
+#
+# hd : file handle to write
+# table : ref to an index table
+# tblname : C symbol name for the table
+# width : width in characters of this table
+
+sub write_flat_table
+{
+ my($hd, $table, $tblname, $width) = @_;
+ my($st, $ed) = ($$table{attr}{min}, $$table{attr}{max});
+
+ print $hd "static const $radix_node_type $tblname =\n{";
+ printf($hd "\n 0x%x, 0x%x, /* table range */\n", $st, $ed);
+ print $hd " {";
+
+ my $first = 1;
+ my $line = "";
+
+ foreach my $i ($st .. $ed)
+ {
+ $line .= "," if (!$first);
+ my $newitem = sprintf("%d",
+ defined $$table{i}{$i} ?
+ $$table{i}{$i}{segoffset} : 0);
+
+ # flush current line and feed a line if the current line
+ # exceeds a limit
+ if ($first || length($line.$newitem) > $width)
+ {
+ $line =~ s/\s+$//; # remove trailing space
+ print $hd "$line\n";
+ $line = " ";
+ }
+ else
+ {
+ $line .= " ";
+ }
+ $line .= $newitem;
+ $first = 0;
+ }
+ print $hd $line;
+ print $hd "\n }\n};\n";
+}
+
+######################################################
+# write_segmented_table - output segmented index table
+#
+# write_segmented_table(hd, table, tblname, width)
+# this is usually called via writ_table
+#
+# hd : file handle to write
+# table : ref to an index table
+# tblname : C symbol name for the table
+# width : width in characters of this table
+
+sub write_segmented_table
+{
+ my($hd, $table, $tblname, $width) = @_;
+ my ($st, $ed) = ($$table{attr}{min}, $$table{attr}{max});
+
+ # write the variable definition
+ print $hd "static const $radix_node_type $tblname =\n{";
+ printf($hd "\n 0x%02x, 0x%02x, /*index range */\n {", $st, $ed);
+
+ my $first0 = 1;
+ foreach my $k (sort {$a <=> $b} keys $$table{i})
+ {
+ print $hd ",\n" if (!$first0);
+ $first0 = 0;
+ printf($hd "\n /*** %sxxxx - offset 0x%05x ****/",
+ $$table{i}{$k}{label}, $$table{i}{$k}{offset});
+
+ my $segstart = $$table{i}{$k}{lower};
+ my $segend = $$table{i}{$k}{upper};
+
+ my $line = "";
+ my $first1 = 1;
+ my $newitem = "";
+
+ foreach my $j ($segstart .. $segend)
+ {
+ $line .= "," if (!$first1);
+ $newitem = sprintf("%d", $$table{i}{$k}{d}{$j} ?
+ $$table{i}{$k}{d}{$j}{segoffset} : 0);
+
+ if ($first1 || length($line.$newitem) > $width)
+ {
+ $line =~ s/\s+$//;
+ print OUT "$line\n";
+ $line = sprintf(" /* %2s%02x */ ", $$table{i}{$k}{label}, $j);
+ }
+ else
+ {
+ $line .= " ";
+ }
+ $line .= $newitem;
+ $first1 = 0;
+ }
+ print $hd $line;
+ }
+ print $hd "\n }\n};\n";
+}
+
+#########################################
+# make_table_refname(table, prefix)
+#
+# internal routine to make C reference notation for tables
+
+sub make_table_refname
+{
+ my ($table, $prefix) = @_;
+
+ return "NULL" if (! defined $$table{i});
+ return "&" . $prefix . $$table{attr}{name};
+}
+
+#########################################
+# write_main_table(hd, tblname, trie, nameprefix)
+#
+# write main radix tree table
+#
+# hd : file handle to write this table
+# tblname : variable name of this struct
+# trie : ref to a radix tree
+# nameprefix : prefix for subtables.
+
+sub write_main_table
+{
+ my ($hd, $tblname, $trie) = @_;
+ my $ctblname = $$trie{name_prefix}.$$trie{csegs}{attr}{name};
+ my ($ctbl16name, $ctbl32name);
+ if ($$trie{csegs}{attr}{is32bit})
+ {
+ $ctbl16name = "NULL"; $ctbl32name = $ctblname;
+ }
+ else
+ {
+ $ctbl16name = $ctblname; $ctbl32name = "NULL";
+ }
+
+ my $b2iname = make_table_refname($$trie{b2idx}[0], $$trie{name_prefix});
+ my $b3i1name = make_table_refname($$trie{b3idx}[0], $$trie{name_prefix});
+ my $b3i2name = make_table_refname($$trie{b3idx}[1], $$trie{name_prefix});
+ my $b4i1name = make_table_refname($$trie{b4idx}[0], $$trie{name_prefix});
+ my $b4i2name = make_table_refname($$trie{b4idx}[1], $$trie{name_prefix});
+ my $b4i3name = make_table_refname($$trie{b4idx}[2], $$trie{name_prefix});
+
+ print $hd "static const $radix_type $tblname =\n{\n";
+ print $hd " /* final character table offset and body */\n";
+ printf($hd " 0x%x, 0x%x, %s, %s,\n",
+ $$trie{csegs}{attr}{min}, $$trie{csegs}{attr}{max},
+ $ctbl16name, $ctbl32name);
+
+ print $hd " /* 2-byte code table */\n";
+ print $hd " $b2iname,\n";
+ print $hd " /* 3-byte code tables */\n";
+ print $hd " {$b3i1name, $b3i2name},\n";
+ print $hd " /* 4-byte code table */\n";
+ print $hd " {$b4i1name, $b4i2name, $b4i3name},\n";
+ print $hd "};\n";
+}
+
+#########################################
+# write_map_content - write the whole content of C source of tadix tree
+#
+# write_map_content(this_script, fname, trie, tblname, tblwidth)
+#
+# this_script : the name of the *caller script* of this feature
+# fname : output file name
+# trie : ref to a radix tree
+# tblname : the name of main table of C-data
+# tblwidth : width in characters of output source file
+
+sub write_map_content
+{
+ my ($this_script, $fname, $trie, $tblname, $tblwidth) = @_;
+
+ open(OUT, "> $fname") || die("cannot open $fname");
+
+ print OUT "/* This file is generated by $this_script */\n\n";
+
+ foreach my $t (@{$$trie{all}})
+ {
+ my $table_name = $$trie{name_prefix}.$$t{attr}{name};
+
+ if (&write_table(*OUT, $t, $table_name, $tblwidth))
+ {
+ print OUT "\n";
+ }
+ }
+
+ &write_main_table(*OUT, $tblname, $trie);
+ close(OUT);
+}
+
+
+
+1;
diff --git a/src/backend/utils/mb/conv.c b/src/backend/utils/mb/conv.c
index d50336b..8658578 100644
--- a/src/backend/utils/mb/conv.c
+++ b/src/backend/utils/mb/conv.c
@@ -364,6 +364,98 @@ store_coded_char(unsigned char *dest, uint32 code)
}
/*
+ * radix tree conversion function
+ */
+const uint32 pg_mb_radix_conv(const pg_mb_radix_tree *rt, const uint32 c)
+{
+ uint32 off = 0;
+ uint32 b1 = c >> 24;
+ uint32 b2 = (c >> 16) & 0xff;
+ uint32 b3 = (c >> 8) & 0xff;
+ uint32 b4 = c & 0xff;
+
+ if (b1 > 0)
+ {
+ /* 4-byte code */
+ uint32 idx;
+
+ /* check code validity - fist byte */
+ if (rt->b4idx[0] == NULL ||
+ b1 < rt->b4idx[0]->lower || b1 > rt->b4idx[0]->upper)
+ return 0;
+
+ idx = b1 - rt->b4idx[0]->lower;
+ off = rt->b4idx[0]->idx[idx];
+ if (off == 0)
+ return 0;
+
+ /* check code validity - second byte */
+ if (b2 < rt->b4idx[1]->lower || b2 > rt->b4idx[1]->upper)
+ return 0;
+
+ idx = b2 - rt->b4idx[1]->lower;
+ off = (rt->b4idx[1]->idx + off - 1)[idx];
+ if (off == 0)
+ return 0;
+
+ /* check code validity - third byte */
+ if (b3 < rt->b4idx[2]->lower || b3 > rt->b4idx[2]->upper)
+ return 0;
+
+ idx = b3 - rt->b4idx[2]->lower;
+ off = (rt->b4idx[2]->idx + off - 1)[idx];
+ }
+ else if (b2 > 0)
+ {
+ /* 3-byte code */
+
+ uint32 idx;
+
+ /* check code validity - first byte */
+ if (rt->b3idx[0] == NULL ||
+ b2 < rt->b3idx[0]->lower || b2 > rt->b3idx[0]->upper)
+ return 0;
+
+ idx = b2 - rt->b3idx[0]->lower;
+ off = rt->b3idx[0]->idx[idx];
+ if (off == 0)
+ return 0;
+
+ /* check code validity - second byte */
+ if (b3 < rt->b3idx[1]->lower || b3 > rt->b3idx[1]->upper)
+ return 0;
+
+ idx = b3 - rt->b3idx[1]->lower;
+ off = (rt->b3idx[1]->idx + off - 1)[idx];
+ }
+ else if (b3 > 0)
+ {
+ /* 2-byte code */
+ uint32 idx;
+
+ /* check code validity - first byte */
+ if (rt->b2idx == NULL ||
+ b3 < rt->b2idx->lower || b3 > rt->b2idx->upper)
+ return 0;
+
+ idx = b3 - rt->b2idx->lower;
+ off = rt->b2idx->idx[idx];
+ }
+
+ if (off == 0)
+ return 0;
+
+ /* check code validity - last byte */
+ if (b4 < rt->chars_lower || b4 > rt->chars_upper)
+ return 0;
+
+ if (rt->chars32)
+ return (rt->chars32 + off - 1)[b4 - rt->chars_lower];
+ else
+ return (rt->chars16 + off - 1)[b4 - rt->chars_lower];
+}
+
+/*
* UTF8 ---> local code
*
* utf: input string in UTF8 encoding (need not be null-terminated)
@@ -516,13 +608,16 @@ UtfToLocal(const unsigned char *utf, int len,
}
/* Now check ordinary map */
- p = bsearch(&iutf, map, mapsize,
- sizeof(pg_utf_to_local), compare1);
-
- if (p)
+ if (map)
{
- iso = store_coded_char(iso, p->code);
- continue;
+ p = bsearch(&iutf, map, mapsize,
+ sizeof(pg_utf_to_local), compare1);
+
+ if (p)
+ {
+ iso = store_coded_char(iso, p->code);
+ continue;
+ }
}
/* if there's a conversion function, try that */
diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h
index 24e8d0d..5b2094b 100644
--- a/src/include/mb/pg_wchar.h
+++ b/src/include/mb/pg_wchar.h
@@ -384,6 +384,26 @@ typedef struct
} pg_utf_to_local;
/*
+ * radix tree structer for faster conversion
+ */
+typedef struct pg_mb_radix_index
+{
+ uint8 lower, upper; /* index range of b2idx */
+ uint32 idx[FLEXIBLE_ARRAY_MEMBER]; /* index body */
+} pg_mb_radix_node;
+
+typedef struct
+{
+ const uint8 chars_lower, chars_upper; /* index range of chars* */
+ const uint16 *chars16; /* 16 bit character table */
+ const uint32 *chars32; /* 32 bit character table */
+
+ const pg_mb_radix_node *b2idx;
+ const pg_mb_radix_node *b3idx[2];
+ const pg_mb_radix_node *b4idx[3];
+} pg_mb_radix_tree;
+
+/*
* local code to UTF-8 conversion map
*/
typedef struct
@@ -551,6 +571,7 @@ extern void latin2mic_with_table(const unsigned char *l, unsigned char *p,
extern void mic2latin_with_table(const unsigned char *mic, unsigned char *p,
int len, int lc, int encoding,
const unsigned char *tab);
+extern const uint32 pg_mb_radix_conv(const pg_mb_radix_tree *rt, const uint32 c);
extern bool pg_utf8_islegal(const unsigned char *source, int length);
--
2.9.2
0003-Use-radix-tree-for-UTF8-EUC_JP-conversion.patch.gzapplication/octet-streamDownload
�V�W 0003-Use-radix-tree-for-UTF8-EUC_JP-conversion.patch �]�s���9�+�l�#��y �)�dYQ��%_R��Xx�H+S$o������i�r����J���+Wa��&����_? L>_���
_��k�����vh�)�2ZS��*��t������<?���Y�ey~o�/+�����s/����Wny���|�xv�/���o�����������(������gn����>�:�������mV�{Us���������+���{�w�?}z��Yu\}�}{�gK�������|�}��s���x����?=�������rq~��wxx��].��w���,_����/���g�p��/���8������?M�O����������du~�~��8���S]������U8yq��_�s=�����lA���-�G�S��7{N�T��97�G��Y����0_�r�$M�V�2����>dE�e�8���+Z��;�w�.��i�����^�= ={IS�y�4��������u���l[��������a��-V�;�M��n��g����1���J3~����|�7���w~�'�d�us@�8�K�>|���o�����O�����<z��1�M���_����5����w?�/�r���Jn�n��z���'������L���Q�?�M�;��T��������;��jH��������O������\��=/_,.�~������`l}�f���M�y���e����O��$����V����<�������7"��I{������������4�#���V�&
y�M�U�9�:�L� ��/L^���]�m�t�s�F/|�G��\`~����M�v�-n�
F����F\F��F�|�std���a�8�?�]���[���1?������'�����������/��/�%=�]�/�~�3��W��pw��?��_��-�'��E��������������e��.��.����i�y=�a�/�S�C�Q���nY�D��>_\f�a��Xe�6Y=��)��i G�b�W�\�����po����b�g���������_����������K����
/�5[�>�|<����s����dT���w�����N�W������b���+Z����'������@������?=w�$��r�������������7�;���z���q�_���3��?�X���?�A4���;�.�f^.H���W���8�[9
o���"8���R��� i��t�N�`��A�/I+@�'�"mz� 3��_E+������e8��$����c_Uz;�n-��
n���W����[�/�mw����� �[c���������`��������IKr ��qs�
��F�1�?g[��I`�+�Z������jq�����.�g��}����_FO_k�����l��2�"�2��� ���gf���(q1��W��|Ho��UZ�"��]w�\N&�N��>��1]]l��i,��������f2�@����0�O�z���9�������~=��'�z�����������_�z��3�~��~=���_����7�oo
�K���PO�������
f2�@���
a26@�t4�'�Z������o��k�_�Z��2�q��~-�k�_��� �����~-��3����]����c�-��|�p~�[�;���aW~�3`�v���a��L�f���R�� &��*Sq;����b��#�-���=l�%�����\���n �$��?*���{�[���������9�`�[�;��{7�\��!%�9���a��Y����s��|v���������a�h�@7�Ch�&^@gDF���Wq��h�@wjw����
t�v���6E�D��`����u�`����*
��"��u�"�~b��dm]'zB}f�����Jk9H4��W�;��I�������@�6��q}j��]�6s����!�N�B7� ��5<6�l��Pd ~�}�>=��
�.g�����%Sjy)�+�M���D�@G���Id`VD�N�\���^@�
�Si���k�[xm�F��L�����4N�Btt t�@w�h'��d7��n�o��_�q���{p�Wg��a�r�S{�6s���7�"*�a"#Y.2 �F��~�����5�Z��N�^�oAO�~�=n����qA�������r�D���8�P�"��{�A��.n��d�����Mx;V����]�����2�[������[�~uc�
u��:X�` �
BB�^0 �P�F���7��>F�/���&
b�� ����I��X��Xp��� � � ����I��X��X��X�z�$@<F����&
b
baS��(�B�z�d�:� �(n}uG�+da�F��I���l�P�� ![�P�P��E��
���`�Q�wuG��h1�~�~� �`�
b����&��;�S���bE��O�V�N�5�i�n��j���@�Z/���&���������U�;������
������~�u�v�� j
�L����u��
���u��~U�N���~��Bwj~���_��������
������~U�N���~=���Zwj~=���+��T��"g��F��R���^C��h
�S��>h���=@(<���*t��$�k��
�j���.�7�u73���N�]�i�`���M��A�`�%�v�nS�v=�v �]����D�]�i��6%��i�`�%�v�nS�v�L��.�m����i�`���M���_0�k��6ee*0�
L����m��^C��h�+0�
L����m���O�S�mJj����+0�J��L��/��
\s�nS�v�\s��R�)S;����\i���\s�[��VN��
�����~+�Cw�[��V`��������~+�����]��V`��o���;��`����o��M�.�f��I���.�V�{l��Ot��c��>h��c{�>�h�S�����zW]k�nj�������C����H����/��9��W�A�^������w�{��v���z��W�{l�z���z��}�m��@W@�s�����m��=���|����c�}�d���|��f�m�]�;�n�H@+'L��
��@7B�B�n��B�h�������@�����C�Ah��B�nj/����w��~���_�:�A7���u���tS;���7����M����0����c��}����O;�4�T�V,���D���A�������� _��5�D���E�qa�D��\�
��F��X�k���������tG.���: 5��\�7���t�t���.m��u�K���S���r`��h����9iJ4��H�9t��)`n����C����Os������z���C��������9H$�'���/�k.�����{�[�6*�� �w���0y������b��t���]����.�li%� Z;���+�S� ���]�9��L���T�. Bp�I[Zt ���li%�T���3�
�j�PV#k��M�8�������;�c����Lj���j�j�Q"�I�;@�6��6r 3���j�������l��=�����y:x��C7��P7��n�C�8]��j�=�����5\�t'���I�;���|?�45�����p`
�quouo'2�����������;���
��D�����%l�Z��C�bw��������C��L��p���u^��!I;�n<��&�����.��k�X�+�'�PO�Xk����vXk$�mM��/@7�o�Y��������J ��9k�]���9��z�}k;�wc���U�z>n����+�yc�Z8�������<�D���K�(s��|��C���n��.lI�Z�;_��Wr�O�aHo�~�Iv��������-H{}��&t[+� [)Wa3���s����X
�8�����
t�S�x���������V ���a�Z[�t��m���;�z������ ������������@�I(e\��������m��Kc��S���sJ�����-~[������k+(��b���sr�"�;g�]���tE������)�6�0r�����O8&����N�,�j��p��R�����E��"
e��t�nS�������,AJ��S��X;Vn���rt� B;a��'6*�[Q=9Q9Z.�@7-;���
���9VER�Iz���1��HP�����(�M��#����5{pZ58%2qUY��-Kzo���l%�
��������k��6�h� %@�!n��@�u5��hF�7����^����n�eN)�P�gh].����K4���5�Ogvy���2�}C��:�Q���I������)ZC��I���u��qN�n�:��NX%�9��N���j]�P��D�VkZ�I�9
�<Pd�#n��� �������s!��l�V��f�a[�:8���T8�C7���!�o��������tS��%�����pha��s��)Nb;�!��L��+��+�x;(T6�!N��}�+'�u�h�0�;B��6e��r��K��v��3P��("O��H��(b��)I���i��A4���_N t�����(��S�X���������MR�*� ��A�V{��W�����&�.Po��N� 5�i�"|�����LG�� �������a�Q�����M�..�PQq%��|�$� ��l�"L+�� b��R� Z���U�����bZg$���^+���N���o)�\�!���+�M��Ae� �� ��kl�w����'kJ�Q�$[�{
��%wR[���$�x�@�H��� @w{=d;�������:t/:��������\��.��[�d���Ph$��Z�� �n�M.L��A4"t����]U�#Z�Y��=����R1�5�7�{v������Nnc�V�i�:�B�oXE;�
��D6��V��h,�[DO�eY��z%��&I�/!���$D�k�n�)���}m5�n����#%�-�R�� ��f��
��8!����
Jz Rm���VFRW�@wc��#�t������#���s��)���GuS+�$��)���AR���z) �,��"�uZ��r'4g6C���?~�B����1������Q��]��o�K�8�2r����o�/)�u.p���'�����p���~eR�"�>(��n�e;j��������(a�h=F�p�����7:S�\���4@w����0Kn�3_�K�6`�)����=�9�,= �"Xf�3�y��N"yq��
�&��|,�"t�nS6���S]��Er@��|'���b����>H�G��%��5F4\���jMVL�A��nU��ew���4��V��L��H��������i��J������R�&h�"���dC�)-�H��
����Y��d;~�M�h�n>p�r����y'��B
�f�%�u�w>�s���:�xNY���'����$�a;�`�K����S�U������M[�q�m����lG�IA$ ��Q�1��_���E:���kQ7���IP�����)x��S){�����j�[��z,��>Nn'Zx 0�� E!)�%[HtZ������F� +T�&~�+�� `E�F-�P���f������l��Z������J���q��������\w���
bM<�T�n}�/x�v��mc�m�(��d���5��z9���kK��CV2I�s����G�z�ur���F�P�n��+|�w2O���
5������'���'y-�}cQ�r�,�5G�c�c�� )�M ��y1m�)I��h\����9P�o �Qk�)2�-��w����kQ��:�s�\[�Q�V��M�Y�Gzi�!���k� ��6��<|�1E�GVl�;%���o�4�Vg��zeY�3=���+��.D�����h@�� ��� f�T���dQb���}�b�N{<�r�\gW��������#2��uss��z?h��m��i�5�-���Zwq�X_�8 z����h�����1��o�"1���;���$uv����:8���i���-0\����*v��?���'Pj��4_6�~:s�����A�[q��������|^�����d\����)�r;���^�kfuhZ|��xc{�+�^�E��`�����g�E��v�>����w�yvXP�H4�����������I"�W�����Z:��N�K�<LRW��\��B[�;9�2�^�<
�
F�Q��2x�L�^��:R|�U�B���rr����:��s��{����tr�#\��1a���J�o}\��K���Nj�yU�ke���}�Dz��y�kh?UI�Zr�k�F(�����4���������-5��Z���>^Q |���z������LJv���n|��a-���I�
������mn!9v�w�~�/��~�dr�u�g�[y���`9�u�j@����{����aG���=p2�3�A �},�����l]��~����w�����7��0�sov?h�
WQ">��U0��S����|��h@�)�i�����*':S��g�]����v.�-���a^.~�4��a�{�����
WB�F��[�&��x����\�;B�~��T���(���3t�S�{ �o��wx n\<Ln8T�}~��9�tw�����G��������5=7���Z �-4���nl���4�3Q ������������VB�)2���y[�#�^Sn ��{�Wiv=s�s���Eb[tZ�]�_=����p#�A�]��!9�%�������qM~��_"[��=:�'��� �F��a��Ex�{���9P�c����8]�c�������� �=��O���x���d�������@��������`�����5���<eC�F"��k�5
7t�^)~�6<���m���~�� ��9d,}+���`�?����B�����p�)���D�B�{�����W�����cTG\��q�� �5��� m�����Z���Ib�1���U�ux|��M������ w�+G��w������P�P,�&��sO�2���N��n���C}���{��{������L��s��u�������vr�����A�7�m�L����)��e�w��
�3�_%������#<�t�������lg����=��������V�X�����7����.�J�QC�����l���-���<A���_����a�[l0)n?�p���*B!���[�G���j��Cn|M UxA���?��`��;�^�V��gY@�J-T���+n�(�yM���kz�L����!w
O3�a�3NF�;p���������`���kJ�A��VM�u��%�u����_Wq��51�zv_�<P{f��o��f�fv����;�Q���]�.���
�X��-����8���3�#���T��ZCH��
Y}���N�is�ab�<����Q��j��A���n�5/z� �7qfE�$���3�dE4������+O��
�B�o�)��W�����\��V��.P�Rn�;�!tg�e������5\,���D���.����p���[�;mo�uC;�)��?���-�@�hG��N�nH~���wB��6�2?����J��X����>�} ��
�.@�
�w�~��m����l�Zw��X�m� '���S���_!������jGM����!|�
4[�<��vb�
D������k�7��+����9� 6h�
�����
�-Lp���H����KFhN�W�*"7m6����Cil��W�Y|;�=m���f�p�*8���`�cL��f�>���CrJC�[�w�]��A(s���x4�����t[X�}���0��&}�%���U�rs+3���,�s`9��T�$+�j��������>w�J��C�uWh�;����� �����_������Ve�[fz��9p�
��r�N��U�`�v����9�op�j�Q |�F\g�tsn���f�<p!��� [�N0�������f�]0�tw��������%�������x�rw��Z_ �)��p���O���w/BwG�?(���LV�us��� �����{+cx�C���� ���e$���E�j:�e�����O=����>'q
�nE��t�Hh�>������`b[�o���4I�)�a���5������'��;>?s�C�^"S9�`R` -M��I���F*�<7�Td0�@���tra�OM�"�����0���xj�u���������h������w���J�5X��) [wv�+x L��m �)�P�����iF-J+������2�f��DE�T��~��4h<T� ��������}(�d�C����S~��i�� k��ns�+E�H�ka0r9L������kVn���4�� /#8�����PF�[ap(�oU�}��@�R��o|#���!Ht<��
���7����#XK�ug�{�$:�N���B���`R�9��Wg%��B�k��F\�6��E��t���4�6�
�������}��dp]EQ�!�p1x���8: M��*U#[���N�������!�|0���aSN��[�pM��;����LX�F���L�m��2��2p�����N%�<z�<1���_�&�a���9�DZ��pS�N��_����;� � ���������=�O����{�F���������l�����6��A�;��#S��)����:��M��e�3F�u!����_��U���`� ����]u/���]C�%��,�����o6m����a������ `���0;�k����atMi��~��L�=���uo����~����>�a�W���l��!�>��/�n��]�p0���&���������h��[U�a��q ����mHn9b�p������~��B��{�����.���I?�
*��R�xDs.)�u�5���D��}b�n9���t�H����15�u�2(��P���'�n��u[$�
*4.���O�7&l]��`�U#��(�\�� �Mv��s��mP���7,����OY9�
������Tg�
4�,���������~�n�|������Y
���>#t�� *�$�| �_E�`��yn��x�6�JD���L���e�7�C�C�����=����=������0���+|���ugh�6U;5��~
&Y
�B��k �v�[N+� �:�:,Zw��������(��(����Q�Y ��D!9����C.��:s�a��"��D<F��y5���;��e����x��Z��<��M�
wIE"�� yk ���'�X��������#��}>Bw����sZ_>�]{W������ �;%���$D!-��=S���Nt�Mi��iv�^������}�� r<}���}��H��;�+���?��R ���~�s�����p�KW���N�0��i�n4��]��0i��������P���;#j,#��N+>�S�v���D�A*|�a�l��=�m����1�8������6��:���uMi���7^]9?�h�l�����wb�
���"]S����>������d���}��@7�.�Io���s�q����������:����r� �
����(T/����*��$y/�k���JaX
U�����1?]Z8�@wA���������>�u����f�F���)��$��������������~����R8&��gn�X�"[�
���!J��ww��v������:q%�O���o%\��%���+�|d��T�6��!���4�
Da�q�~3�D����~�ec��n"i �;��M_��i� "���!9�����
��cw�P�.'5(g��&��9d�o���&l�Jq�������8��T�AF�I>�F�wu�5�,�s�yJx{O����1Zl�3g:�G4��t�����N���4�C���
|�5���%
�1���Rg�u������A�^p�D����!��)wx�$l�]�s��)(�=Rz{��������_����:� ����s����*�M����=��;e���_��m�d
���� vW���P�x!��f��N�A b��h����\����!9 ��@
� cA����v(�=�Z;����;e�+&�~':����$�Y��r��x]_�u��4�(dZ��*�(��uxS��n��@�s�!�k)r��j�8
��{kd�, j���� v -�)?� ��B�NvF�#����%�;�`n�Uo
9L,F��OJ�v
��+��U������6<Cw_�K ��~��9]S��b��n���3nD&f�Hj7���k�T%O��Zt�G.Q4���c4����'������}�����Vo��#�������w��mY�.i�����Dp) ��_w��\#rv�����r��l�~������S(�0��k�YfF`!`��=L��b������
7�����R�W���_C�6�wX���p\�FS��yh�y��?&}-��@(�b��#�n��p{`uO������S�
�3��q&�%�@g���=�&�~��Q�G��HL� ������
2� v<N�����x�S��i���zF����;G�)����_�d>������9��c|H!��-��9�����@��3�3�;(��Jv� t����|���n�0��W��<j�O|�"o�u�S�����7j�z0+��Vz�hMi�L~8��,N#��P�N`�*:
N;nL�<L���`���j��j���[1m��&p�7�����x�Ql)�U��W<p��h�=BTh0�p�Wo
�p��x+aH�]�:�m� �-�D���]�}��.;����R�����������nK��`b����S�%�k�n��6<�s��r���N�'F8*
�����`��i��^r|M����e��/,���S=�O'7j��@�y��Vb�r�c���p����=�ug�7���^�S���A��0�\�] ��)���3|��8/o�N���Q�W��}�-�"����[o��uo�]�
/�s��F�����Z%�+
����|re�@b-�s�����f����W*B�@�l����G�Ll;p��h�8|��`b��q��������@�6l����f���"
O�Q���K����]���N����lkh�������<l��L�$����������#o����!H�'�k�*�^%+�tw7�\����-+��6D#3�F&4���Q ���.!t{�R�w�GC?�&8�mp���{�������)��Hj�uh��;��]m���F���D��� �
?������c��A1�9y���}uR��d��kY���:;2�X���?Q��u: t?�u���_w�3��+l;��P���|_N�n����]�5�C��f��SKD?U�,�d'�����^5R+;<����~j��3$}���n��3!�o�w�`�t�9S�J��mW�H��f����hJKq�')�`_kx�&�m9.=@{���V�@x���c�#({����"Uw\!�&�5���$!M�9���ZE�|+���z���h78(�VC��e� ����g8r�M��Ij�P��j?vD�!]oY�7�~�H����������_��0"��4;,��&t�S�L��
�sy�=C�]�c�8�()u����O��W�/m0��:�C�FF0i�'#�G�m�}WH���!#|}�����_��������?Ih0�������{��#�Z!�=?�3z���9�nq���������v�����e���p��S-�)n����^�����W��k�Hf��(7���`)�BQ��V`�V�Y��uX�&9@7�a��%?�k}���5]����
!��j��~jI�yP�2_�E'��0������RO7b�n�0�\%���w1�OUl�3N~j0CJ�aR��!;�>Dh��~j�
��3��;8�I]E�;���x��e?�N%����oW���u��)C���g8�n8Q�{"�@cu�PxyM�O�����
!x'<�j�8L����n<����p���C�0p����`���]�w��7�[�f"�m�a�2��VEq0�K/�u��T��@�_k�������sF�o�;l���$��8d���C������g��,���0#|9�� F!����+NNu���������{�f�+�`s���u
��B��
�m�F��l5)��d�Q�[�
X��E���]KiCS����6�e�0�)�5^G�h�3�.\c�{�6��0DS.$.��N�8!nanJ�~q����^�5����p��N�v�����h���T J��[�hFE���p�s��ZX�l[�v�Xg>
&�����y���O��E����i�_�.�?�z��;�jS-t��Q�1�:������]�;~������2!���c���3�E
W�*a�3-�]�>�~u���~��1�h2��{Jh�.��*�*]���Vi�������{_��:4������k+�=�hl�_�������Bv���r��������E*��d����
p�@++����-8�����psF�I_�A���\��?���/��'G�l]\B��"jo~�
���,9�kq<������:,����6nl�������)�3���{j�T5��x���$4y������wL��D0>Z7�/��f�/!�pM��B���q��j�0K���!
1����w��7��<�s�#�C����p5�,�I�'��'������."|(�nG��!�"��m_�~� o
�u�)b��9���,^CH�~����W�
0H-�i�-���a2k������5�s��� ��v������s��^`�0qr��F�FD�Nf���]�L`�KaHNF�*����g#2��@�A�]CFB4#�e��0$��%�i���<�W��3t���=
n����.��6�"s�C7�[�p ������K�r���HM��k;�_�}e���z����I������z������
���{\H�u���������`R
�^JAFxBb�;3eP�������9�e`,n0�m�3��=L�����tvv��&r�nWn��m���E�~��n�;~���z�g��z^�abg��%�W��yq���`2#�]�P�)�`;h�t��!;|7��Q��=#� ��r���cd���_�1�X=���0������&�{��WE���~�W~H��$M�
-��
���e��R����.����:��Mp����)�� c��u����l�#���� ������������ ���|��nc�&�#p���)((��������r���J��u2"��F!'�0�s����\���2I��i�%���@
����)��x"���v��o�
b�!W�*~�fW�S ��l��V�����g�b�����
��TEV:��M�^�UB�_w+��u���R���22�8E�q�-� )��qE������H%���m&X_V9gP�h��]HnWZ�X� ��g ��p��}��w82��H�6�
h_��'Ng�j��@<`_�@��Jo7��ww��S'd����C��d=\�������
)3g��T�j���WW>'7Dn���W
J/GN��n��������� �����;tp{�U��O��;���I��V0����H�Ux>'q�%�o����dh�G�R-������5�
���!����5|jD�Z��d�����qE!9fTD!�
G:��n���� �M8]JxMi�9We�����a���������]���7�w���vl��HC8B�{G�0]�p C����r���A{���t�4�!w���/
\�������q&����_Q��9��������X>���]���.��i���ml�~E�p�;�PJ�h[��b����}�~}6h����V���)�n�or_w���$X��8��o�f+f�=��
&�&d��o���[�<L�p������Tc�{�����gK_�k�~��)2+OF5<�Ip�n7"���dn��'�(����+�[,##|�,9H���}��)��\=�ap$�����5G���E,��}���d���~��%Nk�����%�6�i�[:B�_������a���������m�������g4����C4�Q�w����H��A����o��G�������}�v���5��c���B�"�"�� ���{0�����m �s�������u��O��: H���|�n�^wR��!J4���1B��wA�����S3�B���?D#���x���
�\���ug�g���H�fF��~
iJ2T":KAz��9�i��V��k��t��0K�2��S�u�=S��H_wg>H �<����s`�b�[�]�~�W��3�n��m����z�������n0�n�f�!t������r�Rec���)���4� �f�.B��� ���t��T���������[wI��_�Mf�(��vI�����n8B��,�n{D�v�@�z�&v5��h���S�kJ�:x85�j��(b4^��z4eA�#�y�t�������+��$�1�a���(`��R��'�8L������������������T����5��������s-W�@�� �;��9�������n�r0{���!�\��M�{�1sS��[q-�GG�K�D����q
�t��h����MY�]-Y���>������z�dw:�D��~�G����R��W<����
�*
��������*Q��rG�%�1p�S42U
��B�������������4!�{��T��������m6g��!yP����V� *r���H�4�(7�b�Oi��3��]4��G�� ]���5�j��!bk������2w[��_-���m�~�@�
F���&6��Zz��he��
��LO������v��w
K�a�mwK��)��C����R6�9�n��tSM�^��[V�KQ��'0���Z� ����W��O��3��?�Y����������?����{�@{�h��n{�nO|����_w�,� ����-�n����y�\��r�p)���W~�����W��5r�)9�0�~m���d�^�D&��5�e�&z����<L�|Ao��w54�X��:|�W}��<`���������me{+x����da���
�
����!��b*�{�(td�Nfuii�+�����g}~����~�*���{�&V�J��9<���}�O�#���f��r?����o��a |���x"1k�u��U�t�%���Y��sn<1����<��P����r��A��`$K��oSh���~7bjsW����t��
g�f}�u�'������m�����=��������`����q,����9%6p��g��2���*g�7�`�Zs�a��!�$��TZy}A��b���r�2��F�)�\Q������J���!u��#[�O�����nXJ���~����[FtmQ^��o�OU�5��Kr���.ot[�����]X>�m�u���N�6��8�y���Q�+����T��>�����m�s2�-)L�`at�mp����o�1t{X�`p�I0�����r�\m�����Fo_h�|�V�.���o.���Z��xlu�����[�Q&�]�N�tK����D�������{��o�u�W���]F|��-�:�6LD�a ��p�}8��s����!��e����>Ww��r���/�S�<���iln��$+v���V��
W@JEq��[���|G�����H����� p�0^Mb�nH[�������� �����3�o�8�����|�!9[v������9a�����;�w�A�!t���'(�<ld+ ���������8v�)�h�\�.7^���od�gg���^��,Sa� 2��k�a���P��(~���0#|��z}_�����Bw��xn�����OCn���6v����|��-���}E0�O�SC�)k��a�,�D{�u��j+��oY4"}�`�7�zd�P�.+��A�-�Ch�����]��8I���l%�����q@��� C:��������r�<L�Pv,�
S}����s�������s�j������mOH��Bk- ���x�<�0���iX��U2c8�����@k-Nau�h��0�
3"�ugN�5����wB���T8�7f�zy��Tqo�
r��m<<���E��^'���8�e�W�����[��X�"�gf��?F���M�)�4Lp���p#����[�U�D�ry��G���W{�+�VA^s�u�
��
��\q��ow�$��y�ub���n����)�r�������O�?�v�R��a$�:�����P�Y!��9`'g{����Xb'����s�,#����i�����;������6#���d���wB��1.�!%��-t��2��i�*�.�g3xf`�����V3��8e,����+���{���~�
$P���aJx����S���2V#[���6�Z�!�;A(1d�_�%2�@�����q2�.+~$���D�)x7m������".#��M6F[�,s{�3����:��G�~������n�FH��h���������������{c}���J��9���% ]�1������'����^�m�R )CP�,5l/�))nh>"�]��F�"0-ep,1f��!0L
�����SEK^5���Q�P�:}��@�R����|��)n&&����3t7,��y���K���n�w��<���[��C�� ����G<L�r�<���H9@����+��]v�@�;@��m�$�?Kh0\��Ib^����5����0��8���9���PA���krD�j��?Q�� R���A��n3������=M��_�����81��,9�@��0hR���h� MvG�.3�n��w����&1�����`��0�����,�t+�|A;��0������C������P�.��VB�n�L�B!L� G����0��@.�����=h���i�-GYr6�tK�s�Z�E�SV�������@rS2
D�+H�K�x�I����5����U`$����-=J+l)�}L� s8�r���y�hS #�'~���*�eo;�/b� ��D�����07%#7���H�w���>)�~g����B7�2�]f��tT�
+5
���n����{��������iF�4�t+�^�.U���+��;U,��1t9�A������ �
�F8�n��"���B����^c[7x�Mv(�U�6�����= �m�S- �U��a��^Sj<K6H���m�X^��
���,9h���?�u%���9������7���}������c$�nh��H��.g��{�<��������lFZ�����#O�'�.� �z9��n��y��M"����qG�{���L�?a{v�������� �&D��6�_�`��0�0���gg�G�m��y�>Gh��k��{��4�����������=��;~����E#�#r�d&���_f��F���4��yA ��W2Nu.������H��:�l�����������|�x6���k���dA}p H�;X�L��,�9��v��d�A�&G~��,����#6�!~E{W���e����ug��Mu��D��I�v���{V��|��k�'�� 9b�r~�����c�H]�]����_D�t�#w���ir� y�����+�����p��5��������$2����i��C�����V~�~�9id���<���@��;��3���������=���_���.Z���v�=g@h�{��
��_�^�
Eb�.�V
�� �J����� z0����f���I�G�pH������i����Vh5���H����q�����g)G��V>�NB���s��+��y���������s���a��A�D�z8:���t7����=H��#���L(�} �j�y�Xy�����I"������=Q~��~��w�_P��Z��DOD1�o���@�<VqY�l�e-��g�����L�.���r&���5e�]���� ���Q0���LX��|��W���|��2���m�4AH���(�r/��f��C����.��.���%�0�r���xh���5�
�c��)����F �eAv�~��D�+��W?�u9Jp�_|�w��*��Q4�����48�s2�-G���������
�!�T/?P����ioWuH+�g� �r��p��4���{A�!�4F��pJ49Jp���^�x�J�r��W9~����r���<�x��quN��#�����!G|�;R$�\ �����������F�����6���� ����i��5�n� �_�^��9�(��ny��G��iIo�u���^������(��Ccl�(��)�����v!@{�n�L���d��*��������4MH�;@w�To��=��t7�B�w6�[������d#hkI��>�u*�#xOD?e�4�Ps����PyB�9 ��,9H\�=������O���%vb�#�� �8�O,mh��R�nd�OOl��" � �H�a2R0;t��!7������t{���K������p�4���eF���O�]f�F�-����x�p�����)���� �(;��&������BV��" ��V8m� �6y��[���Y����G��iio�u��!�i�"8
�[��]i�����u��4��c�����/?U����+�����r��-�F|�V���X�x�7���w��� �0
��!t����lX�k�Bnph�M�D��(�d�����l�(�+�Mau����rb|�@�"���T��oa>����^�a���ml��oC�����@���s�=V*���r�7a\��';�����<|���c�$�t#�d�}h���#����]`��QH�'[���o{���[�^
X`�Otg@��`V�H��muV��{��9���{Z�w����<��J�r�����!��@c�8K�}��b
��+H�4@� R��#M�N��+W=zn��r�����\�����MC���6g�������0-����-�E��VXg�����`����%ia����Cvy��@n!t��U$�:0O�X�f�����>F��� �����!���1��L�a4e/�Z8��'���I_����.��jL?e����
����a�`�� �&d~�����U
|�Lw2��P��D
�-o�'��I�<�u� k���.���FSZ���: �Af�^S��k�{�F}H+��������m�|���Z����_>��e�%�2�&����
[�B��5����Wlu6��!
�7�~����1�7�B��{A=w���������;���e�c�S�u_��w����vD���uY����+*��D�=*���qW�$f{��.A����&����JXYB�[�P2~���
��^_��%L+�#���a����
!t_����H��
s`?c(���2�Z �l/T �-Wa�U�=LT�^k�SZ��e��'��7|s���'t0g�`���d;@�;��s ��;$�t*�[1*�\���O��O�A�������{Z�w�����<
m�����a�AC�u"�n+�\�
mPO
=L�c��g�~��=�_���6��p�-�v�����9���*���aGUhh
-Q��
�}�_Q�1��+�g�����~��9����r�V8��$� ���")���!�����!�t�������]O
��0�2]���eOX����e����6��SQ��k"'S-d���{<�E^�����.�N�Q��sK�q�X��w���4�}��Q����Y��
Y���3$8��?;R��5��G���w~�g��S�q�D!9��'�;�F���0���YQ��
rD?e� m� ?@����=m�;~�����z�������Z'cIft)��!KNFWg,��%�a�s��Yd���M��~� ���c���0mM��Xh������l�;vS]0�j�V��n�@�#�@+�i�������Z��Q17$�p���Sw9;�;w��_��;�05p�$e�J��m�����L9��6&!�
������Yrz9�p\7,Ur���Y����L4��kJ�����p�������OU �x�I��l�$���u�~x.a�,��������xF�FA�K���y�N�wz�+���[sq�N�s���s�%#�����on�'���RY����N�~���6y��;�Ip�K��^ ������.�:�ot�9��9"so��l�+�#���a,]��4�&��]V�4�dhd�.#���
�:�2�E~�VgG<������]6L-Q1���s������2��� �����;���].�������x���&/t���;���9C�m���M��+�(7eB�C�����]��t���� �^hrd��r��Xso�%����N�
�d����3�4r3�[��~��'��!{^�z�X�
u�;�d 9L���p�C�+����F��R���&Xb�.���(��%�x���o��u���yX�6��+0m�(2]~�.����},���i�0�����2+��
�-X�����y<�pr�n��T|'���%�u[�o��
R��&�������UL�l� $����@F�� ��`��h��
a |��d*
��a�������4�V���zt���C��r`�!W� �n@KH�f�xV��+�Q���W�ux�����D��94������4��s`*'�� �nr��j�8}S��W�)G��If�� ��w�G����NP��s���r �������.F����,9V���of=
&`�L�)l�,��'��}��;�!�>?���Dt��4��(hrd����������9��\�.��XNIc � {4��{s��r��R�I
�A8�m9r�:x���@3�=6���o� �.�-IB��������ws�c�L�!�7@�"t41q��^��-�A��-������u^�KD?����e�Z�[h090��a2��Q |�H��2y��Z�r�,��U����������1V'"}-����L�-�C�[<���T��XSZ�,9 ^a&{�C���90],�.�Y�W�������#���U#�^��_1�=�����){��Y��7�0B�_�^���N ��������
2O�S!�P��r�nhP��l� �1t7,�hj��R�Cp ^��Q�TV.���?�<��c������x��OY9Rc�������O����X� r��@��@���3��gs� �LpTKpx39
���x�����r`����Y�.��c��0���W�i�^�����N���u�d���D��V�Q�
��+?�w���6v�C��g�.��<���P�c��do�e �:���r NtV�����tA�B��%�n]��-n������3�����������.����t��,����;���u���3V4g
Cr������@/�� �[W4������=Q��"�.��BQ�9�rL�
Se�r�����a�]���������������=���r�.�'w� ��c�e~�k�]�����1`����k� �$��� �J����L`�U�4���$�y2���d<["�����IqjP�uI�:@7l�
��z���?����1����i0]��t�Z����>7
�S������A&�J�%���;���r��
[��/��p
�:-�nK���n���/�W��[��I��m�+�����B�`���������;�w���B[�����
��.��
]+���
]����L�)��1U��_�t��,�S��sS�t�d��m9b[7�
�N�&G����v�a#�� �<�yZ�cM���=�'�����:*4v�����%����/9;U��+�������d�3a9���T�jFF�.�!���3�H3�H����B�IFF��]����Y����{���w��{ �V^P�A�o������h�g&�`59��s���y9�rCyd��y����]0��a{����)���`���[�'�b���{V�8L�|A�7�zB�_wj��u\�k�w�v���M��4_z�F�����>���9�{A��$��U$� 33����g}z{�s��h���?C����'�����)��c)����_jr�;#�x�)� �-��� �� ��2�+�|�V��,dL{�[�l/����5��3_~���������&�3�H��rR� do{�����X�Y
d_��;B�� R��v:&������X��:2�#Q����|e3w9A���3�63��gdH7������x�D�7��?@�b
*���]
I_3�����s`��i�V_�l��x6�0�`��:|�!�������w���k)�O��;@w�._1�
S��f5@w���kW�JU"���a�}�x�^S�r���oXG�Br2��g��� K�
���~n�N��� �K���90�T���������b�<���-�=��m�L���
7�n����7�����ES�:~�(+����^�$��c��-/8d�-���l��~�-�*�������%�?����W�� �A^j�P��N� cBw�V_f<;��]��u�w��T���~^�TV~����F����^�������
�����$����������0O���)�s����; �O|�}�|����+��$�"t�4#Sy�eEy���]��pF��|��]vt���sIXzH�E�.N%������PV�Aw�[�Fm�'�C��$L��~s;�}�%�������p:@���f� �����&>��~����@w� ��+����+���^��5���K7�2�����:��8Q������YS��0y�[�'H��&�������O���]����y�ZP|?8y���Jw4�Wfs���������������Z*�0�E�w�]�f+��O��ZtW�ag��2������<�tC��_��Bw��+E�=S(������LoAw��hB9J���%����h�������9����5�3t��w,���w*����3E3�f3�=��[A��o�G�&)������.@� ����[n�*���DL�l�V�y���g5b��uG �QZa���Oi�R�� b��-�����|_�]�'t�����{\�IC���Y`7h�2�����QG�
�s`���~�};�9��r�������Oe��fd�7��gc!���O�y�a�������v�wzH]�2��-:�eC{W��*�_w��y���g��u���M�15#�=�u��yn���<�OO|�e{���^��'V��5e)��N9�a�6�H_3�O��N����:�n�m���8���C����{`���Z���6�A�gS��,#s}F��,����=@wB&L��~�/���
2H53�FM
&�]��f���^J�P���l'����nd��1�]W$�u�d��~�|�q��rl������`�nqU>�Y��!�k��s�)���J�ab��-�x-(�(KNE:�A����k?�RuH���)�ng�4���*�[�}R��I���:@w�T�����r����O�9 ���Jh�K���?�2����������K�1f�c��S���L�!t�V�h���D��~�������|��\o�^'C���@�|������K@���������������X��:|�A�rS�r_�:�v���_/�n�����U�k���S d��Z�}����hJ+���C�:9C��l��o[����3+����W������OY�����BxaYyD?e���+d��-�n�0F�O3���o�9������W���se~I�FB�Z�,�Y�Q����E��@�O���
~�!an�0>�����{�q������o����[Cr��:f�g����ez���^����F����6��.��8�[=���F�_��-�n�jf�����'/�[�F�R����C�I/����U��]��t�6X���}�vTu�)+��C����1R,hVt�6_};���Z7H2�����K���;@w��\+����+�n��'�(~������;�Q�9���@Aw��v'[�#+�Q�������
j�����!s��{_x��vp��S���t=��er��m����,��9LJ���r.uY������� �}�#�������|G}�{��i�V���������[�;~����]�,$�v��-�9����~��7���������������w7��'���1�N��� G��V.�S c�I�j���ST�[VDr)�����`�6��k���+��b��|���r��2���a�6���*���&���6hr�aR������w���1��!H&���-1��������!���~�� � �y���R��d�$�HI��t��dH/H^rz��p
�<�|�<�]���d���7�Q |�����Y���5e����
�V�{��n��.p�,W
�����do�bnW<��*�R���*���'�n�o�u���]��I-�5e��\��y�����]���~�[�Dg�G�
0r�?{`���rL��q���_E
�}bk8M'��3�[���7��;�����V������\�|t�1�����
[�M"������Lx��g
�{�o��,�\]� ��(����������(&o�����~��yX����9���a,��,������
�)�w�r�������w���]��)MV�r�%����������\l�tO�Q�me�
9r,��*H�\.�,��@��B�3�r��`L����VJ��){9�^����k����[�w��25d^��D~�L�d�������`�-N�S��er�V����Z� X�1s`�X�`��%F�n+W�a}� �t%t#�Y������AC��������.���V�@� ��(������L�&��0�����nJ����`69C�!��
� `��M�&N\B<
�����������|
��a+<���
�8t���� 4ESZ9��x����r���Y&g���g�l���n��� �������p�nn�o���=��|@����}>�������9m
RE�|B��Q&��#������!��������A7HH�L�2�8/85���\�%��^�]��Y�R$Npf�+��P���U�����x�C���@���g1
V�����D6���b���V�{O��r5yF���-�n�}K�'O�]�ht�n�#v��K`�T�h���3doK
�Y��n�<t.�#����*� ��#[���������
B��� ����2���@7X���i(��)���!P���c�A���!�[b�B�BrL;�o���e�'�#����<���&G!9�3E�U�W%��,`,4��*�#�n�*����c��'�n=���.��OD��"<����
R�v]Q%J+l��$�r����b�Vti�m�[R�8�2%��WH]��i����5e��LAj]+� �!tXb����qH����]�� (p~����-`,�2��sS�r����|8���0�����^C��{j��m����k)��I��m }�J����ro���#���a4eAz_��o>;E��7�wr��z�+�n�p���e0C��������{���EB�[|3��.�=����YP���}�Mm���u^��\�Ec���+����}��E�@�B=�
��My>�u�����*�vt+�9x��+��)4@��-d�#t�y�dn\3��Dv�sk���r;�z����1��U��aHN{^�_A��r9M�
�����&�J!�IAj`���F��f�����\ci�P�;\�������B*
B�z�bc[c����I�����]����n��<&����~��J�����YK�j� =��x�Y�47@�*mTWc���tC���9A.!tCU�����H�1@7�|�������_���o���
[^+_wm]�GMm]!m� �
*A��j��������mho�����k��'0j�!��'��������<��8��y}� �-���n�2B��_�������w�'�����?�����������@{����@{����@{����@{������?����{�@{�h���=�����t�~:}��s��������U���V���|�|}�/������M\u�|����-_���
��)qN��H����~�W�!������'���wmv�k�q���z���-���O��������@��������
���`�{M\r���r��C$���(�W0X���{4��:�����W�`�;�^r�|D0X�1'�^n(�W��:
��C��I���O�F���t�������t�����g=��b���|*6�*����Zw����n���t�WyF�Y �WyE����W������v�o��%��vO�v��r��v�g�)�r��v�W�9 ���DFz�n��`���`�^K����z�V<vc�G�=<�]�~cx�w��E�H����u+s���~���������.����_�(�k|����@�����1��9���=_����� �������W��������i��H��x������x>\��<W� �5�������FW���W���*��S�,�&) ����9�|��������9����v������u-A��B���g�}x>\������
r&^���B{��C���mw��K.�[�w���� r�hw�;�� �3� T�*�oNx��5' ���
�W���� ��\P��k�Xo>7�u���m���d�o-��`�H%�:�������p1L�yK��'�
9Hw{���1���Wy@�t���B���+B��a@�t�=��9�]��n_& ��� ��U���� ��*���6�PK���
��u���_�� _�f+�X������p�c�+A��q���1Z�~{�X������j�����=�� �h�R{Gj���*|a����"����������VB7 �0�[��J���(A��U�$ ���i������9�]���.a7N%��xA�
scC������
a�[F�#}�~1���~G�����n����k�C���[���I�pZ�G��O��+�(S��W�S����o��E?A�U����*MV������w����n�L�� uV��e��Q���SL��;��-���]V�.�9�^�����x�d��3t_���~�����F�n+/����{��U�Q\�^� uf�r����+���9��[��g���3��e*��u2d�h�V���>�&���(��}]���~����V�OE�x��Uk�o���o��o�-�U��:����q.e�r*�^��.��w�����}�@�Q������[�U�����F�oi�E�u�s c���A�~
�>S~����9�EZ���d��K�(Q���/��BN��(����+3��>�G���DqE�o�r
���0��'�:r�}�o=z��9����D����F���29��x�����t��=��d�G� m+�>[����� r�g� ur�|���~�>�L�;�vv�U��o����d���^~��6T�|�����*���o�C���A��O�3C��Q�����&W���S�h�����r�1�m���e�A�;(=���a2~K��2�o��n2�������d{����
y�,t[��:���<j�O��g�� �3�c�p�%���r|6���i?����������<�m��:x�f�(�������\ /X���C� ^L��Jl�HY��w��%GQ~V��F�!�L+�� �g��_@�:�}�`�/�� C��^���b����0�
����&+�
9�������|^��+�_���ZM������2@���tY�^����t�&7����F�V����9��}�w���� �[�x�{9���N59A�oY��k���0?7h�l����c=��'{��������v`�rkX9�+�<�5J�e��x��u�!t?h��e}��L���Bi+�Qg��A��������:�������cI&L����4Jw��-%c�fh)�D���|A��!G�^�6f�f$f�h���������t|g��*�����t@V�r+�j�Dd�V�����3�eo�r���*�8���������t�Q�W���om�1�2�H0�E"i+�o����}~�nA
E )�-��;)�Y�H�;@�+���
-Q������s�v���7@�f}�d#��Z�����k�
lQ�.���a�r+_PG �t?h����_��U�'�49b����:
�����
(���i\� V^P����!�t/�0Ul�XC���7�q8j����n�
���~�� �
Z\C_5��E��V�66@b�������d�x�D) 6�u�\ {�}�� �Xn�YE�>������k��� �w�`���6z�N�7g�x���l�����������.O����S������x��B:��g��]���lpMi���ty�|��2��`Nr��9���rW�U��h�L�Tb{a��r���[�����N�����t�o�u�Wy���5��g�)���x�\����+�����S�,�wi��\P����5���r�_�^�;A�4��J� WM�-���8T���jY����������M>"�.�yu����'��
Bw�����0z=�F��b��h��^@���YiL��h���Z9����lh���L�V^PG �=%�u�39C�{@(z����076���
�
E�0�;�����V��N�|@��{G{w�����c/Q�-+�5��~���]v�
.JB{S
&%�� c��z���t'�IB�Sy4�<�����_����M�#2R+�gc
� ��Bw�RJ�F.
�V���42�FFe0�q�=m���r ���uY�7����;�3`?�I��L�r�}U �YT���-�@c)��4�����:
2���lo�|���@J�(�����/�N y�����`��0�VJ��`
� �4��q�-L.���-V�P���m��e�n[�x�d����"��5F�o�Q
�
,v&�7+�M��[V.�����`������r�ab������T��H����T����`�nU����0��1~��
���'�I��C[w�8p���9�T�!���d<��� �CP[������a�)0�\�I"��A�Th�pQ5J�e��]@1/]��{y�rh���i���+����2����t#� 2��� !t7�I�6�0���� �
}��97�����L(&���Q��k�����-�@?�U
�}@������H;;@7�H� t��#���:��0v�D��V�v)�c~�!���-&W�����)�
CP9��k��(���'�)��N
�)��
uv�h�{L
�}b��{Jn>OS�n�y_�-�������=������W��{�]�I��]FsN�eo���:�-�����s���oyj]�+����D9]v
\&_2!4���|w��v0�B��^��C<���=s��� u�9��O�kr�� ��V����v-0pu9r���0p�����>7��^_��d�?��6�}�d������V��A��'�nY�'����#H�oY9���/~�����7��0�LF�nY��\
�<j��vd2��u�B��sFV��ee{�dt�U�kg�"\����E&��o���[6�#�To�||���?��@�=i��&�����m��g`�l��eC7n��+>OC�[����
��e/�s`/�P'A���%��h���-�E�C��i�c���w��� �; �5I�r)G�n�<��@��O%tD+���WI�k�^��N��� ���M�\x ��^@tg7����\!���L`xI.��-K,��m�eo�rA�s��`�a{�)$[s���e*���b�M$0XI��������_4��{���B���qi�n�7��G��#�_�u�[��� ��ud�3�0��}qM^DC����G}%O���&�\���8�b7��������4��h��a;8�\y��2�v;��&6�X�0��]�*`
���[�E#0�H�R%�uK�r���`�!x���aD` ��������C�
i�������H��?{����|E��@��K��mH^fr�u�
��6{��� ���@����5@����)r`��w
�{b���a|�����^�P'C.�%�nx�t�����_���D��.��(������|������Bw/W������[l0A22�7���#"t#1�����TQ���8���sR�����G�y��������"�H'
Crz��:h��,:�Zw/���@a�9b��}�F�s�;KxM�8�+�]^ �t+�S��.��C�Vx���F]���K ����.{_-��������]���Q�|�uuA��.c��:*��bI"DEfB������9������%�C4r�m��U����$���H��
��g�����C7��%t+�Rt���7
�)�]$�3��|F���$Q��(<F�,t+<C�!��aM����;�swuHqj�]�kJ��I����5Jw�!q���
8�j�Q�r+��J�Z(�G�t'�7an$y��O������$� 43i�xkSl�� ���a �����A��C�@�h~|!�Es����d�Z�v{���-X��5=-qH����W�� �)�`9L����~��p-��uQl�LK6!'W�������&�7����T6��dPy�8-"��
94��bL���.G��7�I��'qCD�5�n\�k������T���b�* �Za�f{qZT\�k�S%���b|[�|����A7b+��.�������:�
����q������J@�z����!{�����E\F��I�����x��[q�FG�c���m������8�RLn�c����#M��C7n�!�0��z�n������Z���t���6���a�������O@�����Ftb��f_O����'�������.������N������6�������>�Na�n�W���������r���+d��D�n+����c���(�����%��[�r
���3����!a{'���n��������a��n�#��r����m��19�����qtn�.� r�j�*�P'Cn�� ���'t�W�xg�����d|�>B������_��J�Z������]��&g���m��:�-V���b�,�U��y�X9~��`����u�^���[���+��
HY1��u���0y��w"��6<����P"���;8:���������b��]1u�EP��*�S���c������[�Pg��g�k�^.��w�j�u�C����)������n+�Pg��B�Br��ag�|��
���2@���1�{�;#�^����vv�;%��x���XG;��| lo�v��o �+�&;�=v�u�H�mr�����d�[XBnJ�7aks��~�~��}>Awy���^�&�{�E0@wF�eL��)�������Z����W����p� ��(���}�eL{�s�1t����Q�@�^�-�������� �@�+���4�lo�8z�J����8L����L��y��r�Wd��KUB�����p9A�s��{c��f
9Lv�i������]�)wp\�\!{���[��:Y!��+��bMU����~j����]��� z��b�*�QE�[�8LvG��@^!G~�V.���}|��@�n����ak�s�
�w�ho����4����]����|���^�&p`�����p��t���@�%�0���a2���w���V��N��g#��\{>�m����L����OL��t���N�� ������=�$OL�����s��O�2N����j�� ��U.^����3�����3�.��+����l�7��f� ���]V���=1Z�?;9��#�r����|F��
Y"�.S�o��(�k�q
��2�?a )��$4��IQG������"t_�.'��WsM�F
�].�5���Hq���&G��;7L����*�F&V��uk�E�1B�#_��m��u�#�Cn���,!_��+�T��yK���|B�
�%���+`�(��sz�L��% H���d���}��C��r���0Y ��������Q`�(+������]`�(+�+�e������~�{mI_w������{}�������7@�:Z�������lq��r����o�A���5����0\�s{/�������; t�����9:L��q����������#���5�0F s5����M�\J��L~��;a�HX� �D��`��>nx)��|c���9�n;�4����aU(�7�������=o��_����#c'����%C����r�ab��:
�B�H_w������t�!_�.� &o�#�@+������G|�V���[ ��D~�V����.#����������SV���K !tE��
�2q�� ��/��Y���r�� B�L.�ux��>��=�����br�h������Z��u�r
�F$��t����b{}��BsL%��c��]l�k��I����f�J�����di����T����5%�(LN�3��~���?`�=A�_�>����_��
�<t�n�e<������aT)���:�:��80dv���;tX���K���)��%v����#��,42��2N,�3�Z72��0�=L�<���7�&{91N��-D��o����s��%�n��4;��q)�-���`�d��jV>�s@��!�Ee�H�'#5y�,�#�$&��2���R�t����w�O8�o�.���=j��q&�|-��A�wF_����E�@��H�e���E@:���&o�d��[f�����k�,~"#t����
��`�����w��w�6�<A���w�6�\ ��J����u�$����2t���� c� D��-0������!����k���1D��������K<&�t�A�
p>��| t�����h�����E�-0\t��������
&��7����g��7`��2@����g��"[H�j�w�o�/�3��c-�sC<��[���Y��
9���H�2�Kxn��a��0����`")Lpf�;���:k��QH�"��"R����_���;�n��c��+�dWF�~�6@w�6��Zx��~�'D��1�����B�_������W�xm��OY9�����a��X�!B
�1t�@!�� ���{��e@�s8�1�rL-������)�|A�r��u������H�Brv�<��y�2 ��t��`����������0O��e�n����_HA��a_��#R��KR17sF��������k��B��-�b�����b�T�[4�k�s�~+�R��]�?�T����`"�����"qH����
��'����a>��DZ�(
�0��� �
s�����?�O|�i~��<��cD�0�B/�p�! �9L����v`��r����D^"r�V6@�m��
�����k��K����9c�����e�f'��SB���p!'�����a ���>�������`K�������9b������C�a�/&����kar��#���^�.�V{�W�
.��B
&
����D={��g�
�������p94��O�������&
��.��3��`��]�1�0�\t���3����rh�VLF����!������:���Q�+o���K�.%b�u��.h�r����'������V'�4��e��+,%xz��)��-2t���.]��k=L!-
o
]k�j�����0Vt� 9Lv�o���=�����D��������]�0V��O��F ���0y���HD��_�.�|�1��^S��~��8.L�n��FtwcZ�k�;���09��Tx\h������d���x�o��Jo�
u��kz�p����
C���������N���Q�^
�E��f��;c�g�.�,4�Z�f|b+'}-�a{q�����W�D��V>���������1��:��������_�t�g*���1@7�
�E(��`�nA�q����*>�t���>mp�W�{��|��]=���Q�3+_P'A��C[�*vm��+n��q1@�����W�T�a�8�+B?���kCr�s���� ��1@w�V��Vh�Ub��!��5�/L^B������pm����a��A[�K��
�M�|n���n�����a=6�8L����0�8A+r��}`�`��wJ���B\F�g�K��� ������ >���q�]D�������]qZ�.
]�oa^��5%�/v_�,���������yp\�E�'vyx (B9���9�\��)��jr�c�;�$�xO�2��r��������n�t[9~��&���t��n+_Q'C.�%�n+W�S���3�k����J��Y���)��<�����$]��eOa�9��{9�8��������$���d����C����� ���F�)S��].�r���@�`�9C���{9�������_��o��CN�#���=����vV���O���$ &7�x�F!9 D
&{�l�c�v?��
m���
c��(����_`2��N�g������]�C�~�����_��
]h���+v����1�{�h��:>�;�u�(�����Xn�`r�u[9~W�= [F
�n+_QG!{{S��;aI: ��92��rL�H��f���Mdl��K����ud�7�����<3�+�����1�
�d�3�H���
u��
�����O
��`�J�& �&�w1���`���uW�oE��3��(+P-
�L�� �*G�Z�s Cr����>��ZH C0�A{������ZP����m�u��������<����3Cv9��L I0Y �t?�u��;~��*���eRs�h��$�
92��r�\�]�EHy0@w��p�z�W���]1m*���%PC���+���!�����_wW��>]v��u[��:2��Z�r�f
0�>�&������B�����V��E�6��K�� )����x���-� �/���<<��hVG�h������~��� ���(80F�D��V������\=4�����:�~�{"����w�r�'`���z�9Z9~*���p�~�V���# r�j��~O����� �'��V�O&O|�e���^u���E����V��N�,.�($��YG]�y-+�u�7E��)\���{ �� �#�^�'��
�K���Cw����;�A781L�o��`~�`�]<�#����]��������!����EN�+�He�)�|B�
�94��^H��H��l��#���i;�1�1L���� �]�n��2I��H��0�D�]f���2�������c\f����C+�o7<�$]dt�4#���RYJD�j��:
��|F�]`\*�vA"������F�{��juh�����o)N�O|������_���\&��WLKO3j�
9��J��0Y �iC�
�t~����I*��
p
�WA��=,%�[#�[��B7l\���2�
�toXb0d�s������+�4��={��)S�A��h����%�����ar����� �;����r�j!�J� cW���a�G��>^���������MX#�t'�7a�LP3���zI�' ��^"lo��u�X}C� �0�uY W�g���;p}���#��������K���@��@�a2~WB��E������-/����fE��C�����-�;~��*�| ��8@�H��@^C��M�hJf �x?L��[�
rDr����R�����^���W� ��S�m���`�Il�.R��� Db�[0E�#���B����E����[�M���
rh0)�9 �$87���A�bn(�G���D ����q�q�R@��:1@�*k�b� �f��
��h#��]^C���K'��~�an�T*����b�!s���;�v*� c����:�N�r���
�M�\j8�5���a�,
���5�0�����4�sC�h9@7��F���1����[�;~������I��y�����5���n��-�a��ES&���\!����Z��:;d�L`�,�� ��_������$C���[!l����$�0��
u��9�����f'���%��c�>.'����#e� �'4����L�#�2�A>\�
&�N�������.7���RL�oy���@����T`X/��Q�+��S!�7��F��1�}�e�(������|O�j!�k%��d/G^ZB��&�D�Iz�abu���
��`v��+q�W��N��Y��:3�e�����-���*��IQ��$�@�br���X�w�G%��K�X�wJ�1m`�����`Z.X����Q |/G�/���f���2��� ��SL
&�K1������,t�ZX����N\��^��N�\ �Z7�VL^!��0�����p�$�`t�L��%�k�]>C��T�&�� �l�`���6����L�Z1�176
��/��Fxy�&8��uV��=��}1�A��o��{���lI7K�;��i�l19��N���&�������u|N&�Hn���$l�1$�=�u���_w`-A� �� �I�I����(ixM)px��J��EAwFW��M`s���>cy��&��I.Q |/�g,O8�I���D�
�����a8� \�� �0Lu9A��k�p�8� VR��3@w��QK�,'0j
�]��s�.)C7���-�g*�W����a����9�n�6*�c��-H�����%0p�@�C�� ���P07�/��&Q��s��19�0��h�b�(`Y%����o�Y��^E�[<������R1s�����6@w�S1'+�R�,9V�oT}��nO|�Z���8� lVR�,9 T0��4��6��Yr���� �IS-X��:�@�R-X9��N����j�nj��T�����l��S�p��L�@��y\.&G��Il�C���{���[-Hn�>���LNl'�8��K�`��&��Oh8'����a��^���3��L�07e?������!t�����'��9�u+���-c�B�<3&�.�'����>'��%p�$��C{}�*<��3cr���1��}�xH�^����c��SK������(�[�d�3�0���
��������[~��I�L��J�a����'�.��
��+|�\7.���2�pSO�d�O�9�u+l\
�-�S�k���u���632�����:�������9Pa�R�@��t�!t+P��1y��u��)
�,]0� �)�i��i�e�A]s��2�����@=L�Qc�y�z����:>OV����^R�A��$r��
��KJ�
�H�<@7<�tC_m�[l0����=wD�
Q��[��-�0�r�+��1����[aD�
�h����(7��/��A�!��1Ja������SI�8���������]������|�����t��;���5��A�6�./�C�[�=������P�MP����p$�� �0"���d|�D&V����
�.M���@&���e 1� ���
c��X�/�)W�hJ���@14@�s����s���1�2���Ta �"��HY�/�)A1dr������[07
��#�H�u�� �@&�4tTx:)�<*h����D���0�(<���C�l���R�T�H_�eLn��lh�V��i������C�np�$m������!�N���lPq���u[��:;�G�~�����_�"�
T6������a)!�
�5��������| ^`X��i"�Ft�"�Mt���rt#BM���D�#�0�����'�~�&��1����� ��u0UNL ���
O=q:�F��V.�� �=�� ��2&g���m���.����Q4�����Cn�� ���u��L�;C���+���������7�A���i��h����w�|�q��r�qB'�!G���\},&|��S�L����C�LF�D~�V�PG]������y�'V�}���`���y�'V��@w/G?{j�
&O|����_w��{S�e���u
d�|��A��/������P v�Z2�T�>���P��)�b�Xzkh0��
u�?�e{7@���2�
�3(nr�0
<�����$�����A��{9`d����+�8L2�mL�!+�#��s`�X�'��G������;�3������?e��(�����R���<�~�M*����1�v�%�l���=��
'���~l&�@
3�[��I�������CxL^!��_wO��os&Kd0���w-0���:t;9m������������u���= G���[����V �%L������� {��&9,1'�������[ A��"�^��5e��x\��WR�,9V������t�t�� ��O��x�n�r�� ���C{m
�<R��-���-@0���0@�(��T��Yr2��L��R��J�ab�+�d�|O�[� XS�]5"}�����*�j
���<��@�9�J�uW��
X�P�j�8Lz9�^�U�@����x?����59J���d2~8�B[w���+�9��c��s��A����G{�u�M�~��n��n{��%s���V>��
y�Lzy����]��������-�(��;������(7e/�;�?'����@�Z���.C�=�l���9��4��`b�>�O@� M �9t�wD.�a�9
�����<��[�,94J�8oWL.S���/�3d��u[����o�,6&���)�M�w�n��){�o+��2�9��2 ��x��`�4����qL��c��*@��L�L��_����������%���)�3�[���r�5
���u���^����]��5^<��� rh0g�����r��������n{~B�Ia4e%K�1m?|�E#�W+�����@���E��@W��Fa��#�@+������[L��� SV��
~��*�e�2���-&G��^�+��"�^��^�b���0����X��:3�r�ab�u*�94������5��<����67\�0w����7�=�d��~19Jp�J�~1y�\B������;�\1t��Tv���>�%�0����c~���0.
���we}|OxMi��NW������9�� P #U�������{�J��o �ku��0��$xO�����fah*��B�_w����n��
lPM�S4����>��������9$}�������2�vI_3hd�>�C��jM���b������O���pA{`�������R�?�X�TF/�oC�,[�H�] ��V�P\�#=@w�{�.��X�E#����.c���T��0@�`
����2@7J�l�E���#=@�`y�XT� H�0�sOp����((��^���P]kSC�@+�Q����9���oa��2�!�o��g��['R0��Xw����
r�c��:d�g�`��b�zhOW���������U��'���x��;�v&�^&_�2tWh��s����<���
�3�T��-�0��u6����K{�����`�����m�������u�
��R9��G�����q`�n�)G��38^������x�(K���w�e�`UF4@���|`9�HU`������@�a4e.0j�� �;sD?��c�@�w�%��,'���sb+9_LN,�[��~;%
���.+d�C�����vZ9�g���4���,0�]|2t���C����S`L#t���6��p��(7���w�B
&�#q����|q��
����&t?�u��;~��*4��U���;�*��
r�%8�rA�|��ov�GB��C�����6<�va8`o��t�A����xH��!t���9�rL���D�@��.�����-�
�{��1���[`����`��QC�8_�����E�������1�������`��d 3�g��dYM+�h��5���&������b{�1��Y G&YV��U���� ���]�?+���s&��s�V�!EY�%8��u6�;��a��^X��&�����`����|������SV�g����������w��3�n�l��-�����";�*��.Z��wLi8}����3���6:�1a��{W��!'���T3&o�w��5���& ������
N�,p���$��n���$�{| � 6@7b��1���:@7�`'.��dr���1�p��,x�D��|2&W�����1`��/��p4@w�0�����{yC��0���1M`Lxy �it��&s_���F0���r�h��:h/�$<���Wxf��-�r���3c��)�����w����5@7����g�������-���*4�!�.��;hmL�!t+`_��)�2-������AN�CQt�bi+`_��+�o��T��3�n����`�{�H�2��(t�=s`�(q�2�X1��z��A;��J�X%�==�nhc�)�a� �� �
���d`��uK�v�����jL�v��a�9�u����
�B���Vo+���/������e��������7���Gq��)L�`�u�N��n����BN�C��'����w7�n���"�H��6_�t������W�����:)��<?�u?�u/��-�n����?7��9P��3�cF7�@�VtF���ga'gW,�Z�C�=L��e�s�:yl�G��;�wG�]c�C�H�j�������']�@x+O����4 a,�[q�Wx��$_\t����b�8O��%��^^Q���y'2�&��;^�.{���_��#�������r��Aa�B�t���E����GR�6�saA��"��
"��9
�� ��2�
�KE� �#�D���S|M�� �d�'�=��q��3d��FYrr�"���9��e��,9V����Rs��
����c�����{��[~��IL�!G&�����sCs�ab�u
d_�%����!@���yqVD�] ��^ M%v��.�</.�������P� i ��rL��>��T��1�P �E+����t���8`��"+���F8.28.,�%�nBU�o��P�P�Vxz(�;�@�S� �
�����D��5�nl*�N���>l/�5�@]��R
=LT1�����C��?
������
�����9Sa�a{+`�������LZ�W��[��Y��`e?HxM�[C����:�
���k��v����|��4���
j�:�L:��Uj�j�C[�"
I� �C� �
�t�>�.>��h���
�t�:�O�3L+l����rh����F����>G��O��������{a��S�����SC��"�GOl��7�P���u2��r�h��u��� �#���u�[���e����������������;���2�^��zz����(���U��
�}5�H�.?��
�B>������sLN�#������?~eor�c�3��w��F��V���
y��u���O0���`�>���)���m�Z��
r�u��P�P�R��b�x(��
rtM���������0#�������k�j���Y��c�
9�0��u2do�
�o�w-x�{��k�<L�\P�B�)g���
uf���9���������6@���X1�+ q�����[��[�d��
��:�����Vb��0����g���=L^!��C���� ��)���0"���(K���������(���W��o�9�M���W;���4�Zt���w��8�#sh�����������V^Q�p9 ��|������.��W(w
)w�Nu0 �8��9��u��.���+`!ASJ��R�j���[�$��}�N���e�T!GYr
hvL� ��#[����� ������O2���-/�($����XV���Q�K�s#cL3�x����3�+c��F�n+��*~K,���V���e,��t lo�����uT���V�PG 7��T��Jl�W)�5e����-h�%�Mi����1v%��c�������2@w�8�G'�59b,�>2m���)Kl��u'/���-�����7����{��w���U���]*]SP��AN�c�[DQ���M4"}�rt#��`h@m4@�`�Z�b�i��[�$KR��h�rSZyB�Y!G�����Okh0)�c2����%�u[yE|��Bc��H��y�����b�+�c������_�|P�u�_�����!{��4@w��R��}XK�aR@�TrE�V�a
Cr���uW��T�l�tO&�������0����Z�����L�!o�#�)+�oA�k�C�f��
}��W
}��t7h�
x���4�R-X��:+�����{��;~��*t ��i�K>