LUCENE-1044: sync index files in IndexWriter to ensure index is intact if machine or OS crashes

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@620576 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2008-02-11 18:56:09 +00:00
parent 4e99ddafe9
commit 862c44215a
31 changed files with 1915 additions and 923 deletions

View File

@ -18,6 +18,14 @@ Changes in runtime behavior
compatibility will be removed in 3.0 (hardwiring the value to
true). (Mike McCandless)
2. LUCENE-1044: IndexWriter with autoCommit=true now commits (such
that a reader can see the changes) far less often than it used to.
Previously, every flush was also a commit. You can always force a
commit by calling IndexWriter.commit(). Furthermore, in 3.0,
autoCommit will be hardwired to false (IndexWriter constructors
that take an autoCommit argument have been deprecated) (Mike
McCandless)
API Changes
1. LUCENE-1084: Changed all IndexWriter constructors to take an
@ -36,6 +44,11 @@ API Changes
the Lucene code base will need to be adapted. See also the javadocs
of the Filter class. (Paul Elschot, Michael Busch)
4. LUCENE-1044: Added IndexWriter.commit() which flushes any buffered
adds/deletes and then commits a new segments file so readers will
see the changes. Deprecate IndexWriter.flush() in favor of
IndexWriter.commit(). (Mike McCandless)
Bug fixes
1. LUCENE-1134: Fixed BooleanQuery.rewrite to only optimze a single
@ -75,6 +88,12 @@ New features
5. LUCENE-494: Added QueryAutoStopWordAnalyzer to allow for the automatic removal, from a query of frequently occurring terms.
This Analyzer is not intended for use during indexing. (Mark Harwood via Grant Ingersoll)
6. LUCENE-1044: Change Lucene to properly "sync" files after
committing, to ensure on a machine or OS crash or power cut, even
with cached writes, the index remains consistent. Also added
explicit commit() method to IndexWriter to force a commit without
having to close. (Mike McCandless)
Optimizations
1. LUCENE-705: When building a compound file, use

View File

@ -1316,17 +1316,24 @@ document.write("Last Published: " + document.lastModified);
</p>
<p>
<b>2.3 and above:</b>
<b>2.3:</b>
Segments --&gt; Format, Version, NameCounter, SegCount, &lt;SegName, SegSize, DelGen, DocStoreOffset, [DocStoreSegment, DocStoreIsCompoundFile], HasSingleNormFile, NumField,
NormGen<sup>NumField</sup>,
IsCompoundFile&gt;<sup>SegCount</sup>
</p>
<p>
<b>2.4 and above:</b>
Segments --&gt; Format, Version, NameCounter, SegCount, &lt;SegName, SegSize, DelGen, DocStoreOffset, [DocStoreSegment, DocStoreIsCompoundFile], HasSingleNormFile, NumField,
NormGen<sup>NumField</sup>,
IsCompoundFile&gt;<sup>SegCount</sup>, Checksum
</p>
<p>
Format, NameCounter, SegCount, SegSize, NumField, DocStoreOffset --&gt; Int32
</p>
<p>
Version, DelGen, NormGen --&gt; Int64
Version, DelGen, NormGen, Checksum --&gt; Int64
</p>
<p>
SegName, DocStoreSegment --&gt; String
@ -1335,7 +1342,7 @@ document.write("Last Published: " + document.lastModified);
IsCompoundFile, HasSingleNormFile, DocStoreIsCompoundFile --&gt; Int8
</p>
<p>
Format is -1 as of Lucene 1.4, -3 (SegmentInfos.FORMAT_SINGLE_NORM_FILE) as of Lucene 2.1 and 2.2, and -4 (SegmentInfos.FORMAT_SHARED_DOC_STORE) as of Lucene 2.3
Format is -1 as of Lucene 1.4, -3 (SegmentInfos.FORMAT_SINGLE_NORM_FILE) as of Lucene 2.1 and 2.2, -4 (SegmentInfos.FORMAT_SHARED_DOC_STORE) as of Lucene 2.3 and -5 (SegmentInfos.FORMAT_CHECKSUM) as of Lucene 2.4.
</p>
<p>
Version counts how often the index has been
@ -1408,7 +1415,13 @@ document.write("Last Published: " + document.lastModified);
shares a single set of these files with other
segments.
</p>
<a name="N104CD"></a><a name="Lock File"></a>
<p>
Checksum contains the CRC32 checksum of all bytes
in the segments_N file up until the checksum.
This is used to verify integrity of the file on
opening the index.
</p>
<a name="N104DC"></a><a name="Lock File"></a>
<h3 class="boxed">Lock File</h3>
<p>
The write lock, which is stored in the index
@ -1426,7 +1439,7 @@ document.write("Last Published: " + document.lastModified);
Note that prior to version 2.1, Lucene also used a
commit lock. This was removed in 2.1.
</p>
<a name="N104D9"></a><a name="Deletable File"></a>
<a name="N104E8"></a><a name="Deletable File"></a>
<h3 class="boxed">Deletable File</h3>
<p>
Prior to Lucene 2.1 there was a file "deletable"
@ -1435,7 +1448,7 @@ document.write("Last Published: " + document.lastModified);
the files that are deletable, instead, so no file
is written.
</p>
<a name="N104E2"></a><a name="Compound Files"></a>
<a name="N104F1"></a><a name="Compound Files"></a>
<h3 class="boxed">Compound Files</h3>
<p>Starting with Lucene 1.4 the compound file format became default. This
is simply a container for all files described in the next section
@ -1462,14 +1475,14 @@ document.write("Last Published: " + document.lastModified);
</div>
<a name="N1050A"></a><a name="Per-Segment Files"></a>
<a name="N10519"></a><a name="Per-Segment Files"></a>
<h2 class="boxed">Per-Segment Files</h2>
<div class="section">
<p>
The remaining files are all per-segment, and are
thus defined by suffix.
</p>
<a name="N10512"></a><a name="Fields"></a>
<a name="N10521"></a><a name="Fields"></a>
<h3 class="boxed">Fields</h3>
<p>
@ -1688,7 +1701,7 @@ document.write("Last Published: " + document.lastModified);
</li>
</ol>
<a name="N105CD"></a><a name="Term Dictionary"></a>
<a name="N105DC"></a><a name="Term Dictionary"></a>
<h3 class="boxed">Term Dictionary</h3>
<p>
The term dictionary is represented as two files:
@ -1874,7 +1887,7 @@ document.write("Last Published: " + document.lastModified);
</li>
</ol>
<a name="N1064D"></a><a name="Frequencies"></a>
<a name="N1065C"></a><a name="Frequencies"></a>
<h3 class="boxed">Frequencies</h3>
<p>
The .frq file contains the lists of documents
@ -1992,7 +2005,7 @@ document.write("Last Published: " + document.lastModified);
entry in level-1. In the example has entry 15 on level 1 a pointer to entry 15 on level 0 and entry 31 on level 1 a pointer
to entry 31 on level 0.
</p>
<a name="N106CF"></a><a name="Positions"></a>
<a name="N106DE"></a><a name="Positions"></a>
<h3 class="boxed">Positions</h3>
<p>
The .prx file contains the lists of positions that
@ -2058,7 +2071,7 @@ document.write("Last Published: " + document.lastModified);
Payload. If PayloadLength is not stored, then this Payload has the same
length as the Payload at the previous position.
</p>
<a name="N1070B"></a><a name="Normalization Factors"></a>
<a name="N1071A"></a><a name="Normalization Factors"></a>
<h3 class="boxed">Normalization Factors</h3>
<p>
@ -2162,7 +2175,7 @@ document.write("Last Published: " + document.lastModified);
<b>2.1 and above:</b>
Separate norm files are created (when adequate) for both compound and non compound segments.
</p>
<a name="N10774"></a><a name="Term Vectors"></a>
<a name="N10783"></a><a name="Term Vectors"></a>
<h3 class="boxed">Term Vectors</h3>
<p>
Term Vector support is an optional on a field by
@ -2295,7 +2308,7 @@ document.write("Last Published: " + document.lastModified);
</li>
</ol>
<a name="N1080A"></a><a name="Deleted Documents"></a>
<a name="N10819"></a><a name="Deleted Documents"></a>
<h3 class="boxed">Deleted Documents</h3>
<p>The .del file is
optional, and only exists when a segment contains deletions.
@ -2367,7 +2380,7 @@ document.write("Last Published: " + document.lastModified);
</div>
<a name="N1084D"></a><a name="Limitations"></a>
<a name="N1085C"></a><a name="Limitations"></a>
<h2 class="boxed">Limitations</h2>
<div class="section">
<p>There

View File

@ -5,10 +5,10 @@
/Producer (FOP 0.20.5) >>
endobj
5 0 obj
<< /Length 1115 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1113 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gb!$G9lo#B&;KZO$6@53W]k9ICdOP`P=a5[dnAEt!C8gORi4Z:^TSn%I4u(M/f6Qu5V)`b?+hcW?/#04U4=qR5W\?WoeGhWYioMGj;W_>r>%*jBf#hS$N07??;IG:iWe2$GTd%P5A[5AGK.,clStMnIs*foQHm-?;6D7rjp(_fkuW9P8UVE3V0PI;7%6iam]H;hfIlOSITofT^+bJa!4,V)0b+f8okNaP[D!`crot;@qgDZ/Q,oMcirC<R4cG2#OC1TsY8>.FFE03DgO%D/GNrrb][MfLhU*Qmad9XH*(>sh([0>P%hOHi!(FagE2O5c4Nk&\/+QG3O1@heA$Z`U8iek<0JVAPD"J$fgc$)54&K$fAj``m5pu3!*MF\&CN:;4(+,C4&R4h40sA])K64jDS(%7PP_nM'RnQXB(a.Jf#AaCNo`5O!^bp?PMcD>_SL2%%VC(D/5Z;4dMa/&4Fq'2FFK;?W]H=V`=&E;V$hEk.b<RR)-HV3QSIMp6pSNG(H[M0:pWg0@;bQq#W-OJn-Ice\ff3boDn9;t*[e!`7-t8OYQQk0hL[rR<j-b8hOXXFAh.$3G)cqK`TtSIC6N.kn54.g;]]/:i[sGaB5k>``"f*F!Sj\Z$ZMP:Z/R@XRV2+Fc7;Mi!*s&(aTo[7c_T2kLC9M_n<2m5`3+_2P=FK)<nq_n-^Hi!b<1D!@o^Q1Vf'R?&oSu;JCQQ&<o#UAB5ml/\TGN&K,o`PjUp!>[4%"$)E_)4k=*U&',7q9<a8$64,l_#V:P@soI!MmSp3lp0`G8(9d(U3UFslb7]W7![ELG2Vf_q^]b>]Vn1E!A[W3+jW+MX;6S\O\("a![__jRXr?R2[jbYh&pu/O@)%=J^<.9=q4!1DsBBJ_(UG0;BO)iDb/o@2rIBcrlgO.TJVUm-MSDOD'M@7U[W"H3mW--s=V01%3mi;8("kP*\;A>+,`@Z2W1[)(1EfBBVSAG2fpAX?S[-lm^3@![:km/G'3%+rtpV-Ci<&:!\#9c&LC-UHZ2Wbu+&I3?OoD"F5l[>0QhC@8Pr^AfT,D&a&8#b8<5&Q$<(\?p?;L0p4\W%r9]*TLajjFY2[5ZB5CdbfI1MG29"8,g"a8~>
Gb!$G9lo#B&;KZO$6@53W]k9ICdOP`P=a5[dnAEt!C8gORi4Y_IpYbOI4uP7>VL*sJQDKN]6[Q8S%SK06ig^-JUH<Al=8O8@*ujlaYsElqCQ+]dUW@K(&6J^]Uhf(`"fd/ks57"^Y;LPl&;h5f5Weas6O](/=h']Wa8Dgbn^!_nuqmJZM#o`9CmhRK^LMlr+=3/eTP@k\(dc/b(WtL"dm!4)WMB;gocr=T%j/$lE>Xo\)4)`fne(e3=[b6f>EEC"UTpJnTI4b:+&Q\[CnNTGc/7;_)qPA_)lrGchW__JWg47o`BO[p&Um!+.u0W#O_5XQks>]'NNfml7k4h>AP)7<_:=9$tb55Sr>k,OS]7BE[U-Ab\Y@C53O7U[j+kjGtTb7cGJWt4]4q%1?L1!CQQ<5`TI,I2_)adekIJ>*t/^>pAl3uDLFdf5&^rP`F@@)9W(IcTW(NY#\]*sIM'Z<d&t3hRVikJNVEBpodoAaHU1t%N=<1'@Znt!e]BL\HZ/a>\]8oJGjSbj1prR?4Z*aJdu7J43Z2RImnNO,g&5I3M5VH2':-I_Sk%/*h!,Ube%='Nl=)%ig<O]S?L^)IJD0):,^^6[jHQQCW-C^9o*fNn)K>fBIK6kB('./d.ond,XEb"Gj0GB>!mi6:P'nJ.nk=omFh!NY##@\@,j[:b1"cq>'#cGHH=j_*[ELH%0iiFuF6Ypa8)d6R)6hg!:TBoHp'bhG-KhP`1"^1W>96'N<D13]Y/+UHG@&2r2F2$s\)JV&fP*2-,dk)TVE]n5qOOP6%ca`=h%]EG39p)4Bs]@HW+DQO4f/#dl(-2uJWE(:fZI2,;9_4U]5%3*,-VM=__!qNI>idM=<:"39-:<OT#"5S7H:k4\oWGFH"NYnA(]m_P[!j#Ab-=JX4=f8QF"m2]WUJ\p_Mdg>ZdRbNHV-aWUlu;WS;@ccG>Q&E%qrkRV5YNNK?0HTYmqU0t*ir#5_'Mql>(l\qQ((N0FFA,D72uTGCqlqqeq^]kh-tK]%BZrG5]kQueW@*6=,bdmL:Ahs+\@db%=c0>at7&VLcYb2'f+E?G+`RQ%F6g?W_$D)>,7:$@rrQZf%*]lD&$,O1he6&Y^a7t[t/~>
endstream
endobj
6 0 obj
@ -267,10 +267,10 @@ endobj
>>
endobj
52 0 obj
<< /Length 625 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 627 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gaua<9lJN8&;KZL'g?4?*Gic'3DrHp:kad_\,_:4lePae8"7a)jjt/-X4T&'\Y[Sc7X=BH`oEKQ13lf;6ni"J*YOEl#sih4aQ-9`;fDd>1K:gH>:P3lcjs*)S4.N8/HV@ANs2@$g)[c!h!UfkR!LT;<t.qej;bDWZ9dCg1:p@T+q4AXA*2&l@%KGak7.[7THDLkX,T;[NZ'$5]F_Dl@`!\B@^]#O$L[bSZ'Y?6]GG1K0aGrMBYFhuEs^Jdo2Y!Fn>RM`W\JuQ9CD,YW9UW02Ts$\0MuAkBsA."'dsSEetGGsr2.6o9j.KBVd^g9lf8,&h+Xu8LOpK$kY^f$L=Xu><d3T$po_Wu!hI(3D4rqa^,)aTOI\T3rRS>fXBtp8];l_F5L^Ar?m,]CTW-pAOphW@B9'G3<IM&flD0)<$B!9dF03(,FVS%$]"S(`Gm`$=K#0DGHi5RapLWpZ7VHZq7IX8a*&1ME(aqn,7Yg&GEc#GAGoel9_Tc&7EX#-BU;![gnTct,?=l)D.N&aE2s681NYIrq5j#3*oaaiN3Sog75Pl5En!"K(;2g4<b"K%1&L>]U?i%\8Wu;%8d@\%a.h`2!:#c\,p*m6=8In&Y*dmDT!f<nlIf~>
Gaua<9lJN8&;KZL'm%S?C2Cpt3DrHp:kad_\,_:4lePae8"7a)jjt/-95GED\Y[Sc7X=BH`oEKQ13lf;+;eN8%gdu#63'e'R=&ai$;Q\u%*B@"V%V/rr.+@u/>BIa&^UsV=A\:Vh8'ifG5q3Se>)9?C3\H`F.gdD%0G]@ZO9YD@Gkc81O#-.k..g@mSH^gqXLZ!g?@#JVua3B$Sbj5-6,lYbgqV<_-MOGp2XN$pVKT[k+LB1bu^Ta/eRg(Hke[TI-QKqC<W<mP5@9nWd=#ebq_!o9GCWI>-((aKp7?.ld2=JIa_ZsVQBf];mDG-F`N&NDC3uW6c"b"oVhLM6gO&ZX1N@MHro?KJ^.VU[SWJkhjMteaA*iUIVW+C<j2P-h,Oj^O.,&3K7ZE@UgOim<?*:+&>ApDnb/ZVn$Pl\PUMlM6@+!]FVhZm,3qV*f=rP+@@C:pfEP`ehZ`Vc9HN!qc#'<`SdLDYF/=I#jY1@-,bL994*/O8Yk\uNVD?41;XtIn?$2+PUB1'DZ]UuFnSiMiNcMs%Ypf]ahWeDHn2-NmrTNs]D^#'S3Umm(=EpmZ,g.&knsoZT5E*@W+n-3E\_F_O6%Q+O]]%.""h$VZiMYNWT=_jb$G^i~>
endstream
endobj
53 0 obj
@ -439,10 +439,10 @@ endobj
>>
endobj
79 0 obj
<< /Length 1891 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1783 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gb!;dgMYb*&:O:Sk\V7];FTETQN1_4VqK<Ug">L];])l0gVQRW9?Fa2fC)89bb7`g0)DeS'PL0t)P/s1AiSe*l/6;;pc>X]BGfF*;?-:pWW2hg0JX'A2#8:X>T8<hD:qD:Hgnf5f=t&>>K$1C_2S3JYJH"Yi;G&\8A/EB(tciK!#FK71"o\l(R><G1cIV5$`k#ZF;ct(m[5bIMM/[aWDT+ho#1<49RgthPK)>L(52!V;%Rh'HohD"Y(FPMLst?[0f[,B_A:"1`T2c3G/J3b(@l[W9$ric`Dt(.A+l(OPoT@HR;6aiXW=^O$U;eH9rpT@?.6IL1N;etI.i?6o#%EdSKR&BCmhW(.TPJTH1rQ8$AdG&nGLW?Na^`SD9F(`g*r-Z`%\h#JCI+Dr'4*f("c4!"\Xk0ZJ?%p>]<[XVhaJJA&KPcK=B.jl]sc#!oV5h.$ueZ05)Ji+YGUibf3.fG+Ya@jESY9fb5$s4j+R.cY9fP=dN!jA6A\$<\Qhilpsc$)c-m9NfE=.Jl'6F^3a0@A=R9='ApckU6;olV&K9s-a\'B@_+"Vir(AGZ$ae"'krCL@p0SZd^nf!O?4?],V3F?92;`!d"%imLpY%uVT/gh/U5K3[$!'R>b:+m<MdZ4fj;:;]Rd0,jg_;BQSj!npcZsOppD34L(=o#?H3;tr-4ss'K31/C=5k#:i=ZJ7cW$M"AfXAW.O.kRocu8;5e/s@AV3DVX!-IA8;N=7l(;b3I\M0p&a"pKn]ccPEiE#l=l:oR["itXoQ+'f`s50LZp%dV9@:Qo:Xb).b8m+J/jKGAH_04NhHfPp'%ou@1-hlOa;8)MKgA!U'P%iH;re$$]'P!?mppKc8SmM*`)Db*L>_lkO<C\$`!c/i(AX@Ms7$?2Q-*&qT9Au]m=iI:Du"d5aJaZlH.2*$T_CE>f#&9fpKk0!df\92+[sn=^7W!3pWJ5@6s9$%CCmYeOtUH"YOpDcdANXBVKVg%^'*fMR-:L<V?pG1hYs@.P]Ft(.1QcNS2K9'suruk`T&Fq-#e4%T7m&&_n(clY)i1;4@b!6E0%eMG`KBII+Pc)ebX3CNT=?kP94CH[###mF>_kfDR%GRa0s8.Af>QTG?]'JJ,FN`(3^G"U&n/@&mBIi-aH+%/$#liLZci:1L.QM!iWVq0t&b$,u@Gq;Pg9D45*X0S0si<bUYl/\u+4%-Q(Ch\;@(Rt9C-&l&<3F(=ksK:*#[rA-%paH=$eRF<bsLf?<Xf67qRq%NYO4kILD,,sHO+m<4q<N[4rocl%9:fB,-#)"5/eK8^;L1RkHX&jiQoL&etoq`:>apS\Bihf7]j(I7[StLhjZ/#?uo<tbLln.?QGs&&jdI%d.P2T\1G>GZ=C#2]irM%9k7&[Zoi@s_8a78\AJj;ru+:Lc(IRp:>Q:bbM'RG\OiuuA_lJj"/>C-.T#s@T5%MJPFq:Z4'O7c'jRN=_(hF8edNRO!rYEr>*Run0bM,44i_ldoM9O(eKHL'<[b]&IBV@d,JeHl+N4Ll>:hd4RI0Udu=DQe##]"b/_PlMQJI45&,K4?oi?SJAO-K:*L%p,+(bI`\cA:"">]nh*4j?pkgp)Osko$`'2'A)K)p0I3$$ps>#^2Z*.HtgkDIP"mR@bK?7d3MS8?JM]l?^:D#_W+Afn[.u]rQ$];bJVqO*or.kq*kYQ%<7-WO1TPdJqfsV$j-`MD`;5WUYU@cDPg&C3RR%t\](]>KNm=#cjKa=73ZiEIXR3ckcPN>5&;>!\rgb:4Y$lKW:/tKh(M*4PM0o&$#N++kkXEl\lQp,n;YKk!_@+lK!/NM\m.,7)Ff]O*M:E8A(uPSD/V,_!@"]b:I#`BgM<[0AlKS1VK]GpCKVthc/'`bZ]^.mQD!R(If\rR^Us~>
Gb!;dD/\/e&H;*)Tl3SSQk^oEP4oT^:$XE9k%(m)023&*12RDa7$j).Z<tA1/;6T`C-6T'0a*;,ME3rqgiLLlhol;nAqM`=Qc,ZOoptq8TX=]/mO35u[;u*cIX(;i7P8\OO9P,<@.j7;AVBo2UP(EFr&K!X)n[(Z%]pPkQ35`A%DW&C;.oW<i5>-4RH;`7PE;:q6a&4cE[0*'Z<2oQ4Z,tj;4]h@SCqgBfX.pW^'3I#,S%^%#'4RM^4CnseD&q90mJocd14$0/bFe@h1j`>/bJdPSY@L.8?,X`a5Sg*.m9,N(iI-_[!%75(VZ/[Q]b0,?.6IL1N;eeHtFTq=;5tl;oJT5XE%0;V+H%fmk"0(8Gs#b&<'/)%-oHbM+;EiF!1'\@!;8FR?8'UJY<p`YaJ*5YrKTk],t@iPboqU%aj*,&;T0ShE'gq/Uo)N/0\t%_-@h'5m)fL8S'(-DK;ObpR1kLO4Wqgg^A8UZeRh<OB1\=FW"72JYg*oKi$RKNjQ1!!Mpq,fVlM2#Qr<<ahq:)j"agEorg>1<3fAq1kT06JC`"7_RH?q]RN6mO`.&T.a?=N_M^`]<gS4NElTASKucY.(2$_QBY[Ir<*E^[dpn!E;)/_0=t,dDY0?LEWmY?fCiA,mfg8a$HJDKd[l&C9pl3MGjJa5iKb#)aT]6,f^-+>o$6*)*2<e7e:i=ZJ_3=PC"AfXAW.O1lS(G<k;5e`.@AV30[d)hY'H7NK7l,iu3I\M@lijmj$4@5ZTIXNIe[$;I4=_]f>QK)-Z4C@>\$/BX8f+`u$_Z-:=01":!qHUSb6ZBH*((B'mgF$"_?=VqOa;8'MI`cPTa4kf\i&BE$]'LuE28C-c8T$QDGTXV*L>_lkO<[d$`!c*i(AX@Ms9;*2Wr1pHY#bC?9LB5-OUeCeE*d4HFe.VJe"(i="neg2PK]K^u"$<?).h=/?-uqSb5gVYb:]M#2V_Al(p\]&sp1-k313'4LMa0#M4%I9!Zt5<V?pG1`+%i2e$Ij/:iaJ)iMa2Wd)GYd3Z%ko8R_s*M`g.,.K0Rhm5B,UG`Q"K[Rr([G]&=HeW\(Qd@LQA:4@7O4U_MX6Y]$YMN!OHJL7Nld37,4i?o))71SN+u&$MJf#qOY(0oZ(>(*;"um\/#Xa$;<k,'+*n!GY$_fjp3ra,_(ddci;87A.9+Qg@H8('eJlcXK^NKaTR0fBZ(NN_Z7W/KSRn5I01X&S;_[L.S;"W)9^gg1'C1ki'0Vhn.i*G5;1mdsr]6eFH+2h?t/Ks=`kZ-II]^)c6S3frl_upX3buT>"Kc-$3[]Ed=#1+"k"J9BS?BuB?HGj[*W/[F!Q$J6*@ZU<AS_ZG7fk@a;iM%CfX\>Vbc`l`sZ4qPMO56#N1Mj[O@P[]BKpWk^nRC!Q*Yli\qJ^i+]J.iUo18@8b8-/4rpGSYq/>;-_;h<alC1`DKm;;`Gc7ON=Gu6]\]q5a?)H_+&?qqIF%Em]!L*3udI\LE1e,55%[D,IjH`=nUNu(X3=MuNLSh`XlH5]1HE9mmeMM[io)[BV0nXjaKstj(hp&ZbnS7DN0a[=q=5\DCMjY5E'rToerQ2G2)S7CAUF>c)PH/OE$?KrqN1OrRT4^2YHD6OkEAq(Y[-gJ),8.9TH4G6]<aX-(]?<4G&"<T)5PCXF5iS_2ID].KIb8L@gT@Z&8aJkH6PmD"0es*("*1.XD)4_#SlE:)2o1L5VJ#N<eK`C_6To2Q"a:&:?8J1qAbFDVhX"joFi9`/OJ(429FmT*9/IE\;"nFM/nP%`hd3.!~>
endstream
endobj
80 0 obj
@ -454,10 +454,10 @@ endobj
>>
endobj
81 0 obj
<< /Length 2255 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 2177 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
GatU5D/\/e&H88.+ll:c+^l["ZE3^t?fnN/OD[^A'psc.[O>@L/>f.P-iN]_ALkEg8O_k^1_KCIhE?)Wg`?4ap?\^3Z`e"m\!I%YS>AF>^$0VgIn-cT^5BfGM;j@n1N.R4qq9^O5D[eoi:U1CfF(%Aj>K6o]<?VX'6o9cj,ct5aLg8SJ%`Ll>P=)d<Q")0mjd\$HW^W&h$TJ4;8`)"RT6Gd%F]MN3>qTKk?uSmmI``Kop/SeGdSrf+;`nlhX)tN[$-#.]sK_7XTZ(*g2%0poSD[jf8bGfl8uAq`W/;1$S+qV@bfKb0CBh\dBX`8E!MUBVdN!2*iEU;!OsYo^#O\R6gt.*<)aaI[ot"O'B0+0g_AX8=eje*Tn>Z(1;7Lg!!3L4270;)hF0:Qr[?`hR*9m8hm;kUX%(0ZFRt)c:r!i!3T+4f'Sf6!9%KXXOTm+Xp^p4(Kn@oX_'N9i"tNM0W45P=$T(et#tGjR;AG0s-NA+Fj:k&#<Ch]B;kdBAi2n&:>6,al_cHdU1%HG*[WR6AZ4X]%jK$hRTF2,s;6$&Jb@`#-!YE&E*i.B;)G%X(4##nX<WciJ)6Lh:)iem3Kp:cD[?>3C@<_Y]YetUqSJSALn/QUZ']2YMVfb]U*+3.FH7L3LGR&VB"mfUWirFX*I@M>!0,e0'b`78cf^&=/rifub?>%TXJS2oL-!)mshA'G9k':"kT0a*c'?u)n"$B'Vb1.OQMkT3#Koh8]W#Cu[.plZO2f(=gitT+\WH_bd9`o$S,T-t(1LG2E;Y9jnJZepX3l7^(1cD'u<%KG8!WI0;PcH]5TZp((@Y&0\BY"kUN`"kkW=DUg)2WKU&Cs-@;)HhAoMN8"V+Os'Es0TVA4h*3l]3Y)L-1.CZd\1fM9ASk[cPct'Q/J01=<16LA+6@U]f#jigH55qa>aj'L.ke4<:FU/FWhBB!hN6=DaR2Fq5?+;):&-G_,tuct"<(b/#!%3FQi;-L"2;%KucD.W/cZPt^;8\W[I241GPCa2u6?4G^Gd#(u-8e)Pp;Y0M$#/7.b&An+8;(2HZRpMsu!=Gg)NG&d'+NjR7e>c9=`IA);TKbt'=8KNOFng(c0r7[^qDjZer-V:7nmp$a%QGke8[HDWX"VKoOiKP<=d\tU:R-^86L[W35-COUUFe]ZKPimY7k-ohZF4(iu:G>6;BI=ETs/7uu:5tH/U1=(RD,:uu?q07)!2=9`%DtM62(dXp$(,Htb8-Dt\0>B>i/8WAXL5PlSAG!QU1P6V@PDWV=P7R&LGu!pn5pbs>-9Uin]6hl\8",nZ-Ek3M?RDZ`eq,Bkf(^e8=mBa=3p:XdSJU$:YkC.9/p4`F-XH()-Uc^5U;X2g,MMF9L/c(TLV*+7kY,@q4pWRI3Ft>;!fG"5;pI<;sF]M8)Y:WP(08GZ(]8&6c%FNeE^$Z>s=9hUHN6tkm-MhdllZMP:g5ol6_)E.%CQa`TgjAKe'p\'&jtmJ?kh*nqY:$S$\l3K?Ek9bK[1@rn(gI$OAI8D/<efd=ZdHX7sLgD:p"9Z?9$(Ah`D^e;67tS>#epA5+<'=C?qMI5<nj)#u6mA/JI5d`aDd\1.ruLCs&c^d*^H3,Z,"m^4]'\q:<]bgMnDn^QGE#h7ba:M68#Y<DWt5<a=IUPpmcag=:T#U89'UQ8LJ`!^+2Us:_LfKi3j+?Y0+]1Cik^N*H2+':mgMp>0d59Y$2oZJWI\,@EQ/KU]"$0'mI39n4X9auiR'Er`Sd@cFE+;C$q%QA@/U@&X6fUH#ql\sr4/R^5d)Fo\Y2U4DKM%][?g!X&,5FD6lm/\XJrY>O.#[*;QOqQ+O:X-B+Xo(G"A?jCH[!i/"U@^8c8[%d^BDM!'!9iK+oqEH7&C#&lHu0)uO"s`P4&f"d6-EK2g)WWD#9QFhU3.H<\C^T)!5Mn>1b8VaFDl=o"bgq3B3<H!7(I&JjZY![JM!3pomi4^O;H$WS7Uo$HT`<9!L`SKf5/WSf"<KDc*%'6/^ba<*(E<(__!'V>e]\7WlQ^>YoW<r'+9\AOua.\bsP9t0UjV\87E(GoROuO45"Oro-Oc8PA)Fj\6Z?Hhmu.LO8[dnGPVqJhroqGJb"dGn:NlO'n8aAp.6)OlHXbA%]hEYigqGdOaj-A]N>LT%f?FtbCgTW;%/K4K/b4B2"kN:#D>TcGfT7ol=.Hkcq@uA=+tXe8"oBIp#qbu%Kh5d2nn6rZTiVPLHBQWI<M(^"FP@ulGK9!>C`seVL#-N66pOM!J?6JIK~>
GatU5;01_T&:WeD0_]TZ-h5m02UHfnOcZB`OZ(Q]B1dpk[L@&59e?Gj^V7"YdB5&5PKlm?,\0KYhsPs*P.jXoGE^Um/Yg#;J+p(ZY3BfDEVTR"S[GZgNhJK#KXU'G?X)F5rqssdDAsoERJ8?sT;D./7f+mg2Dn"6g65P6c\l5%X@RZj^,GSJ/cmBRf7mrPn9aM[p=%$X&J]^]Z`_(N*RUHbN9Oe?U\$;3A#9^-?bYDkB&gqZrR9S=`-\iLj3uiV;SD7#mE&-=<B>QQ.TNI*@VSa=aFb]a'[h;/aG67O]A)$+o3aJJ4MRC#;?`#YFefqCNb4Rq+<drUpV[t4g^S]Rs7ZD$i`MXK*o5E>;P)c[_La`[ZeTd=V-o'i]S"t8'RZB&_0/-*d8r"4ME.SRR_'imJcZqSH_?qm"jmBfR>A:A6^j`H06T8lBM>G*h<Saf;UZ4\d6XWL"Y7GlfJ"^P)N4<@j-l&D-l_]q^f2CJ),Yo$3uA][)MD'5XTO0@4$t@!>Z-6%KKb"uJNbRe!C;9A;%3Y*D4G=7&n@J/5Bp0.7)H0Z^c>4hS1MG]kn6m`)m@gIUSf:1$l$jT<c/AQ-_co2@iW-=ZGpK6LCOO=4$"l+3(caR6+d;"JCRohK^a:hK_R,.mpYu?AM;iO:'Zd6CJAc(jM?_*KH!l(h++gd'FH4d/$rdfFs+"&`A1lecrb_3$&+<FW<,N:e?><3:/EcPUJt5L-&TZi]Fg^bL-Z&O;YQeUN9j\>=,KgA2PRAUs"naHF<SK!V[Fi?rLn4u!(8cnSned<R*=SB=S.S"^_eRo,1atm%@h+SjdD)`M5HV;f=,S3%\q9`(sl*qF4c%@Hn5/c,RN-S^`N#4Man]T%QFegXh\iZ#^#?@FW`nAj+M`!'KUcXMO@p_6JXWkcCoeFi]cA6<MEe7@&M7j<p31!0L;*?.DSkM+Q%%>fF0&Z_AHp-7Fa'sZZOUM/X7WHZrd;a@4L+[6&LQ),KWKL0FYdC8C<K4"'t_NFdPcCBP^(tW*%-N"o-F)4]b@?5g*@,;MV1!mg6R8W/f!@$WA'JcDr5Aks8DT0m1'55tm>UOHh;ba;Z<oC6?Wsc!8#'8T;g>Lcdc0Kd8-(U"VD")lIW]"HFd1W>aV&[GhghTuEFfrF7%B,K3pobL9B)L'0]p&<+&%#4>BWATqOG"uSc*MC0ep!0%crXq16Ib6duAVIT<'iBZ<C#TNA[$>GfiL.XA&fKhq[5#*/ab_'T@BeZbr3mmsh`mm'ZNTnD78=*3W$qG7DIH=\nA%!.!X6J_I3RUO/:n^2^oS"9Nc`A!<[6+_9A1b?-r^`U6a&Gg;H:JP(Vp+WU%,NJ84X;WrKRJFY-\PP35/%<\Vl!;YHY1S<,]ap:j%R:%he*WEI`m:^m9!VR@#Fq\:*b)_7WjZ[)GR+_@_9W<.#S6_UW,@q,!TI9$k$@KC?aMJ^f"ALDIBH'.jCD^^=^lh>4>Rm33K$u&iFN1&4=D@MOil&:j=e=%`eG@BXt'VLa,D&VQ0/9MWK:8;9T*.2c?I%5Yj%^d!SWcIl@"Xmf2.O$XPmMqH%XHFK6m<ZSAi;/q?L:-+"g]_?:+J"$Od]0>&:Wk;b&8FoeB'5a@,Ws0rVmUI5i;MSdat^s+K9&nLW5_3k,Lf=I+p+%Nk=ShmVC-gR%tHbp^L;8>`Pj[=RY.%CSAarA5tQ!\eCnVV<O=\p%4VoTUB1aS"VITWa(Sh.3T*<`,B8fO1$61j(kX47#TR[2_>cKQO/)DdcO?E3,t?;+4E:h@mD[ZSAP"nbUM,ZFeX^-RM42(A&F#&.,7+nqp'4?Hso`(i$NBU,JsBouin4E`!\5uqCbUGIk'r*!1`Vh:=%`roc$k!Kk[17I[ZD:j2Z`GAR(f0jP`#43fJ_TGI`SLmZ?Pqo+u\`tYDrN``*8`9'267H40;LP`*Vb/%Q>IC@'e6[X'.]rPYBXW8VQ+`n9A\1E%1X,hafpak&YO`M()8>!N9_d7`r95-m*m<`Nj"D^TM]Zuf/Aiufs34kj%q+^/X_S`6^Ue%2g!d-355cBtRJW<$lP:]E65FgP5#BnQ^<YSp[@Z'ZD")b@q/tT"gHCI1D\VU[YP1Vm=QUdXmH*tXg4ngZ]'bA\EC"[0!9E_M3;1g,;6_-j).qPK57D45g;2q_SU>>[qE1R;Xgn/e_#4LfCkee~>
endstream
endobj
82 0 obj
@ -469,10 +469,10 @@ endobj
>>
endobj
83 0 obj
<< /Length 1688 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1887 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gatm<gMZ%0&:O:S#lBhd0pe\(?%AZbB")*B=DZI+BHN[,ck9n'[3ShJS`K648QIZXMb.(TJRafdLGNSg>j1M8m&/t3T,I7Op@EEm:dViL%.4'[[*NeXi^D/?X-L/_rAqQ>F[Nr9!*4SS#`j!&@[YPU[u&g'k4KYs-Qrr3A`<T5@<A3aV>$=cr:slgcU]akqHb%^l-4KtfS/!L1QHXdY%mI3)P7P?DKJm=':c;-EV++?WY3C<gBMKN2_"pu`lGG9$4R^%<pALP*%j1])+P2M$u/dA#:NS^h$+u_YUPEmR/1.d*AH&n$_##*=0Fd]bMU$[8,"],_SJAd+B8-W6'94chE`!%b(sH<LHQnH0^O_O.O/#!JCm2tUJYYl&<Z`U"OT"o+qcpbJi"#n,ttOLK3Pg+:VCE2K+q1jEd.Vp1=smA&(t@L#8c(AW\dV?d"e%FU:3tuWnJD>7cTY5.O_;SmlV59%CBXGA>S5+<aCsk(OZ>i<On%^!.]i.)[-5eSk</L<,T0@M"&$i_+(@^)CXoMKj/qU1:h:\Or9X$VGRmgM$=[:b2ku)He(0Ob4giEiVOl<3W8Z]'&9t5Os-t0i3^fGhJ-C!*\;V*?#dhOX5BQPkO.,`<M4N/[AHm]cH+`'EXgCDB&en(RuABU:X@P2*m(IoJ;dU0<#19(\_M+_1P<R-7Me/-9U(h>aK2;\'>t49KBYD?.$j*m->0>MCV=a7CkP-S*eB<18e!74pD<*L<BGh@EJPes9X%:dQ]i^N#\-1+X3OjiImMMq\\B+sk49ZLaZeH"#)(hD?_ih!jE[NYCqK8Mp"@jWEU%(%.qs&&KH3A2#$u5gKOgnPml(_">``n917I0`nuZZ+59Y^b@uLh>4(R-7>#.f]@t1</3>b:Z/K\5[PBFJa`.ra4q[ufi<FJflKuf%9j`OQ_(@![(#G(7Y4+:up5qUU/2F!Bnp&d>gi4ikN:4,=?X@bbqR]:NIWr)cY8mH*=L'P9F_[R5JQS"[,RY)(EW^agj8eD$,Cc<<dSN;<(O%'Vs<!-CR=D4@7&]\>)5Hu!cRf_PX.lKAf.Zs@GTh:]Bs4Aa(ppK/nH)A`moYX:>$.YtG+;bC%!AB4:%iV<a#dhf2S@`5,p]@7p).eYWZGL=0PIMJm-qK72ljY\U]D.fp_m\XPfu00o<T6Z2c16qag#MSS(AkAOYh]NTK@:khQtS0u6qQV:H:EE?\#Q%WogF+AO2Gglo>g0T+.i..N[BMd0hK5i.Q=BEp'3"NS1k9BWi.Z)=UU7`!;gG#&W%`IO8=YP?Pun;Kk^7+Y2@i\4<a'@>l?WT#`\W;5=V<PCgqe.>P>5Ak]9fF#:+bVY?2F"Lp<XIU>1)ok*+Bs(5;I23k_Gn2_K<GVKul"G>\NbV["KkPq.6/:P-+#b@7d40d2@6ChTq>Q)WU:6KcpGj/dIjVV?R]l=B.e2U1mf8aZG+0Vi"G`d[:7JpXc9MZ>sHp<LKaiC0&PN$#5fT6<!W+cdh16m7=SrHW.<7Q4nU*mr3kR$5RI/e'%k[_Fuu7Suf9E?lqY>dXFQ#C`sW[0;OA1OUp]9TG-:ho[f?O&9:DUHQG13:\I/i&@jjgIWeU)\n=<Tip89eU"pn5`OdKk?P62`$l/9d4p^T"+X]:^8l;GE9)=0ZY58`CbHn6D!U[q5NpC1^t@5sqU#2*0S&mNM0c)o~>
Gatm<=`<%a&:W67+T1:#d>#9onc4OK3U._Ui4?>C(9%,7,+&"t&ukA^m=4":.>fq2dS\^XE6MVU1%WXK(Z2>15HP].(>QHUhiE]JAZc[L!W;E0p3Gm-&X+tn9'r\r^Fn@`2Rk;q,M."j^HUiJ?g5Rmj-9;`r6*t]3eo5]5@21b=aK:d2cl]j4a=BW(2bLf$id`cDtQ7/F5a4/c<ln='C;!j'\u'AT8Vd&EHGbMqN^Z]Zhi@PUF042LIVs3M"sF?l#>$C5]7G?i8FS$Ws:dopK3k&%6#EQC\7>:,S/q^<9&%"(SsmS]6o"/nI6g@p!Ylo9rSfYq`IWUd;u<DeSOO&p.KJ7`HPX1d^*!<$O#[kCa)K,-3_>IR5Y,O+U5.VLdaj/QK_<1of/5%`V)MH&'O-tZe,Jq1n+r+:nN8[WjI]e`Wr=nre-mi!YAQV'HDnq69>rtHu*ipV*USoV9TM#>aK&`D>+*F=bB`/W>\X</m_$$U3._`4D9."!2@XY'+81WZd'M,\q>SLYi)o(N=OOf^+j?EQ9H8+JSbFhH8bTtGq?P=<,"s*(St.'+D0T*H)\9lSnq#WUtL>Fe_Ma/`ertU3ChKDH.;m#=D!9"U<'Ts76W$k$p<F'"hmi:6'/o"qJ*@ebdn,_lS0Gc(YJ,R<kHco89.DE]e1g6+=[];&Nr<:M8g[:9nbJa'm:'TV52a]9hd2pCT^0]#+``r68Zd'E16TpR8J"H,YC``j[KTi/"$p)qN)LVUtQ'edC!.fb.X/^rA\>:llSlNO9Oh.7R$r_6((K"RsFU#m0<n0MW0^hm!t$d*U"[f1q]3N>>jOi1-)dP$<t^YT>*"IRbfdbPe"e([Ha=ZJff10mK(.\V%Zq.a@'so77B\M3A3(`c]G8&U,Fu3!9JE!9pO(<.]CLh^b*:1SB.a7Wt`fXFqc4kB0[Unk%mT$&!X,;r]U5&pVQX[\,2FD@&"YLIcVfL]a&e?-:^2.l4KTY.iu%<kCX0EC<<f>:>^bPIG'Nt>H>%V;>no#]KZ!@cMSRO-R[nPMe#8e`Jdpb":Cle$?J+[Os8LoGF4dD0)(rOc+IBL0eoVuiJ=4IBes1<WiMpPla*VV>X'AP.k#.'lFFOV:PmrY"I>IN(q9m;W@J6*>(pAH6*!p"h-I<3K48;[=Y>3",^U%`E67ai#?@g3ZE2K![U".f2R@6AV^<A:PH=(%HGGt_d)0lZ&c;;u1P^dR"iDLY)De:=/m@OE:2)dL/BTj/.QQOt/rffD&VVIc:;J'NPj386F4+rmjnQ9j*f_W(j&Zbt3>E\Z?1r:dAbIHfe@bfT\jOPsH[E.JI"*EW[mh^\4@D=\U6-D'4n5UuG:,OpB(Q9:pM/18%17FuUeM!:^?]6aZR#tS5>Kt`K.?6tU%%iu@2s$D8Lf5pgQc^tkPU1F,i3>D/s*?c?)09ToR5JWb][W+$iZN+3c@q]cLaB#b5++`E9)`U?8IJ`2r;oHdFaH%`tolsRSlBj&f%8]:=3NiY;%W[\2L/,T(t8gTHl`k'1aq+kE4O$;?#g,ei5dD(1=3sTTe<*5HSpQ97aoHT?/eR"Uu&VDKNG%m?r'6Oi&?^H1%qgnK<=R;r_gG=NkV(etDnlnpnZD$'@cX9'V!KaFE[^>Xi4X.-aV<.3gV%1#VM9<Kh"VBYHE,_J)4X,.2f"0G-l(KX-c!6Y#h5VikAs0P#/?#TOV@;bJnB1c4L<RB$Fja,,ht`$k#.diMXNA>)-<_du0Ck/[aJn>Y%Q&h/Y'`":V>K@DjFT&.LnUc3LEHNG?Z#]q.QfQ&ED*(U;Q6SA.1md-N<F8sT!.Z?Et'U"M%iOZib3`)Ll+S<88D3<A0#6XZU=0;A#[Y/.(>>L*)d+r!$0gr<fh_emGGlIir&7f)~>
endstream
endobj
84 0 obj
@ -484,10 +484,10 @@ endobj
>>
endobj
85 0 obj
<< /Length 1332 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1375 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gat%#?#SIU'Rf_Zd+b!tAjf5?a3YQggNc=EA0'\&A_dG?e$`(UoH^G.qWgXMP'JL=:5lsh01?pghL"Z6\QI+*>L!/:@;dNC^X"YWq-G&]npa&&m5YIT'R;S-f!E,'MlVWj/r>>cq.6t)q.c3Ks01,",QWf$aBK[tJ)2^a<c7?iJ_bSB5_6V:ceiHs6.A?2]CGmn<Zagc+\%RpZRA6%J6=uBAd.],'GqqmJ_%W;W]dFdbr3.GrN+a_c\bnq]\Y>M()7flJ0hWmacJD^qptNjrR'bO<c`fE(otPWFX*D!VCTm_jJU,,BuEQ2+b,:bYF#Ep@S['@d0fZqJ8Oc_D#^(7"-5d)ZM4bjDKt"Z==(rnC8Q=p%<1C;@Cm#qZK6b4FP(WY8h,^:VlX#jP"8OK6#_H0"2-87_GF0V4DfG%Pr1YSXH@rnM>>6;*&g+V*o&C<\OK;30L8:aH\9^k3112qI]CE.\qX64VafIrYU>MS%S?k@2c#XC0I#%!(1fn_De7Erc7;2=b$Y3SK$es1,nM?$DB='H@gXOj=F.hiU`1d3KS1Rq=n9USr($WV6Hq7(Pe0l$o^"4\6sjgO!r#a?q+Stt24o+lLk8])7bR4'pcb"ZId)\1RkTj;s-=7S,<I*6Ai!#_h):A&_B:>X$U\#gSl=+=a$OO@>Lk.c0kgjU\@mRCfEXagcrq-/>LM4H'V+GD);#24Do$HD,6DR;jYh5GM/DB$X*.t@Uu1gKdFNjMR95(PN&F*2^=?,oq;Kh5gQ`<7OleZ>A5\C#%3Q`?N.Rb18@i^<E4P%9E@q`K4_G6WoOE3dBmj,J__a[<+J6Knnp).G^.(:V\K.ZIYe)o!94$?3bNe>J?Lu%5";CU:d(WNq_ha9>GTVlT.o@!u;>e6$o;l#n@2MDD9]:B>)X@3HhAR@"eG-a@JA0M"'UVL&XZ1L5r(tu_*,B]^o:5t18Fbo2O\T3l')S+dJ$_4g\%oJfkdESWm!0&W9]u)[CO6Ql`DhQ>JREmjF;Qu1F@cG5b%CtP,sCFd$iLuY1ShaqQgnJhFR`,YLD`$cj8'tp=&qoD:5[D@br4Ge$Hj3='O3aM?Ho&,fHg^AJX[):$o2@:1>iFF98;24i2-"`3S*$iLBn,f#PIC[lB$$58Pk_kJHu*7GKgT_I-E]d/Ghe5GOYGo`Ggl>&*)+P=07P^"=9jn(&KC%V`4-hGS=SfcWPqLUj)VJh3j10%!IU.melQO)C,mqmqPN%b$sA,Ok`.gYho!3F'\/+MJ,S]j>%i[>Cl7`g?MJ*c2aP:p&j30a8Y&Fodls9Ct(l^JSA\p%CL?]D#$i*^SrCo,54;[Qea@~>
Gat%#>Ar7S'Roe[i7j]oAk?8ZC$5cFm8B_sMJdG2ddBD`.@Z\UH5+mhrUlp<2@jNEFV#hG+aL6gc:;ocRf0L[rAUKbUOV1G14fg9//J)#'R/I+3(Pc\6R3OqR/JmHNX>oe=\;$1rJrAorl69W&X"(i4o+h61qJJfj+9a\LKCqkU7n/^Pr$iY7e@3[K5)+[N!<t'6XO.FR,T&A%sgSrDE4cKbmjLSXkrsG$=fSpajFsil-(]^0quMBeb0g_30*7]Z1h[3hUSG>*SNQ-ic%5^c?H?E@P4&\)C6X2CO'd2eJ9\eD4A2!_b=+tV%Ff[g&`(-=n73-EEVb708DM5Qs)Llfn.;[p:=ULbfG22S%B\Z6sD/Qbj0`QK<E$`BBbS[`infi/l$Cu7<`#&%Hd9<KUFO1M.NOlD8OB-P.]Xrc-V/sOY3+01Wq/bHq'heEBl;`PIfR50FK9>:DQd:1np(I-Dr!*)]L].SQ=LRR0;;h-3<%t'aLVA;&<%jWFc0U<:a'?M$ud@bpLZbli82rf-#ePU`jK7rWf,JHT<o[nUJC9=qp@a60o%"e=>^[/=<.4[MeFW;I0oeHc`9mnScY+^JU@9B\tni@WA6H7T0D#=09,g9$-di2*1.s?p6Usebh0lnEQ>9aYtD)h/J=-im[I>Y,3K3f)t#<?"D/:4+R8ATLEh151kFt"Wj1fO<I\+0,:3^3Nk";5Skt)[c[XB+>od<_0&J'Z@]3VVOM[uWuRR].n*Hpa&Dr-]["%[Y,dl5r9M6Go+)i@3NlW;Hk[#k>h"j)g#elUG:@KQIE'=9R0eqHFG1Kmh]ek.H^!B3gHa7<$`K.?/7PSHOkW`UBC3*^G.0UmbQ&X+9/[^g6AV%7.UKJ^0)\3^TkT]8q>g=C=cr$-5a\*P)[:FmlZpJ7%D6C7n2ijNN3pb/9g!q4Q8cGXF&J42fQAF1XF^lQ@pTkNd@4mABYa3<$rO6n*_JA`5@&0;/'c*Y?;EWf*W;Klj0ZNoT0]4?Y"/<sIS?BiqWo-sFS=9o:ZW,9nJ,!Ol@/GsW1J?/^=3f\-JE6<0aNVWa*VT=G#+!1RM=Y:ihU]C)[7QDD$oq`I.["D2stho`k'nR&:9H1g@9nde'rX\4S.i$RaDX?qj>bs52/UB)\i8(((B%$?iOlT.?;Vh,=!]<cFP=LY>-!HK<=&*)EGL5,f*Wh#S8\>)OamYl'OSmgl=4(#)(dnb'Kn-Dqs26-/IN=5#;OqipM(q*hT@[)fleR1`8[[Qu?eRP_+iC)*2(ga+GXp_8D_]qIR<q;6"Q`_-"i=@fI_LB%uchP4en-*G_I$OG/fdXr%:<\mE)eV0X,bMSmN;`lkqX3\aI3.O4T")^+I9V>Sonq6=u?"/GSUE<~>
endstream
endobj
86 0 obj
@ -499,10 +499,10 @@ endobj
>>
endobj
87 0 obj
<< /Length 1440 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1291 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gatm;D/\/e&H;*)Tl3SSKFoY201-=ZDW\f'2Pc8_%pLqW[V8sE/@cIc35^2m:Qf9126%P"+E6[/na;uqj_6grrAYaE),Y;]T+@^8-b^CO+oLMtrd?3s8sflH-,'-<j(S@b!"F=mdIF8on0io:Zdm-8UjVs4*qePs<%ns]gm`/o5L(;e'Bc$>2O_q^nd-_q&uAc(A]T.o%?XZS>TjQ].ZoN>5Kb8J>;"=p9]etZrmRA8e-?a7KF\'`jJ"2krsMM5\YDHa:9u,pelZdOWb4[P;k:M)ZB29kXEp)0[tX[Ckm[j2h'lK,=]lkY4)NBN8EVuCr52[GH9hc?GEfl+HdCug$Y_m(c?m5jDP[*><.K.-=SiHC^c$YVSW4f;SadH"+E;&Q;k!0;NQ]L\6elHgAagf5F5/%I-i@qlPWN#!/Kl*eEMfc[:a%cgj4%s4CS'?Lq-Ouq]CE4nf70>4\aAn%1]f`'[PBH,;+i\lHkIg3+*dH;VmQF=IM."!>'n-tJ,S<5K13"pKVuZ'M/NitX%DC&\Z[ARLUe-%Y+VkM*"bXK^mS0#)K)iL?)UJa):6rN'>>T@7idC.!gEf9c,.707];t3L)I-dD*_8J#d1I4Z>L+SBO&"q3hW*;goIue^-=Z"25(-),#2OK\(E)h*WNT!D$e_TPQ0Q6<ijFNeX&.AYu<MRiiG$j!YV%tJbCPVSA9."1^'%+S6L\+RspO#*Zu8BY2>TC66k?o=NU^[WDnpmNqi@@ZQ3MeOE$nK%b]1mTpE3RO#!nb->UQf4,).:"=N@n_um.6)o)cI;brLnW4"`?^S*%9DEUj1nnLVU'psY.M8R_gKrTY67/@Y0?k`bBH5b6'XugUi-mGR8Ap*oHqHMZNXk]/!M3;3WV^?=mY#E9N;rs_!Wrpf>Y2,aY7O8o$&>2*2=4uP5(2LV"lLuf:@pJ[2nB68B@#cT_o8W)Ef:ttFMbVJ%E4nQQgpX192)99Efci93Ge3W^RsUB$V6Zunr7N^/hDL!4;h\1/S$9TFWX<lu/e?X0>cVtUaVJT^5J&N4rZ(s`EWS#ap,kg`<MWj3:S`4aY1LUn_"Q28#-$]M]5J92:e<\PD2l.GYk$F2>cn9+_NqjR51p0rCVgDl5'G';?#"iGmM?M>kEPH:^B;tSG3@Jn1=WJk0KE*AeHX)@Y".h;DGR_d<g\dCG)fqcE1#RC7q_UW=FfLT!L^sT;s!_[UG+?Uf1WKFE1[^X$SR*':Kc-Brkg?oZl#Nn,Z2X%&5TB#MeXk0Mpq&6L='PEZ8Pri(7V.Xq@V64EDQ5cS[mSi=5B05qlg5L$m9mN8E/ihqCZ(.p*`:W;nq/5?iagN(\guKmbT3'?492X*OGd[gc_]S]kNWN`QdD\">pnbrQnB]"LeMk?:3V:/,aQ@OHq\R)\HCHThbq,bB$$-[OXb`'!!(CU?[@)(%3R4rV~>
Gatm;gMZ%0&:O:Sn1arQL)3^R@V_Xne-d(,Rl9[uZu,o`VJT`Va\1$l/Gel_A]l'nFLIYjK#F5mkO<G8Ak!Za#2d`2*sWL^c0r817PI,J*1O^*Yht4f)+/A=,e=rm1)PL!J41?9_=Gd+5a7ZaZdjT5;8iB/6qM,\"iE'Od-MhPEAfFkF)DOmTX3TrjG`W8QKRI-2#EsW^aY:)JG4?"P:$oZ[482!(kWIF3fK]3o%?Ir%DXL[a'l*9;E<?7Tq,&a!+G=I"o,fIj:XOqWl*$F$LGF*_4J7;+KOLc>_;-m/=4V8F6"'\qFR]uLD@m^a:_1X/6$aL4Lkt4a!Uqd?2XA5+lNGkhn&7jBA\@S7.bV2&=bg:bS_'@L=5,DJ`OOaMdL`9@:BWnX@K@SM]1I'/')foGI@P%Gu:tqGHb)MJ?kNPcGNlncp<_nhA#8TV>=<X%4$D;<O-=r\cQ8DmFeefmn/uBq,t!!$jIu$Lqi/GT!_iTj%dR"p#kGJ<eg"T?cq=jI_VJ7kNB\Z7]'D0?dc2?qet+mlt,mBk@[GuQ.=:jJk<0mFc'-D>ic+94_jAj>9eXEEo,HS*;t#-']+U6Xq8Csn1N0^+&L[b6ZAKjKHp@3;//<!a\mfA)P)?PlT;Hq;s&HelGaNtH=Q>_Os%pAF/n9UL@Hh3S?b<>">eE:B2Saf>SRVse@*L>Bl?o?1r_XMgoQGDU[YAW3@##GK'kYCTIR?>@0n?8A>u@V%9[DDgDhckO)Ru2V/3_DI6!uK\-[^H5B*Cm\GY5Sfh9,`:]A77MJ?//OlgdnGUQ3@4H_[bYW-N3d&.(h@[=p+_$4_)mKmeiI4pk&+;b#_^TD^F#/4fZPYXcE;Ea(enDA[C[_Nnm`[KsiZQJcd)f[GOAD-U`[#DmYKbK(&nSfN/';b>jTu1"ELF"Cr<d2\&E8i:),8,4(?=mN[M9#&D?oD?m7E(*WIlS9%Xap-<F:.2*[pTm];&J/jnjE%BkWgY"ojXZV8)UZ7Pq7%fHoJ'SlMGBa>2L`%8l/?e?A86/DNnB@:Y2mUlOAO)Xc+4iZ>T_-mI!Kb:;KbAg)t&C&Rh''![X]e;jQhFrbe2Yp>$oaE0eENJ[`juCZnp'$p@BfrQ\9@qcl):S1uV-D/GM>8KYOOH%Qd==0K2A:&D;O8XQ31N(VOZ_"oBbqtj4pc:uEp6^hH&P"<B<O%bna@Y%FP3-NCBgNrauMY3huc6R8](DG;$N0)[pLYfU$5k\CY[U/4=>QN0hkgI*XRN@&H=BR\[h7E]r)("RArGSQ+hEP\T<gj~>
endstream
endobj
88 0 obj
@ -514,10 +514,10 @@ endobj
>>
endobj
89 0 obj
<< /Length 1623 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1768 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gb"/'?#SIU'Rf_Z&H'rZL_Y>2#)31p3tE47+[`rW/:4$9asJHt%S=Y5If1\JZ7]_2ES;SOL7q.H9"JAbLPM5o0H^6I+$GTgCT7;^*).I.Z(`XErs*!2HP61[m9(E5Mp\`'&$Q!1Fr"8aVr#YhOjdDmlCVk&:/[R9SfKhB2(u)hYN-$t>8+41F1(0*RBT;*:F,(62qk?(]'>#]-"Jr<k`.!*9J`1>=02:YoEanN6rE&+flgjl]AJQ[R%$8$$mquSeOK>ORfClm_OoPu&IQ19G%W^?<T45DOUnr\KD&%ZDZNTP"C@'(N8cPlL1Y(u!86R"G2o%mZNlL*SG/C'jiS!_(YN+&!H]hNm#o"VjpPe0)r'MJ:1u_+cl'Ygp.JC<i7qqS/soHd?C97f!GX7fL!>J]n2Wo`2CBq!j,g1#E*>XkcQnHN?up<rinI%/gaAE53.<W<f%d1)U;/tqI*5VqQ'b/EbL;bdb,@^\RFbOMj;J%NW"],_$]/1E%;R;H,`J0*QRd)g?-Lj/NX<lQb%,_ups[=f[)4t?/hd8UI#YW]BiQ8c(UgtaE\`g@Qt)S:B+$7L(_7qi/\km&c@Lu\l>c=K)E$L4KB<)1Niu>@4m>H_f%oOCbSbpnL[VT(3T4pQl&"7.ehmquf)lG*J^cf$[XDqG590#CB).m6p!PI#\`7jro0:F#+A"Usq_,50bjcg54-BF<-X"21a.%;l.f[#7#@RU#l`98EHT_S7ENt]=*L'4lV.$:M3NC%Y779\3[-/6N9LB\=F5;E%C"Ggs^Z)XI(Pg$!7HV&ol:e[67Hq12;&Ls/Yhi\^"i?kuLa\I!,eUW#3)QEEcjg+-7.j7BW1(J6<4(uQG30iA7'5Y7*#5uDm3W(Y?MA_io(aF@p&.`jk%>TSRPQqGn.s4g<!HP>ElfO"'@=kF[3^+^rkK3FN;3t39uV$*J[b^g.\T;Vg!G":,qS\A6"R!&FK%n6Ub"2N=Gh4F3RJ6K$8LaO`[[2B$$Jg2CDanF)&?aSLCU6O+NlL^1lM^\OQ_?ab7^-MJuJ':Po'k<k3SLH0WHn0iA]:/q4/e(1rVeKSSD)8#0Q!j!0)Y$D]sCkb)1gA_g@'NA]$Gf0*Ne]'(QD[3(l<'AXXX^Z84S33da;<@R0b^J0t.7BIu2=Pq!<8]pi$RB]]<I%bS4*!PVL,d29\0UXk4@lcJ3N@.rCmb_<(^VAH=Wo!(AKp0Y1B+$><6H"5(ERt>MOB=+cmmZk'lE#[NoSWOVUY?8FS&GDK9B<N'DRnCcrb^Mmo9YK(7cD&,@bQfU]+H@6ck"PI1lg%ieWbqEN66(M5HMXf+9RjJicFk=ZL,lVU+WV#ShI`r[rc6np]ZEq\p87)VcWbVIc?m8A4V<sGHVrB"2^\QjUK2;Ss2MCrmEDmL4#lK$@b8(pDn:_dq__"?l,U%lN?cc\]!#"*og][4FO\%bPM%+OmK)>oP-*4jpYtiTG8&k6H5_Qo96&#G_YQkeDm.)bA'R^+P5(.0"'Y!+>TZr3et:P1l?;1+5mX[E_pFuETp5b\I!>&F,?sUr=frZZj.qfX-"5WNF.B0J*Oa]#!'o.p5I4U4&-l!CC)pq]pLFB4U?DeUC]VOX-rIjLh')MOXh!KI~>
Gb"/(997gc&AJ$C#ei,'X;/E#?OAV]?#6bp[TGJTD@a$@fgI.792]Q;^V5qKR7!D7HBB>*U'MC!'Sl;<2]mg=%u\i]Mp_.;G?4;'0;FruH;9VKo%tj5Io#'3jou;_c>rRSkeimV^RrQ,+36*nEo_@@T)@V1^6c:&ESr0Mj=G'/cZbFE=lS7s1C:XphG#=]WkkH<R&k1q^RW9H_bEJTNVZ?j(m:BD,I.sJ>:'9*k&Z9K_+ZrgKIW+8\HbTJ_n!h&nYVd3lh479k]LYFJ%6f@-kak'Y1JR_esItU&m%V`i"o.V5_U+0pZhJSZ.d``5VchW9?IU[i'&H!>c;(bFuZBMQ>P24c`1_uegT*W:EfE0TK.S'_X)Od\nUUna_CsqS.T/Z(5k$s:g`pUN>-%R>dKP!JE5_X$Xt)S?qY8o[qL%)/uGIVAP$#6IF8qc0qj:-/t2uO*a!C=jG8<qA?&c0N^+Af(t+&57[l$JO:>ZH/VaMJ,P2qPVqDY69VeYSLmc/6#o5M$e3koHB=U-=(d1?(Zb$6/>YV-gmfh0plehg%Pc+\mf*tEHV7t>fK1H%79i)BZ4$<=h$K6hb3GdQW7F`mp?j_T0.mc9DL,uu%K.Ou"4I^s]bW$0P%"&sH.7%GOaNqLQ_aa(!+5^8?FOZ^Fi;&7u1c0+720PO](US!C2J69ZHAZG!/Sk>DJ9uiNLG?%HNfeZS'Xhf"@6.!JZJ7*bCA3>*^-Kak$N]p#aPpGe;fm&mIBQ3:S2S;PMZq@p)A0fZHWho]P_dOu\N!iX-<ijQ"fJri%EG]H>]Pstq!j'NBW8Gj$/IYSrj2c8cY!L0&+>>-_#S$@-=[,?9pIH#f]%e+.<&G4nq#k0DMc:I+e,`D':9trB4*@t/mOL//Z*df+PgD2nP(R4)uV&(>)iMIN.R.Xn=*`(kXF0ToPCdMj7%/#7X-lp<-,'[^E^T;Nq-8HI)C-=CTjKqS5'g\r/YkG9Hlo@s!<n60HPq*jM7h`]3r;#Va]^:PB\K_f'1f(qbg#!g^C6e0*PTp\>aS/C3n/M_M'h8%ida#+bc?;Da.+S[?]8qJ#gcCi438bId`tjFEcZ-%P+KC')3;,X/UX'M\Qhtk0?9VKo4f5/YBc26XZGsh9nA/cgN0\qL!QQpJ8JiIcmkd';Vlp9og4uC3b38r*W'W-AkqA(CG7^;E-/Q%\+PM#$52[9p,`QChoU^X$8:jjEl7bi?n6l0nF<H&k#!A_s>^?_.*11LWtd8[_4.]"V@t)YbHE?F3YY9R%b.EqIM30)5"n9"DKGWn99FC?4krP'L$%FapK^iQlu`W9V>pUC'oTlh,Oon.GQWRJd\MW$<eM>JLoGLP`XueinV2GG%B`#RC;iU+ec`A$eFC5d9:9dG&H4S*L@bt=;(EE*aAi=N\X<].uF]n)hn56/.d@0`kohQq%WoaPY5&c]tM%ZlT!?5Cd/*TG"]VpOu,3C=CJT7n8tE_=AQW@gOZYRPE5)mEql?SrBYWD9UkBA^pX&"gU.[gP$'8Z%3=dfE/r-o(-bDE2AJ*irf@><_23gI-Zu1?b+AI'lNac6N^%Bdr"*pP!md`8!;h5CWJNMoL%mJ\5N5[;FA9p7UTX.E[dZBW^,L24nZhEK\O#$Er6)h;mi-,87;b.LRf&8LFCfn,4SHJ+[H"f30TBQ:rs!K0cB?gO,O)plf]sd<lks-3]38U2G)27pWCDI%-#.PGkedCRXrBUR;J_C'c,7b16E00>g'(qcV$<K.%`X;t5]ofJ5'$Z)cl3k4~>
endstream
endobj
90 0 obj
@ -529,10 +529,10 @@ endobj
>>
endobj
91 0 obj
<< /Length 1817 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1637 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
GatU4D/\/e&H;*)Tl3RXLCki[P(oVuS'FHWD!4o0q_2pg/^lU4=`j<L-i<Q]--JEnUn)Z7P)'ftpY>Y:hTnYUc:2/1fNZPVO/(2p^,MHRjPPclk8P$H1a/Po12eoDFnG[m"WR[o!QT,V`5@.R1L#k:p6I@]Y_W0F>pTN?plRMiH@K8g7)`poMJMZ-BSg#OQ%#kp9J:Q8HfCd!^hZtg^B87PeE%(LpAXVp57O$8(e$U:ci<Lj6QpA4d9PSkQ%SV,UmW1^9S3$^12se(7ELjG0Xt7Wepn38jK([oj.WpABduZX(AaG2)9Z/SCM&nP-Re3?m\(hY@OC4M=ZsM(llTrPAu/g,rA4qu-s>*][6q5Oc#\,t(JFJGWj1D`QV?Bi/`<IK_7<!5gP,/(np=eGCi:OE'rj2K=r@>6)\-5I5lM[I(OWn[0<R-KAXCps*D%tXHKjPr0-uD0qrg;6JLnTn"i0sjI9&)laIZL?c*%/Y!k?oa]('PMCl^Ogq`;q1L9Cln/U[h@=Z"k:<&IX*),U-Z_(ZQ4/MNW1b[b1l["/XfatJ::oHsY2SQumSf"!.EH`)iaGH:'Y>k'ZWS'0(^4;<$9D@?Ep6Hf1NPJ>b(O!:-FL.omNE&Lu%Xaar[T5SF\pQ\\33BY4OHsUn_N=nJm_NC[N/2bI!ATQ0]?J;FEUEiS3cr9rhN7EF;VhsT%CX;tM[uXisLC(sKcug3$7bteL`Z>W%6JR4[Y5&oC*-<$-'EW,H9D52j7E1Tu1eshB7qmcu'_d!mM7`a+CnisY0u8W[F?ZckOqC,WH/Zm\3No)c6A$p[eneo!!+*h]!2KNYqcm$LnWJf>%;>9X1B;"]6RT5,PXmJ!lCcq!=7eT;&eaj+?H?Onm^PaiKfsWkg8Ue"H)l0l!H)f3kf'+X(t:->el3jn5\g_PWFP[s.>@lN5mSZfO%/RE?K)7aABU]jXL.p+V*irHdZVfZQIg4pqYFD8h(Z\!;1!k"ORJ4+m?&.LH8EoJqZ`SjV:k-;G_7Z>^,p:P[Vr64-bf;Wj*__A88FmSgUY]Is#n+8;#4K_iq^!&)%2lee_YI[k-g%+1B'[KE^?%_mbWs=Qu9YQD::1>J*pn2[VQP(&mSkPRn\(<7oqe+>>ZAE\XbKs@/^&?mb:#gdJddtkp#oTAeGKjR",lr)D)Y(-;cC`dGHK?@PRJpr+pD)KmgSVH,aXXQ`F#7_e(u0_n.C:[f_=Q/eVkg4cNDu\oIF<Eq1E<NC(TmB'sa"Y_TSQ$Ua<h^"36f9IYl97\:l"1B/ZON[B*,BcbD!*!npjhpJal=X2ZL@%AA/D.")2n9%8]qs>\:Xe^]0%.#QJ]k>P;47N7LOn3[Z^J&Kn"Zs)@=XgH3qB-pT)`N0=[*Qq<;I/[T(q@[B<fMt4%g0\JF$UXP\K"ss%*-VE=Zm_I54+_2^'9Hg@)Y'6Og,29:16[012<IR<E?NfNWYh'VT-jR9P&Z^/o6i,Bf,NK>VcHlg-Up59kc"8@s/ABooD]ocPZm4**-gg4a,MU!XL3MkLm,qjL5q"%PoDPR8N$c(,`C0eJZs.WBj[fqSA%I"D&A_@%7?^LPMq>e%eUQ"G[*0`WZ\=@QCOJ+RKHJgW<L/`=g3>N@0:$p"3fV0XkqYeUnbgNLdf@eJmm,B?W:!)eCCQ+3SmhN&<d+L`1#/rD8-%Y_enE,\/Qf:_FjHJ"g_B+-(m,=:nS@78X]9&V8h/#smg/G_p7&K7bZB`U9ReUNLA>3,,^,5d4Pu^-C#G_a)a(JG.^iB<eK2LSEMi9P),Ue?ef+>5&?&>I;$nY\'/:%bVI'Yl=`)#BG(~>
GatU4gMZ%0&:O:SkcG0V0V[t:Jh2)/HBCX"UlDu>FUQP/jUft]d3VQe^8,;-6\Vnr(-uc#,%o10cWmD%bLo\7g%,VV)ufk[(>Hp=Jfs!DShGP"po]T=hmI36X_5Do^7=!:KKaFlo#l`*@>@'MAi%'VAb/-&@65*+I0TqaB.XMWO0cGRV6<Nq(H5>q[c^mr0CnJhi`Zh@^FS'S41G3rQJ\RV>`:d6(gfr%(Qol&jZ7]jfLF.7lg\o%rU6KY-q<nY8smYmZ%P">8QWfmXI@>ChO%N9\)_ij$/D@U*J>h=4OOTlB`<HfPoICV0:3a.6r$@bG7%A;X4epN#MZ7e/ZVK(;e9nsK,_A"+2E,CLn*-Q^8!N`67in;Z(*$%1qeIC/#fj$Fj#q#-T*jb"pi=E<i.FkBMHYQbcPTkUXTG?s7h;6D>aZ8.6:##qEU'3R81Q]*9$ttT)o>dWTaek\l5Pi<iHmf8C7[5dIgX:&i3!CU"_$tS"$jdBr^o6_c6drA=_0REl^M"Y(6XY^fQ[E1oE3Fp!37T?\[eN'aYStC;l=CX)js5T%"]_b;&`e#NMta9p`<nb6jEqE&/0D9H-7h9hdXn=p(@C(T^hbqOHT"+Ji^mV\?ImHq[)+!8'F`FF=Dihq5PQS'>qoq9_*H>P6SCYtPi^7@15GcTX+hAmu,jE<WTG9O:-rgs[(.2lh!e!#M0F+*"M.inR(Z\YAQq+]jb,5g-G`f$Mso!GXm]'*Zi]<4kIfnt[TG;oXG9+KM,o-/c,d'94_e:L<FIB/Iu;dA&D7f-G0-P7K>]YUA%Vr^W[DD"En+l&E_c-D7?$j!Ga?(Vo?t<98B+$P_[Eqm/(46Y/G3#GF`r+R@13V;+%n=\;310ja^"f1&UWfrcX8\Z`r\Do[uOeJl^0n@,UPclfr/KVG_;`ZDFNLAGp&>1c8lLHL/.[KR6MYqobrOUJh4W9JBDq[=gs7H.;#E]:4"k_4f3gbCbMW`kJLo'sF4*=jZ^A?)p]X>K$rS2HCqnA*L&kmH8^JB,;*^o6/65*cmj]]=J[$[]j&rQ\fWlQJCESF)mEVT5aiFY"\%L6"f!e*s$$T0k_U(Iu)6>eB8A%kGHm$\R(8bF1u_[m[b[R'$cZ"=M2;4<42Gn<?Bra'0<6UQlkRaIgU5Lur1CX2S?*qBrCM0:#!H%5pMd3g@(">&N&lM6Hb'@/V`a+`$$?5-as!YIuHh[6U9RZKnaPKG;RtGkZ6T[)INsc6e^'PX"^'>YmnB`XX9K%a*#Y+dU=oaBMu5FR\+a\H"G0f2^]bbf.U3n(c>4d6kkU7d[HhhX)A_FuXC`EN[jc?C:i[pH(OIESsFfBlTREC+dGW7*c#cnpRkV=IH0MV+Q6D#)Pdn+020Y"W$f@S$RdUJ<sDEJ]0!S`;PCa\*J(n=:t+3(%?#E3K\66]II>;*j#:PjZ0f:)29OL",C5O\qHC7e:f[Ch\7Ip@q(W8IF2)QR!;7\GhEQe;>0[@?UoNA]s=nhi@/U#(?NuP@Lkod*q]WgW;<_'m)J`qrclD*6I1F*VgKT*JT!ui/3*NJ^8\PZ2=I78X<'k0Gr\'S,KI4p3SIQ[@0!slNutF(h<e?#bUe!N:1_n]h_s7)L-`#T!FsjQ/3m.\WVcjb0%H9~>
endstream
endobj
92 0 obj
@ -544,10 +544,10 @@ endobj
>>
endobj
93 0 obj
<< /Length 1950 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 2091 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gb!#]>Ar7S'Roe[d+]0a@79BE/1oG2M[\j3/]M,Z%:Ca0(++s>,j4ae7nT(h.$8]$G$>Qc0kp1uINQmq-Z9d,pP[_&)7J,UhhP?F,MVd;?i9A=o(0C^6KEHQ<ANlXp?V(id1)*X+9Ij9bWQ=In]`8Gg!)-Rr*ZS]kn3"2Y-lncJ"413X4tD<im^":NMr&(X`t^b_aM1H_]\*Bh3R0q0U(t6GH;e(G:_]!fYQuYq(rMVZE(WMjZUk]N^G;EI/(?^j`Xt9:DGpurj3G*O.\KFE][r9afqn?:Dp&A(57O&du*!n/08C+#Qk7im+^uZTHn)OI&8G!kLK'+PcPMmN"i0]_Fg,t^g'e.m:a$74Rj$J^^DI_.Gk.PK('f0PH*iMfVb:iNeXML0Zim8QcKQ/KoR0aGL4)?_`d#ZS'!0d08`TYiMCf^Ta;\KM!)%2hja<nf.:8rDhQlG>'P3!EQ@=$enMiVi6Om0G[aiIX*VX@(3o$RZ"k=o'Tk)r]I^_Nn1(P=Slak(AB7Vn<ER!bq4fR$*q`YhE/&mClkSgmHZ:/C+Q3M1;o4s#)*7ggIXMFj<!?LVkrm4]+>8V6%R?kElJ"[N;@L6L)3p?c$uYX7R[1@QT0>&u5]qiDH\S,kcV)"0d<`:P1MXL.2])T0#R?f``#Ds-%YH*TUt\5HN"j&C%PL[d3h?@21mCZ")ZtU-_+RXCW.*Y<-E(Zn.?h\bE+)R[XE\(#1cIo5m(k=`L`?g4icoH@l)tM]')`e49Tk]<r%nW=FN_ZqB$,WCJYH[[/jTlZH`Ec!RafQG47Cf_buGgGpd2Zf[>B!4:V+R,)ukO3Ug/k8J.NDAPmE>44mIHD*]ki6,?T^$Fn$=%1JM9rn"4;$!N8rr)cO6a@e.!9$6CU#^3NZ(SDOX.BQh'nYoLh0#RF*m9]WH\@B)Y8DU.p?@U#6kr>@H:pN-T,7b5u)BLRKr%>@r-]?BKHJ-l+/SqZ,97B5VT)VssiDFhl1n\D&1P+s?dYQ4W5\!;\m4VDisI/KAobEJ"ZEEK)49!1]WI7-[_cHG<@`n/BdRpIoRQoUM2m>i\]*&:T*_E?+tL-_)n.5j\l)]Lp.)a#U;^Qp8!Wi5AP#`p`k><g3?J=<Q!isuPf`hNe,4uW_V['a6&KV0*,hj-JkkURRR'tE2sMD8dG'<lf/QZeq[WL'5A'=A;J&KDtD64Z>lFK!XQrV]hijco8^(XsBX_H7h)l7c+j7$U/>;54Im?^GKrHB[@P"m+J*+l9JTGG=O*@Y,nA#C4[hG!SRh"_F`(Z3)S8`V;`H,RVA06o.*iXsd[s)b9I#>%?G'Vi>K_0Q/ma(,p\f_p_j^i?`7Y1fMg[LbU;,`1pPN<(0r`Ct3#S4H:A&k86*o9F[Tle')35,9?8j_oMV\AdcmgN%'oc<t-$*k`:+_IW0ps/WZ^D8(cEZo4<gU9[^;jD%oI5m9&qgMm<3IfhlEcjk;J>q:c.TD6=QB@L9[/3d-Xf1ug7OJL)7qZFd9D:BDUAR'c2ba9<o41MVnC)u@J`;;_Yl<X0RJqPmH>6]QpZ*fl3L&X4,)X(Lgj`jD=<Y^HkZRGukOJfUdbq.d<iJIV$o8F$`W__*S,"!)AdKKIgl_eRi:CsXh2LRIaT&>jnU)L^NO3WHOiBJhii"[kN(:kD8u:.8N"9GQIkUS%kb>iG-R<n"C'"8=ZT@8momaQqAuh,].@fGB"mBp$7(Pub72%G%Htd%K=kX.JX83O(p3:+SL=cA"p?b5A4Mk1*^Y&>1H@M$;aErRAA5]#nOVW6BA'1/.1X95DL.jWDcO9QhOrAac!E,+`o"UiT214`f\Y0ShcEf;^SDM0m1nDOjs!+VM:+phLlcSfRWu'adS]HV32dii,W'S&TAuYVC:[iF,,jgE/o^GgHp#Fj9]I-hYBkce$IdL#p?S?5Wh!g.J"Y1qG1kAsK=4=U_%n5:Vci<W~>
Gb!#]?#SIU'Rf_Zd+]0a;+,i55R+;3/lJKt[i4#=]E,B-gVdXMV;sVhY9,&nk+tK":=Br3O]\EubBj`f4hR"c:IjlNUX(/q/]HWE;<$5g'o&FWmg&OYFl<(=*-`5HepR,Ukk40@NYhb6^DR7lElB]\`Q2a_3T>[mG%BObU#TA+[Ir_%Pjn@5PL#TH[G0?F+@X6u.:ibH6oGn.#Uc&,Qp8].>,7\HOqp^DQa(YE(,f`"Z!GZ^[kFkuXQbM3CEBe[cF(,4I7s,<"'_Q6ma;/_2eUZ5[]1'&4Z.ajA;7>s_9%uWl'!^rmL"6Q_7ZN,2TP_fG'4qa\6<i"[bVE5/PT!h`Bl7`UnDX$WZ<5d*!+*p"QtN'#KjL$Z8N@n+C"jYcEqP]^ihcK]9V&t,8CU/bNbI)'\EOsd`$;g]*,2/LZ60)\=aclbH=SDL1+UY[#l@SaGr>?T&U0'+N5/n,'"8H4,,1iLoc/#qYOPql2?V^.2HF'mc4t`2`WVgG$"n*/fYAZek%Gt]+n%+7^Fr-EeRng))),HnPRlu)7+i,@&Tgc>#dB^"HeqEj3s,qSSuQW3E/U[>"4h@Co=4Dfe+fihM&OEjX[IB?77Z8"^l=ZE:%@p,*8uP-9qGr2_^Oq+oOmX3iO!<,$#eE&57'T[e4/4']BQ^LNAuTrJ,(8#I+Aomg]o>_,!)/R;'"NBSkn4h!L=;<[ao!p]hZ&PiOt>;00ItY>F4Vi-Wb`Yi'?R]6stAO(4To6b+"dD[d#Lbhki7g^h_DOqdpLOnp*MNrk`]?]/@acM<I!eDLdARJi#s%H?d=D?h/R[*IHj@L9@Q%PK?0<.qX"BSn</cP6;&cPVm)ogTAas#i#=i-llXX"Q>f8LQFAPEuS+Bgl0%Jn:u/8**`ZIDX[4:.A%BX;=FDg?^3<'D?5cXm>j34j4hiR-*H8SGMD!Lo9$i[%\'VLR#/[U][:G?[W`\7[-1$,fd<(!/$[pWY4F'Sf^ZIU4V[#]/jGn@F!JP^W"`HX_6.j^.ol+$-9sP:+lg,L(j4r"gCG4f;NW$^jm@.at3s[!`7L*0HcV(g9CYl=H,.WP.]6&MEnS3ZD,4C0Im>7i"$#[*eFE4\i;/39*S%G6f4#9a\W'H]I*SO7`.IsMNWG!"=&0j<%5lX77BmskF=);U%3FP(TJ4Q9VdGidPW3*j<)U=iIOopJ.E!N;@'&fOSTT5<-Um:SdVF=qT-qjKq[@q)%%F`;nbsS9Va"b&#%p5_WAB?"c`QAJ8nDlqe\q4?r'%?8lK_qM*"C2W&U1ap\IdEXnoDm_pEddiPAY[<Mp\d>[b.CY.&[F2fQYu1!`h6Li+2(6,RKH/IYjhc901IZ^qfF!q_iH\dl[UStTA6q2lH!Mi[X1hPT+ngrPh>[)a-FZ\t>*BAIHOdkp".3P0)l-@n,lY/UqB1`%&7<GFAC6M8`@K1?+:p[$mG2>&2UJ/Tb*lWmd4?rId&Ce(-RSohKSDB-0/ZruPfTGDk*Q9VSF'ob*YI,M^O%,/=!otdg-O<!%'Zs-]2i_7i3bX^SXXG-(nEp6]&Fia;U-'cO0TeEEHTS8K]%+Jm`1gSXpX.&<RB_o;6K@_+X8'`U84rrZ()0gAUnLl6!B'guW)Uo7qLgEod%P=FY<#F*`JbXm*_<hNkJBI7k"3%CGj?I@2I8?A6f4nsTXD"1_Gr]<?:pt=<g'9H%dPZhj:f<*:GC<@(AVDm=O54b7DU;Z6_ttQfl1[\(6_O,7J7fhPT2-E7^u%kYU.Y;8#)2"2aHt-^<`"Y7Si(`uq`8Cs2UPq\C#5LDG+?L2B@Ym'6M5Q\,dCqNV.KYr\+`r#c5$/3m<7r'O`*>(45-0DA0K9:?Fm0'H\m`DgThpS4p[iRn'T5$fX52C+..k;@H8c;M4Z`$ctf@+MX2Sq>mo/PLTYEhF]W,7;<d>:dDug;f=3:F^9$E;\J*XnY_VZ!DG]A"FL[IakJXjiB^bKp0@e20W&l+k:<T)6>a/UsG[ZS`^eVb^ML>LI[C4qrbnI"u<MPLM>gkZR"!h*Rj+O_G=1bmAW,(+ZfV7*eT4tYq@o9^I\6]gf$u8kHD)?=#TM8$UF-$eug4.P5ZI/~>
endstream
endobj
94 0 obj
@ -559,10 +559,10 @@ endobj
>>
endobj
95 0 obj
<< /Length 1757 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1690 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gatm<>Ar7S'Roe[cs)[LAjfoUW@.s%leH(8MQS\FLUn#'7+h]RdA<Y7afce/ZOUP%;1g#_@T)4E3T'YXbr=tA+7%VS)7F_Zp\cYKO_l\:9DR32ld#sr$..+E/O?N+m%;tUM[*K+UEt0`HKAA`O\aQmb\g2/ncrCd8`)g,gU#H9lg*X[nsFE[I%^;<gg=1lV\+0a=St32^>.l4Q1`C*s"tGM@cWLO]d"gXLtOtXFEF'C-FT&cb?Rtcpr!P@17f;7+<p$Ri$+kH-LdJ_F"%8$X3G4dVl#4X>egiiN5_lg%pQ$ki+5=e.cK'FATW!IFLM!RK^9@YkB>7c'TBcK:mT.RP(Mf-@5$SZFeH/.qOP8[CoK)io(%@c:lSb3O2BbV*kQ.5V;hA/k^3n[[Y*ugbok\A!W9pbiUa<#SI*r%'kEe"A5b)h$J1fY:qTN/A8ZWP@;.2sQH*BO9?RQ4o?dl(_&eE7@qRJ<6Ya+eY(TVVaQ%FO.`'.!Ri+K^`:31ld!Nd5iq]W5QOq=f.eq(V)Uk6r8-.gY$-^8Z9Zfcn;as,G^"!SdqT[dZ"JY_lae)gr$22\D8&P@BB*(3ER:t[/cOcf5l\f^s%$d+EPY7T<=jb<X'*'JEF/2UPojn[U01mkG04]!mYaCYIc;fD:7aef4mTp_?WD*Q)<=2i5:A'oVgOMLFpFh.Be#X[5kmnu%3AmWl6ZlGN:J<6$lraajW<3pUQSHj4,p9([+q@O^Y"<GN/HW5fS.^k!.^L$EKj#m[EgBMM&:d\YgOamX'H[CI:T3e5K,jqLU6K6e6"+@U*[Ujh*G+1iAX^4%!t]9*RBWldS#\JOEB"lrf`S,Hi3T=CksjW7S#UXu0KK`O,`2)V'EsO8.:LAD@#AT'(IG]t2E?8#/IhY%!iO(`YECW>_'Z6V<n'NN?*;UWbKeVh"-IZl1uSYa&;=c=<jbW%dBKl7`be!OcPUL]G'8Mc9@"Bk`oq$!ZV_Qn[VOFn4cJ_,^)_on-$GUMiTKr948cX0K_,'!%suAuL;iIY?ssj/e"6uPk9(R&/]7\\JIatcE?H-G,&3a\,-gc.!EE3u:tPB%lNJY9U3\6<c_7?cNf>5)GJPlp_Emr+-nTf!Xh[,W5l/:+XCh`)m@aY`(`<>GLXN!KXd:IoJG\W:Fj^_I^.*u#>,p<X$@;#8qUJn?hgR<5'_As_/;M-f"=<1DCao+?`j.SnK^(;,-Wt"CW2/B%/"%@W+jdM7EZGK^j<;9.6&j<[$PVZL"tL>49j0FBeWqI.E08o4>Ou1#E4n=k=2V3329+Ai#pNL/dG5gide_XZ@>arOj@;ST5dpT^:1Si;i^7m^g`-bDF-JS5A=4?.KcVHT`92&AFq0_Kf*G7Fp7IQp<6X5X$i$qeXD6W,s2p/`jD#C5UEN#p\%gK7=E`E4,.#hI+Sk$W`Ve`%r;HM%re66(8W4ReZ@>'cl1CQJi_:)8)A1hHi/(JhV4r6M5f:g-&S2@TKmD0eRj[I3&-*UB@uEgomJ+:HTM3tBADhg7"n'RlouaO+=i<R:n8-bYKYA2Bie<r%,MuA\[mn+T5Z+b-XNM,S9QP>lTr`.t>d*Lq]"f.@)DdYq<e"!#\t_jC>tqYfj4>"tm40(rX6nXbOt'ZkSV9AZpT$CK5.h#AA%O=LZEQEAkCYF$Z1"H$l!'i/&a0Mj4$Pn9[bE`9$,S%g*$5\Aa0q>PC>GT6/-("@qMqK86T8&T4"FY!#s#E%h6Ak2[#=)7!j$*%`B?p\6h;CY5CBF~>
Gatm<>Ar7S'Roe[d+a`"AjfeOV&3Aec!7%mL28U<;YPa..%>`<3g:=+q=UL.b<!#P`9E#lYt=6MgqIjW\Xnl]R#`:XfNG3"QVm)nf*R$orV&nTHU>@V"_?mhjt'#PNNV.an@])ri:3icENl?9k1RgmWEf+OSW8kX-uaTI^2^kobh)40R<\*V(MG=kL/l67E0c<SU:_n0SI1PB]WAo:aG/SV;=]SPSWm0mMU>rDcC!C;Mism%J.g;tGdZ\QYYIVbDcfl%kgK:cbeG3u4iPM2e\77,Br:.(BXM2P%Qflra'nMm>`!UTCB>!7rQh2j.O.aU,4AoSG_;!8M8bHFAs,<E(m$.8mQmgn0>Cq@7E9WE3>XON"J2j'#so=HfQ;49-Je2N<ii=oVk2'@Xlr:8eUB3F;%q<ni1F5[XbfPuJ"[GB]u]jOVZH<>f.<OEG/Wh@:-Y0WI^/s)8cV@);^PWSg"8W%Tgti+a8lCWg#bEUeJQ67']<Ee*`PGZ-5e?eNIg0r(kCXG0,9V4Z_(-[(Pu\0jMgC@b(lO^hCP)%)WYae7T"eEIP`1R\(u>,bN-QdVGi?.IfoV]"TIC7YBHSF))gS&?09M>R?/28)pJ5SH(3p[f^a"(ZV7p28AO)F<BJ3X,79(?(rTH^(sHPYj/Iode6LYWKd.c90n27dBTZJih+IC^7:!XQC2j8kq8/peEfr_2(Cqr['s:HTT2Cu7bOSq2_jbtS>O;$P\*!jL(&oY1$Bn2];0\Nj>5V`QB:.k0,[,3[*[(*"IFF\j772RG-*LNUPK91+!%?8o9-;7"AIt#kPu-]]U:3[=67XYNlE/pjD8HSQB`Qj=h6=ru"758YpQf.?J3OJ?IF*;b+EH-K<QUcad[R'oCZ)*KO`kLMHeFVNeJdYGQBeiaL+t:f6XguR/YJ!E+lR+5ZAZOT."t,inDZP:JT8hY=gEfdo-'4k?o:gEYQJ96/*4=oBuoYDLC(`t7m%eI:"/n='.aQ[YcjXVb7CK9]GS:Kq*5YrIeuUW&>->/H?T[jF2KJrC8\Ei7D`aB;b$@&oUeLlPg[k&+%Asr1dp9t1#@h!9kpm=gF"L`"10LYRB,FUeXncg+cL=QSkk(h4@uumAk%W3NO:W]9=fC;Be.jC#HO1t/V^CUm=`>F;&m,FVtp$@DSCrH7uefqNrAJ902=ITnC/tYf?Z9QVs%kOOK2OJW$lP=)S1b[5o=s>M*j[]&-!>a6Z`5nofdFd'BP&`MQRCi4?#[\%sq.TqfG7?"NOFT'&8NbfYg/P"/mbdeZ?PEM*gjMYgc/r>3XFi,ms\=8aFcmcHDLk!5IZuodgT$\ji,0-D]\C7ASNVA`)<8r8UkJ:Am.dr%9/4BBX@m>:L8J1:,4IBjP!M[4GSG'j'jRd*Sd2$DS>f&5@L%kWtXCBs[W(CX]W,-<9A<@n"7`J8iiDJ\>+BH?\$)>M%#R@"$@%p\^l^o@;'+,32,,qlY^eLcD(88aOD:hqtocD68h,LuL,XS;V<._?t^,U9/;P#\uO#?uFV1U:AXg[@(cQHE0jOaZ`U&b=A!dc2']Ea'o]@6:tc9p/)ODI2-)Qr,;Z.`blUW.U@=n:V\e%'8#4o30CBqYdaOCZ$hpc[;LC.7]l>HJqoWS]"tW)O'fL&Y[4rU(Kn0#^FXIg1Jnr;4V[6G=e6.^Q?NYdXCUmaV'&M4$T`BKh>~>
endstream
endobj
96 0 obj
@ -574,10 +574,10 @@ endobj
>>
endobj
97 0 obj
<< /Length 1845 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1852 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gb!#\gMZ%0&:O:SkcGdHAjhNfm1oj`?$kO!S"tn1(9!-X[O>@1/:'b<8c&+VAB>h6U?r"fTElO,'JF+EbT%+83us9WK<GAn/`l0e+LI8JH(HD`;n)TdFI`tgY%&3jRLaibgn('%-q+.RIPZ.!9]VCO19g(I[f"%oI\oc"B2`TW[f1M1MAE6C\NmjR]D5[Q</]iG*]'KN,5!b6GZ:R9;Y=jNR[:%bMm=p%WdUA&?mU.j<+Ec91.lW<;6Bm+B/i]']'Cat;^%lN!%X+]$/.Sg0Y"Ff[&.1k'M*gi9>Tl[?%%(n]&>Zm9@UR)%M3?@SZ\=[lU1p*/DdY+A,phqTg@u<A2G4(QB7>DXQ,$Fl!POI7)H9h^PMUnm[Gpp7#5`5D'LuQCr#6ggcZR9m@YM]Okn_R8IR08JX$:AJ$X_/?Z%aHoO/cHd5b.\^d1_2$9<#s2*P\J\X3)LQW,X=:9`/ST1G<Z(5&X'9q6<Q#6UX)p;:&)U]TK*4Qce;jS2+!7kof_)YCqoi3nbda?.XdjU/R9L>aihhhgAVcJ)(r'XkI!$Z)^`iRcJs!2pX!$g:]hbLM[j0e7e.Qt5E&$pY##"<TZ]T:s0=aD^<^0;-3+8uTs2/Hqnb,/O;MNTSm^<EBt=V>1D'EYB0"e<c[ACTr;W)74$RYXfGdQPD<I"8\Iocl.julHNX6Tc]JM/_>:a_1Q1@j"s>1ge.VVIAK`SBi*o]9LXe^lujk"h^/+3GGjH=p#Q<PT)cE;Ohu27htk7sf>0'H4.@I7$ErJdj>k/;\+0-6J'b</!H=b=3:6Ws(VT5DW7'F@"gu'f=20eZ7g[^5_1i*OpM/^:"hY10C`Pr+ca1*@'rjN%Dtk+5HP<l_+#O4J+qYd>LhP(Xls!UKlXXoJZ`dRnA9MOgER)bX.>p(I3R<$]-_\tao;'oJ]T%LsYT9e@VANClXrR_4<@f`n_oL*S^@8%DLe(bF@u>*iU2Vp^/aW*L&Pa/eN6*-?b\^1F:DkGVR4V$;#`)TpGW6j9R/2T=LM+>IkQ#uPq(?QBj.7h'D;3<i(Ed(lC'+0tDp"=-;$LOA=-"8J<9:(S;']$?aI/8H7WS=OFQh+Ii1@&H*R@4-_&&HleTgi(+Om;bI'M3tPt<%'=+CsgMoskV%^heTc$&Z@jL![$i<i8j1rdf&:g1:':IsY(;[pg36n:Nu%\$(nprLT;.Y3drQ"_tsG:eM1Np`=)<-.r(+L9F5RF$?9).uk^[c:P`F?EE=oEmkoLHg;oI/a)4'gbC_p[&"m>bkAe?]Q]>g(p&s[p@1^U)+H)G8c`hLW'_7:E[2(=O5:XP\medO\'OEd.mV1kE"*,Sk"i;PP[FN'\]1f$^LlZ&k!0s'9B'tn(M]qr2Ttf;2Fj(U^R9(Ko;N/-<q7;<.Jl]-D1tR.&2q:,/)b]aTXV/6Dt7T1J6)XNXfLrM*PmO&n.es61_NR>T8:V!c<M]_KJ(tf+Y;a-iG=eldJPMGlK>nl;B89mKY0l)9VdelS0IdTh;n%kAX)c7!mIZ&Lu*^=Hd$D\:3tf$7N,*"mnig"f5<kH5p9/QnuVthFE@iWN<8/7qc(7$mY,M.d`6@o2tHC;YmAX;Q5c<DD]i`o"e11S(cjgM8]ruVSf;2)i;6G'.Z??82P]s-Xm]HpZ[[]]6XK\VM2`Si0oG(U"28pCknU%F4F;3FaRoBAG&-thB1KP6.j\L2Bm$=74)jdM0gt4ZIndga:HZrh[KCt@VkLe:DV<qV_33#?:@lX\X'n"@FD[Q8%s7!dcIM&N??[<+>-\H@b[Jh23PLg!:&kC'2Qc;k,u#Y)-=;!3mA5'Q=['nmk$eo$!-EBHN;1C5:N&$!<~>
Gatm<>Ar7S'Roe[cs'ZrAjfPHAISj>>MQ&WP:Z7P!R!1<2J!8X79>^pl)u0T9B)Whg)a')WHBKWn2n+?L\s+P>u!oI7bCkD^JCWG7BY\J^A?_seao0U&:'3q"a=RR5=Q_*:7qVb5C3+XKK6Pg2,-PX4j-k*Yd9h@#kD9IRBE*qh[tP!4(D0hGo,WO+O%b;s%1D_'ef?="9\:k9_2i8[>aZcXed:shrZ+9j"sgnJ>t0L68"/'<"&`d9L&95;iJ@)q*gTT=!RCVa*QR^p\N-Zr3I1(dD,3r_2He6>(@^(lUf7*L&@C*X%)OX!:1oX!pHb(XEFO4[H'TekKEbp4!2nm_?d],c5fB4k*,+pduuldpZ:K.JJU'Vq4LbQ5[g_$)`O_#Ji]jIo6$W5!aEXZISW2RPX]qujXc^oQ:.I>T3(9)V8X/4.p!j-'"*)qF?K4J;?PiVW2N<Q!+rqs$D*U;.rY__K+9"tVqYbkJ^/#&[1^gK@_mgm,KW>Y)Tf]*Bb!joZRnZi*,,^9\RY3-F=]VMj&l`]Y2d/gV2Tui7X`UAFm(e)FFdn`0&cOXcckMHb=:R8c\%h^,-5il&W6d<ikcrK($^qagnfN*l0#+jb=aoAeR@Z%*U&(=,-jm)G6E"G_&\f02?X`AYd'lp+NV4_E9r>(poYgE&rRRN`.D_$SJM@GEbkHo*7cnb*AiKG#`%5rE7bj/R,LNn0:l)KCJ=<I@\6>\\WMN^]O2Mi)*=2cQ*h\DR]!UCW&9VW@31DMh8]nbHX8Z?_gpKR^8:7V^mRk2>Nr?^!8MqU2%JA90FD"<]d%>@T]"Gl`WE(bQQt:j8scT`.#a.<g1Y/L%R:Q3#X7n9I)$c*LKNjPG(_q^VMWHeeVZ'W-5-7:Cp`C)+M6&50[0d&b"Z-ZOC,"gbLWT-`1.W+2G!Ug`GPL&WO!pTMt)nBq+E3^D,Trdg8lL.0s&><9iA\BG!Ou#IemeE9r]PD$bQEslrDNPLoKB[?_4Je:%6C6d!%'>\.AVsQrr<FbYZen*6EVR.J8cuM&gLJldOgg>H7s9:D9rspiVcY&2*(]Nc3)UjSCoB__J%&-E>gPO4?f+S6#'=n>iqWesgIl3U@N@<c]nE)mP+phW1dY7Z@G@*.R\YU"2@"W`SlchmR8*4?+3:c:#4sB<_J#+$P`^(Mcc,m'QW%l_6'??0YD(*lc_`F/.*%MPXh_Gk]>5SWHE6:j57G*cd5Rp":U00HJdi8:XQjaUY;MD:"_I3HY*"T9l:6&@$4A-U't](N]p^_KqM(\d:W]B+-`Dn\C='D;3<)'VJckD?@aYCeAhTMDLXKK7)2<UM`ThT443\i1-h\k")X=g,;*tQad:t;aLeJ-pG5m-<.D8SHg<&r@6r6F'#op(]4Hp"PuT/du+O??dm8?$3b=6Rm7G<3&E7V`?DW-rL['*jgH4,,`0p!-m#h)>`1OA0&dKHq6;\l!UJPiiEi/^jsJX6!DhWEFkWrFN7djD"T\taEG\BqOf0(BZ/4V:+9$#X29oD0[mHrlR\/.i3NZgunu/hlS"0bH3n*;ml]bUS^OjaZK`17I?*Xnugsm2fBQA1gj^S)T_r]_Qe]ftV'(,/B3R9u2$1_nmVG(WIhgPsq58@khK1OmsPU3$'F>5;>_f[j\:8iuN?aG;-=%o?c$/MOHW:d.*7tFI/Xkg%_+a*%r]pU_UWI@_%$5j7Wh0.B8A%,(8L4!8p=@Knp9>,Q]F6'#KBHZ3P962g-!/n+oiuPPnOMGAaMA<It,@aL4.J'#/r?=/'KfTFgXq&>&q1N0.&_H]`e1fHK!3WBaH]aQ=bm(q01T@h2\-b>"drm[M4/#tb!gu\f4R)!2H1!mBSj`h~>
endstream
endobj
98 0 obj
@ -589,10 +589,10 @@ endobj
>>
endobj
99 0 obj
<< /Length 1472 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1313 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gat%$=`<%S&:XAW&G"6RL`'&&FY&Ca9t+H)8<[j6pgpc[A=Kk-aJ,`QNrOO,_racBh&&G0N&7(#jrX%N*.82b^IHjXo&"pdJVd(<F^>!u+jL$s@f+5*$>RdSs,M!CB"soK"'b[t(luo7Pf\EZJPEm>8hB%lNY/FQs#rWrX%>pi7<rU+X]F/rnb>Qbe%<9(2#]GNmmAN^7a>=4.tW]-8bo`+pmYkq"C;*$.NR/0ekejei`rk/&!/53H=CGhV(=h?H-`>+4iV5bY(qD$^6A'T=h[kYF'K<`0VRJ7W\t5N`G#P5V80UPUk#XXir<#c('LZg^]bIlK/BQj#d>Z'#4P[#=:&_0k7oM_d?9\)9p>`hS'Z`s0W7*CgEQZ$Cr#I$B#Q67goY`hRVRlChMc#aU0d`i"<VAnO//mGJdM#G*f)E#aUrk1*4mDoV,0ot.p2<_>dA;r+<o.L"PPE)`gl-jPih'B@"8Su1f"=\f7tD\M,#G70F+bF-aU/_nl`%#<hfX5^gEsmg+>K(jVSg+=&&:38kUT:JL%[AE[T#8*I,*^.p/"E/<204%?aO$/(YIm4c'&P)tDNQ9qqulP:8(U0)?7M7,bolqc7e:(rmr)^mg("_Taakh1?Y/&7m3<C2gTJ=4gnJU<u-k3l*V3(:6>ja6+OO_Oo:qcS'L'%i2NQb/O7(:3-tj$TS["$8(s?D='K-m]<Vp:Xmb,QPs#9]1UnUClaCj4*Y*kEZ0[o^8W<R#]KMV4RD!kTB3l^p8`]U#7Qi'Y7B/_60%V)T'"3G!BOTQQp8;^bD5I&Sj,:T]Sdgtg1f&mGc?R"1Hqo@K=9FEoNWZ-)i.VqEIDN=g^$n0H&aOEd)YU>Zn!$S+,!jinG`TkB_e!`&$B$<Q:8:rN0SfZW1UK^/p22bS.BSqM*_)@dMt;2(8PAZgi4Jf)k`@k^20h"/B\m]FZ4d!N>_W%#kYn[TnL.CMIt\A\MR=E-;u6gVt*[=E1!V?6uj8Xcg0,?9$VsIi*M<s0^Q@4B[.WJe-e=XG[M*%"^>\_Er.aDX0o4f-7N1<<](kP1Ki(L:%O'V,98<I+5lEDkQ%TH*"+4_JA`cje@$#e-V)q1LJj/?'YG@0gf1bYW1>g\%%IS2i*s]HHD+J$NmOfTL<J4T>Rc^m4Q/8o'>,P/\*Q8fNoC5)m4L4!BS1K1BhI)/)h(uk`eF?X`HOQoj^?P<9.@jlmYXJ1lP:m[H+"YWd.h+5nhm]1Rd&cu_(pE!_!_EHj<MtuL50W!Xe9aQQSm[A>="j9!me:4??CEKQDclO@W0\Y8cKAF*jdM?h\Y!nL??LKrNa8:LHO!l1@R"aROa,;J&QqnYR-@2LMR)'o"?+;;m_e^@3=-])o9c>"nF7k+n2@\&/;Xmn/^otgr]E8f"3,3->g#4I_/T`":N@-9ik/>GO5[o]p=+UEOG3*VL((,FhZLHl\AtQ-AY]H$#!Mj@O$U5GM[8Y9LF=~>
Gb!#[gMYb*&:O:SkV3*QQD?o?#)X,>VI!eqD6f#L0?mt"g6=2)Q<]QpjmSPI:`RW@';I4=^k%7^cC>ctS09P\ak(4sF$cD)qYWOF@>#ZYpXObS<Ss^uV9!]bmeiIM27YhPrSsD.KW8CRA:j,Q=T&hei3:qX..XY+6p'T/ff\BNeQje*RHE251SqLuZg%,!#_-2odeA^uGa9_6i6n^&@&r(%,9ZfH(b&%cr:0^?q8\?k:`r=7;?L`%F$2>f>C7--lKgdLNQ5VOe+#iQqc1?]G<>smo>8AQZE\+N4o</beC[QNfsJsZ)5int!:2re4C2oaZteQI(eIb/mXB,Z41A+cc3a?(c.?e^jR=eFHt1%7c`%'*,VO>YiF@JD%f_A=,n)KPOrRlPXFt3UG5HS0idID1$4;;4o4.=C:l8OpfC:p1oC4B3_q4IR%<EX4/.>?gFcWInqX$%IWaiZL406ZH\fROoWs>E=eO$*$"o%7c\XW>oTjCP[*3ADU(;Q6a;uX`i8e3gYkB$jW[2jhQcY/M4ER3Y[0@9Xjrm*7F^Pf;i&(e;JW0K8C<&`p%BAfj*\TP"$6VB^\G'Or+];X*e=i%8?n1KnL<^(5O/&%:RSQ5'+^kW_h?J0!*VkWj34@7rG+`"2Wgf#upc^gJ]34(LHs!H(C_tHV,l.#.C*[-/*#/QRV"$O^9]6Fj"V$+b_PR6Fu#-+0f$0Y!>(`NAU>D3\5)N+Zh2n\'3$ptoKbgbO^=A;&7GE6)&f&Urg\%-hU\o1AN(NQ3lP]JpDhUijbeM@^2dN6#DBq.)7Hhm%N4n'(+07nW^r6fDa*!lFJO:KSYQart6PCifcH+$DWYY#I/b4ep'<0J.MmLLpt<o'eA*>`b##Y304>sPC.&\Z#H*#WR60\E++*]2Dp]WH+Bi0R&WY/2"DVI5a@Mi;ZQ594StS=/6^R5oCj:d6d"eB?s0Xijd+bYlpu^s,,WIr+1F$5%b*3jF7r'q`<O"&F<Q&%7II#T36N5\^1WiOUOaP.o`@Cb7T]k<kZ9jbmREOIkG1;br%!XuXqMj+rNs=em;&PPF)/VGhdr&s&EdI.R22RCP#Sg7^d&:d$kU9^o81JWMkmE=nnN`qYn6@c+cTK)j-tT>DZ5QDTsiSZV[F[5)Y,h^m9)V!F_'*`im.HAW:STRaFg@uMh:?H.hN)3[7#VD=kg4#7D$6&YRl?JGHM#i[2@)28u^2"2?0?o*OL5''%dAUlVGI`qJh7Cq@*,oM@nK2>$r6o"Cpe;hpG!84F7LHL^FE<0,&H'tck1O%T^>9hY3"d$:Q`=lT%PJ$nqSmh0s~>
endstream
endobj
100 0 obj
@ -604,10 +604,10 @@ endobj
>>
endobj
101 0 obj
<< /Length 1726 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1805 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
Gatm<D/\/e&H;*)ToTLl1F2X"$j2f=%Pe`6[s6Y_\fVWUZ@tlE-;fe%m)P0OjR=QrDMAWiPE7-[T"B@;Kt^j;ITk"qSe5T_s52(1N3EruJc+d.lL0Ff6UZ@SWL<?;qfR)ZeI.t0!c\!JkrXR@%p%p'GF,Q38*$8Q'DMS8Yii(pkL1/AYFPjaUg/a>du-q)f;o[4UZ+up11'7:[-#?Gm6#*a=$JdIT/@T..6*oW]G$emV7sT48:U.sRPmhr>#J6#-&ph?gO]sP_(Ei!U6ht:n1?d"a*fBTUt7,Q50[gJ?_,`JXI6<'&P4R#`:KrQCr=H`fF<7.>`@mg%?ihT\a;8I5tFKSN1r;KY+pGmKW*clN[)TDGL#i9^YLmPQ"1lI':"B/YX,slA`s]7-drBBGKl0nj:^/k%T*MEq2XQ15^1!'S$nE#C*7>FcbB>AHJo7,>r"uc'1Eb2fI&fh,Do$4!6-j.p)rouEAP^&d#o#Lk@OW$,&kaEaN18B""BAJ=RQSAJRmt5P*I@cRF9oa&LDbHHln%ibC7[ST!u<p81IXn"s!p(TP;kE(D;Qk","OPK%1aBJM[kq(DUk^A'QW)l6^=e4li5Oq,js,7HD^L0?OlQCYcsJIB@]/M]_TNp0"W/&rr@:B&_p5`i<7Kf0/AaH7-S"4RrUO-;:%t]IjieH#<WfOCsY=rr@as8S'=)Z:a8+<pm%i@>r:L.Ths_&.7Lo,qk4!ZLeA`YRfD'p4@G(]&+fB>\]PhLRP0=H%q"@Pq#uHX[:i/1M5\d;l]\5rX1BoYaUV0JDFg@'CdqEPctH_I2b/\n&CrZ"OMCE\Q1F.*QQ0k\fZZQ"mYIVO?/Sifd,#(&[RSXQ:8o=M9$/,jWV`MG#=9Wm8T>bH\g:.hdWl5CZ;SY-?!IV[aku@NAMRSD_?PtqI\CrNsl^\3;,Hu)u$Yd1`6#TIVK:9Y,(b2[Ya\-"5Bf^\r#"ih\!It,h0aocb=>'aiHe)2T?._N-PuGS1ZF?j4k1a#<Dt2!c[#gGf^AaTRFW,_ibIBG2$GS0YG;`*Eu9#Ii=m1E0[,/_kkIL`J;lVlZJ%YZc2><V!L&\O<ESG&;J)3RaJ&!^Fma?C<Ak4KRfi`X_d1I6t8&-LA.2U"$?-D_&L#V!D)&ogQbnkkunF(W,?%WaAS3pQWPH'!/)slJKT?YGbPVgO/P'eQ^:qt^Y\8l&,;edl!RU'\t]`;<7S(5D!X[P(V3R;h3dh?*8!_EG9B?bo9cB&qq3B]-.2^=0e8D=O:-uh#l3/#Y.'Zo>mpKSD=$9)^/_nY&5RKkp3NG'E7(of^a:.QQceTOFGnrlN)j;j"La/n&I"2]0J4XV!UHAl<jIhn9Og_jU`P_ob1T0P9$]ah)-nH'0]C7<EoF-2*[Gu*kZj+/<ZPe3*-(A&[lI:V#k#)0gV0pR+opl<m`%3L>;.hW@6dfYi&QZB%ZU_('Ba7'!m1K;F=1CF6N(YbP:U^en`*ohkg,jF7$aVsEdQt;+;`mI74+2ViR?bu=].b<5qj&db'+SonaJ`EAA@Q,&r%eE""GV))ciD3$Y+=_We7R_*8[`BURNKfD[k24>.k5E&Uc>R,O-eke!&V+MHPu);)gi?R-[LX,R,"tgj;clS;*UG^I=@Tni6-08<3XjT\!CS4kJA)hkb!'Fb-GSd4:)oTN=Lsknh-Xh>2]#ib:rCIV5Y:h-E\c\<c<(dHPPa>.0[lTIY]fgqT5NfN\~>
Gau0D>Ar7S'Roe[&G!+0M]%?aFX`%H903q-MDe\Om@ics,#bp(=`ee+ls`2.Ua2GmaHW0f(kUBe7@s+A.mN<B55AbRq(crkV>Pg%M/6X*^@06@O/_#8k'A:`Y#TN.C%C$>p7!"kcmBf9IPYK"6fjMW3jIFF[et1t`t$X#7Yu&1epM=_])7qm'sFYgCJ%f6Qdb274o"9f];!$j@l8WskN."jnh4(qB5@M4:q5g&mi_O\W-S'Y][$$Q4fj"+PY!0]dqQ?/<E<b,\EUeCohYIRlVUZ?Oosdk.pfE(W@-t2"s2A/[N9:2,.Hn..L&O)4TIVIQ\kgDX@PdjNk5bQ<2tQ2gCCJ!VRE-6`97jM"sb2YaW."Xq$mDWPW[36ckUO,rNqGDQ%t4l$9Z"?2o"OpZ<dR$T]=u$U00^#j:(5Xl*WM&'8b3Y-dqOMd4m3AF<[TY=HD[1p2)V5N5kF6PpHG-\uB)_?pW6N0,Ap96j_d0XPjDWZ"d2$->4o'NWi$:+Al^)fleIe$*n`Q=iD9_YGjcUB'Fecg/'JB+tS8&-W0@@i0[t.U'=+3__Up%f7Roe:O&T$">@j9BCQU>-h2f0p(nqWkV?GC,>DmZb1a]:&&H*dcYJVH]LW1J6.K!d'OqDmc"!^F)(64g^)oTWn])hH;dN)@,=.u_DEVG[VimpA,%0RoBp<8Q30,TM<`:.%V`tfMP8!83$j.'2AgN,6f"Re_<cqZu*TCKV*1-._W&-D#`J^+$&u.Ku6gHQKP.G342@6/*FtNCS3KErJFqe+\\[9?urr14.pk9No`'<f#O4]8LgroiCEBAblJD>#5?,@T8+V%$C]_RlV]S>j'/!=Kre#@r'&]sP[,KpN*0O3n&dN"4_UVpS9LB9o,a;f^bc+,ij@\[s"c9]fRYJGq5G?IpU-3?d`L9Y'f;$"ecf5XjL,irm8)7HKh12<$:@+ikO]b8R&1s-7\VnES)DU>b1UXp-863DiccNmih@cVc,`-NXHka3"kPrc6'DA85k@#Jk!;]2rDK7(KEpT]_8j-?3%D"UiW_`&3'I=7U8`ud\:dHM--_@F/RhN9M4:L_M^$e)"Vb7-[C)o)L8),-ZPJAQO#"t<bq7:<E1Bsc#q`'%3+]5e-CJM_364C'Mo_A;K*p6?`)N`c%29Gj*7V,F<,'q9ts(eU&BL.7l"8KTa#i#4.H4hUb:MQaZ,!tAYH)V%7;NJ^"r,Ds'B^p'G"3YF^<REROGEPl4$bbb3AmoQ1>:-hcj11cWYL0'&ZI0@)l>4AB$`bY[uD4EQ_X;hu0YqB1_+C5_1K?:F@*tsFuP.XqfhNtg=[]?_8jNSRH0>>T-Rq;gm@=s-gm`&qUINu8`n<eI5I,IXt<]B!teW5a<-mtfOnP6.R]50f6Lgu&s<)<)s[DWm<'5n]rOV]"hg]1([4aGdI35P+cNuf@[HetL[&'-h[LT\4<Tp$>($E6UM1qD=r^9sET]%Eh3@(-A5Fr1^V+@Aa,_dP5.S^Z]XB/m&'=Th/!3*KTfObnl4+S)+F7+e1detOo=mt0Q$Z=T/p10oeShI"$_-0XXG<[\Mimqd?H%EV*SZgZnDX.u#.^%K1cN*;o#VF)+;8;?KXW97n7RCd>0"TaG]T3@TMRtMT&C5PK).\4u2XQOJIU@"q96)tfN>qg(d^faI)D*jYbcKTm@4%@GV8&CpSHlk#H1+*,=4)2<P^IU[0GRV;tPcR%=R2&>#62Oo3,<#ES>HV-g#@Z?r.<G-;g:"1T4_O3f^if&>6)=0phls\[RGsn,$TJ3rjgcG>\goJs'ed&m@$.&X(SEf<_>~>
endstream
endobj
102 0 obj
@ -619,10 +619,10 @@ endobj
>>
endobj
103 0 obj
<< /Length 697 /Filter [ /ASCII85Decode /FlateDecode ]
<< /Length 1120 /Filter [ /ASCII85Decode /FlateDecode ]
>>
stream
GarnU995Pr&AI`dI*>3>f/T/<Z!0*9<^!f>gIp:R#praU4%i8Qq=ca"GoS,fM#7_+j81qp07!%5&)MpM*UNATa9X+@Naqr.Z/N?P#u4>F:_EU6?Lm$"a&#=oMnBD<kX=bj7$Ajj3$o6[>P)Qnf:ipSBLBg"/+*F^XMi)/A&J'#jRC-L@;9c`Y`]G`\nehWcAcY:nXLV5q1ie`eeqHE(8>kP=27aV/VqSMq1Sa@r7_\VD2+g>I&Zjg.hjPTke$c%FAot1hg;Mb1tOU`V0`d8GjMg!_2W-2c5aB)+e(r9Kq0Nn-)_-AQ%ic4P$d\/CJu".B\.GSTJ<=%"@[5/#H8d"7&/;KQ[8$Hirp]PZ&AZIiDK3Xj&G?Y.D;gRYY?=7]/t4GCq^OZ1:9J9%KRPuHW;DQm]$h90j.I8o;3>%#FAR!@pmN]J<Ka(J(o>\9k!"Ra?B=.k%=^5p1>`B>k[cLU(g,8/2%DU68;1P8h>g\AeU"s8%tV`/4jfH`"]>fpNrT]e&FbYWY?PHAN,[N1Fs2$(,PBP@ASo,L*XSAS!O]&PRT^Q>'<_e>;d]T[O%\"_J(c,rIZ3iI$=Wf+dnZ6:2(=qYf];PE#(6o7TP(ZMY1*oYFRf*itWj(T]BJ&6AHs4Ys\>ged-eP,Q=MiF.(Y=AD-3RQ/!pS3kJ+G5(PH_$"2NQh-#upM>&*u#@5C~>
Gat%b99Z,/&:j6K'fsW>figri*kB7ZG\U-U<$"deR2gkl2H>90&GA)u52tO$Ma2Ga;]`"$<km/__>^t?rj_d2U$-;V0:S>:Jq>+!s*`b-GEc"L:a8iCq.6o4hs)9n*)U<s3G'3cS0I*VU@BLA3$:CGQKZYm<#/BT'L/A-d.)mZU.;Kg;#K`MeEo'GZ,,I!,O7h-ZI3-aX[U]2.jqma?0+YY"t5b:!@fkJnl'GM'R.0g0;>c#51_BpiiBNR*82,VYcBOkSjff^`E[,1fd#+]9'*[$Qd&O1qHGibUj:Qg2kS('[A'TtR&0_,UIMe1AT=!>'M$21=!k6K6HBO['9:0Zmk83\V73@p7[*`K`(/?XqX&S(?7H+2X>R\qdVM&:E:aFsTKjjp?'KIT5ooR@W=3Sk!JQ=@ij<4-EYka-rT>Y.ZDDC'Yn\@_e0)&"YME/Z!&k3Df<mC4B!DmJ(sf[h5@31";S1`$$gI*il7oT=3&g''kj<!eknNK7,(cRL+s-$aI7jBT:>uPQk)@l@r:L5QnDZXLd:[]D9Kh:,=,<e>d`X5%!E/7f@qd,@#s[Q`Y(b/$e\W^T?ukC<"3lVQ^dPRs9\1X<R"_<Yj+$]iYff_sXAB_m"Gc:)g+_n%OdY;7PZ1QgfS`1jk6aOJnXc(Qq/o*PZ74tSnF'EMit.D'pJd]pdcJ/CMs5S)QbY"6Fe[<p^4Yj*j6]sJlUI:!KU-R"I5m]b@BS:W+N2MSHF-;`I5,'g1+?bE?*$:eV5XMf#:CP/dCG$!<G/ef>M#34?u@oe&0uqPGM!l%`ML[A^<Ckm6GSs9FPhBnFJUVjX*JX!b.elJ"TDjB6@sih#ZjlFZ<b#`6u!0*hAnMcmX#@FBf$3q,X2=kMmteYbLo9G*[i*?$q8g(1[Gd0=i=0.4\T!@-Lkq):?.Un+caHj)1lrX=h.`u7saR[$(RR(1okf[0s6%jL-S*8.1("8-hnOQn=DerK0=;t2CB(p+f![Km5IK4ZT,SrHGk<YS!"@i#&>d^3I'e`SDuTe..U(9k\0:WWp`Z2^'n'S7K`/uVcI8["BLuJ1e%LFDifHIRD)BH*-VnTKchCTN5;d(cUVF5h#c8OH?6Kt#,ZYF!6;")$i~>
endstream
endobj
104 0 obj
@ -1038,73 +1038,73 @@ endobj
43 0 obj
<<
/S /GoTo
/D [82 0 R /XYZ 85.0 230.6 null]
/D [84 0 R /XYZ 85.0 659.0 null]
>>
endobj
45 0 obj
<<
/S /GoTo
/D [84 0 R /XYZ 85.0 616.6 null]
/D [84 0 R /XYZ 85.0 520.547 null]
>>
endobj
47 0 obj
<<
/S /GoTo
/D [84 0 R /XYZ 85.0 538.947 null]
/D [84 0 R /XYZ 85.0 442.894 null]
>>
endobj
49 0 obj
<<
/S /GoTo
/D [84 0 R /XYZ 85.0 286.494 null]
/D [84 0 R /XYZ 85.0 190.441 null]
>>
endobj
51 0 obj
<<
/S /GoTo
/D [84 0 R /XYZ 85.0 234.16 null]
/D [86 0 R /XYZ 85.0 659.0 null]
>>
endobj
56 0 obj
<<
/S /GoTo
/D [88 0 R /XYZ 85.0 528.6 null]
/D [88 0 R /XYZ 85.0 409.4 null]
>>
endobj
58 0 obj
<<
/S /GoTo
/D [92 0 R /XYZ 85.0 516.2 null]
/D [92 0 R /XYZ 85.0 383.8 null]
>>
endobj
60 0 obj
<<
/S /GoTo
/D [94 0 R /XYZ 85.0 291.8 null]
/D [94 0 R /XYZ 85.0 154.2 null]
>>
endobj
62 0 obj
<<
/S /GoTo
/D [96 0 R /XYZ 85.0 357.4 null]
/D [96 0 R /XYZ 85.0 230.2 null]
>>
endobj
64 0 obj
<<
/S /GoTo
/D [98 0 R /XYZ 85.0 414.2 null]
/D [98 0 R /XYZ 85.0 287.0 null]
>>
endobj
66 0 obj
<<
/S /GoTo
/D [102 0 R /XYZ 85.0 613.4 null]
/D [102 0 R /XYZ 85.0 478.6 null]
>>
endobj
68 0 obj
<<
/S /GoTo
/D [102 0 R /XYZ 85.0 151.747 null]
/D [104 0 R /XYZ 85.0 547.8 null]
>>
endobj
105 0 obj
@ -1115,74 +1115,74 @@ endobj
xref
0 142
0000000000 65535 f
0000048388 00000 n
0000048583 00000 n
0000048676 00000 n
0000048685 00000 n
0000048880 00000 n
0000048973 00000 n
0000000015 00000 n
0000000071 00000 n
0000001278 00000 n
0000001398 00000 n
0000001570 00000 n
0000048828 00000 n
0000001705 00000 n
0000048891 00000 n
0000001840 00000 n
0000048957 00000 n
0000001977 00000 n
0000049021 00000 n
0000002114 00000 n
0000049087 00000 n
0000002251 00000 n
0000049153 00000 n
0000002388 00000 n
0000049219 00000 n
0000002525 00000 n
0000049283 00000 n
0000002662 00000 n
0000049349 00000 n
0000002799 00000 n
0000049413 00000 n
0000002936 00000 n
0000049479 00000 n
0000003073 00000 n
0000049545 00000 n
0000003210 00000 n
0000049610 00000 n
0000003347 00000 n
0000049676 00000 n
0000003484 00000 n
0000049740 00000 n
0000003620 00000 n
0000049806 00000 n
0000003757 00000 n
0000049870 00000 n
0000003893 00000 n
0000049936 00000 n
0000004030 00000 n
0000050000 00000 n
0000004167 00000 n
0000050064 00000 n
0000004303 00000 n
0000050130 00000 n
0000004440 00000 n
0000050196 00000 n
0000004576 00000 n
0000001276 00000 n
0000001396 00000 n
0000001568 00000 n
0000049125 00000 n
0000001703 00000 n
0000049188 00000 n
0000001838 00000 n
0000049254 00000 n
0000001975 00000 n
0000049318 00000 n
0000002112 00000 n
0000049384 00000 n
0000002249 00000 n
0000049450 00000 n
0000002386 00000 n
0000049516 00000 n
0000002523 00000 n
0000049580 00000 n
0000002660 00000 n
0000049646 00000 n
0000002797 00000 n
0000049710 00000 n
0000002934 00000 n
0000049776 00000 n
0000003071 00000 n
0000049842 00000 n
0000003208 00000 n
0000049907 00000 n
0000003345 00000 n
0000049973 00000 n
0000003482 00000 n
0000050037 00000 n
0000003618 00000 n
0000050103 00000 n
0000003755 00000 n
0000050167 00000 n
0000003891 00000 n
0000050233 00000 n
0000004028 00000 n
0000050297 00000 n
0000004165 00000 n
0000050363 00000 n
0000004301 00000 n
0000050429 00000 n
0000004438 00000 n
0000050495 00000 n
0000004574 00000 n
0000005293 00000 n
0000005416 00000 n
0000005485 00000 n
0000050261 00000 n
0000050559 00000 n
0000005618 00000 n
0000050325 00000 n
0000050623 00000 n
0000005751 00000 n
0000050389 00000 n
0000050687 00000 n
0000005884 00000 n
0000050453 00000 n
0000050751 00000 n
0000006017 00000 n
0000050517 00000 n
0000050815 00000 n
0000006150 00000 n
0000050581 00000 n
0000050879 00000 n
0000006282 00000 n
0000050646 00000 n
0000050944 00000 n
0000006415 00000 n
0000008563 00000 n
0000008671 00000 n
@ -1194,68 +1194,68 @@ xref
0000015503 00000 n
0000017991 00000 n
0000018099 00000 n
0000019975 00000 n
0000020083 00000 n
0000020191 00000 n
0000022539 00000 n
0000022647 00000 n
0000024428 00000 n
0000024536 00000 n
0000025961 00000 n
0000026069 00000 n
0000027602 00000 n
0000027710 00000 n
0000029426 00000 n
0000029534 00000 n
0000031444 00000 n
0000031552 00000 n
0000033595 00000 n
0000033703 00000 n
0000035553 00000 n
0000035661 00000 n
0000037599 00000 n
0000037707 00000 n
0000039272 00000 n
0000039381 00000 n
0000041201 00000 n
0000041311 00000 n
0000042101 00000 n
0000050713 00000 n
0000042211 00000 n
0000042411 00000 n
0000042629 00000 n
0000042835 00000 n
0000043043 00000 n
0000043211 00000 n
0000043411 00000 n
0000043569 00000 n
0000043744 00000 n
0000043985 00000 n
0000044114 00000 n
0000044268 00000 n
0000044422 00000 n
0000044566 00000 n
0000044716 00000 n
0000044857 00000 n
0000045097 00000 n
0000045279 00000 n
0000045452 00000 n
0000045655 00000 n
0000045843 00000 n
0000046095 00000 n
0000046236 00000 n
0000046445 00000 n
0000046631 00000 n
0000046805 00000 n
0000047050 00000 n
0000047241 00000 n
0000047447 00000 n
0000047608 00000 n
0000047722 00000 n
0000047833 00000 n
0000047945 00000 n
0000048054 00000 n
0000048161 00000 n
0000048278 00000 n
0000022353 00000 n
0000022461 00000 n
0000024441 00000 n
0000024549 00000 n
0000026017 00000 n
0000026125 00000 n
0000027509 00000 n
0000027617 00000 n
0000029478 00000 n
0000029586 00000 n
0000031316 00000 n
0000031424 00000 n
0000033608 00000 n
0000033716 00000 n
0000035499 00000 n
0000035607 00000 n
0000037552 00000 n
0000037660 00000 n
0000039066 00000 n
0000039175 00000 n
0000041074 00000 n
0000041184 00000 n
0000042398 00000 n
0000051009 00000 n
0000042508 00000 n
0000042708 00000 n
0000042926 00000 n
0000043132 00000 n
0000043340 00000 n
0000043508 00000 n
0000043708 00000 n
0000043866 00000 n
0000044041 00000 n
0000044282 00000 n
0000044411 00000 n
0000044565 00000 n
0000044719 00000 n
0000044863 00000 n
0000045013 00000 n
0000045154 00000 n
0000045394 00000 n
0000045576 00000 n
0000045749 00000 n
0000045952 00000 n
0000046140 00000 n
0000046392 00000 n
0000046533 00000 n
0000046742 00000 n
0000046928 00000 n
0000047102 00000 n
0000047347 00000 n
0000047538 00000 n
0000047744 00000 n
0000047905 00000 n
0000048019 00000 n
0000048130 00000 n
0000048242 00000 n
0000048351 00000 n
0000048458 00000 n
0000048575 00000 n
trailer
<<
/Size 142
@ -1263,5 +1263,5 @@ trailer
/Info 4 0 R
>>
startxref
50767
51063
%%EOF

View File

@ -113,7 +113,10 @@ public class CheckIndex {
sFormat = "FORMAT_SINGLE_NORM_FILE [Lucene 2.2]";
else if (format == SegmentInfos.FORMAT_SHARED_DOC_STORE)
sFormat = "FORMAT_SHARED_DOC_STORE [Lucene 2.3]";
else if (format < SegmentInfos.FORMAT_SHARED_DOC_STORE) {
else if (format < SegmentInfos.FORMAT_CHECKSUM) {
sFormat = "FORMAT_CHECKSUM [Lucene 2.4]";
skip = true;
} else if (format < SegmentInfos.FORMAT_CHECKSUM) {
sFormat = "int=" + format + " [newer version of Lucene than this tool]";
skip = true;
} else {
@ -320,7 +323,7 @@ public class CheckIndex {
}
out.print("Writing...");
try {
newSIS.write(dir);
newSIS.commit(dir);
} catch (Throwable t) {
out.println("FAILED; exiting");
t.printStackTrace(out);

View File

@ -46,6 +46,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
private boolean closed;
protected IndexWriter writer;
protected int mergeThreadCount;
public ConcurrentMergeScheduler() {
if (allInstances != null) {
@ -211,10 +212,11 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
}
/** Create and return a new MergeThread */
protected MergeThread getMergeThread(IndexWriter writer, MergePolicy.OneMerge merge) throws IOException {
protected synchronized MergeThread getMergeThread(IndexWriter writer, MergePolicy.OneMerge merge) throws IOException {
final MergeThread thread = new MergeThread(writer, merge);
thread.setThreadPriority(mergeThreadPriority);
thread.setDaemon(true);
thread.setName("Lucene Merge Thread #" + mergeThreadCount++);
return thread;
}
@ -297,9 +299,9 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
}
} finally {
synchronized(ConcurrentMergeScheduler.this) {
ConcurrentMergeScheduler.this.notifyAll();
boolean removed = mergeThreads.remove(this);
assert removed;
ConcurrentMergeScheduler.this.notifyAll();
}
}
}
@ -334,6 +336,12 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
}
}
public static void clearUnhandledExceptions() {
synchronized(allInstances) {
anyExceptions = false;
}
}
/** Used for testing */
private void addMyself() {
synchronized(allInstances) {

View File

@ -19,6 +19,9 @@ package org.apache.lucene.index;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockObtainFailedException;
@ -37,6 +40,7 @@ abstract class DirectoryIndexReader extends IndexReader {
private SegmentInfos segmentInfos;
private Lock writeLock;
private boolean stale;
private HashSet synced = new HashSet();
/** Used by commit() to record pre-commit state in case
* rollback is necessary */
@ -44,16 +48,28 @@ abstract class DirectoryIndexReader extends IndexReader {
private SegmentInfos rollbackSegmentInfos;
void init(Directory directory, SegmentInfos segmentInfos, boolean closeDirectory) {
void init(Directory directory, SegmentInfos segmentInfos, boolean closeDirectory)
throws IOException {
this.directory = directory;
this.segmentInfos = segmentInfos;
this.closeDirectory = closeDirectory;
if (segmentInfos != null) {
// We assume that this segments_N was previously
// properly sync'd:
for(int i=0;i<segmentInfos.size();i++) {
final SegmentInfo info = segmentInfos.info(i);
List files = info.files();
for(int j=0;j<files.size();j++)
synced.add(files.get(j));
}
}
}
protected DirectoryIndexReader() {}
DirectoryIndexReader(Directory directory, SegmentInfos segmentInfos,
boolean closeDirectory) {
boolean closeDirectory) throws IOException {
super();
init(directory, segmentInfos, closeDirectory);
}
@ -190,7 +206,22 @@ abstract class DirectoryIndexReader extends IndexReader {
boolean success = false;
try {
commitChanges();
segmentInfos.write(directory);
// Sync all files we just wrote
for(int i=0;i<segmentInfos.size();i++) {
final SegmentInfo info = segmentInfos.info(i);
final List files = info.files();
for(int j=0;j<files.size();j++) {
final String fileName = (String) files.get(j);
if (!synced.contains(fileName)) {
assert directory.fileExists(fileName);
directory.sync(fileName);
synced.add(fileName);
}
}
}
segmentInfos.commit(directory);
success = true;
} finally {

View File

@ -453,7 +453,8 @@ final class DocumentsWriter {
assert false: "unknown exception: " + t;
}
} finally {
abortCount--;
if (ae != null)
abortCount--;
notifyAll();
}
}

View File

@ -32,13 +32,20 @@ import java.util.Collection;
/*
* This class keeps track of each SegmentInfos instance that
* is still "live", either because it corresponds to a
* segments_N file in the Directory (a "commit", i.e. a
* committed SegmentInfos) or because it's the in-memory SegmentInfos
* that a writer is actively updating but has not yet committed
* (currently this only applies when autoCommit=false in IndexWriter).
* This class uses simple reference counting to map the live
* SegmentInfos instances to individual files in the Directory.
* is still "live", either because it corresponds to a
* segments_N file in the Directory (a "commit", i.e. a
* committed SegmentInfos) or because it's an in-memory
* SegmentInfos that a writer is actively updating but has
* not yet committed. This class uses simple reference
* counting to map the live SegmentInfos instances to
* individual files in the Directory.
*
* When autoCommit=true, IndexWriter currently commits only
* on completion of a merge (though this may change with
* time: it is not a guarantee). When autoCommit=false,
* IndexWriter only commits when it is closed. Regardless
* of autoCommit, the user may call IndexWriter.commit() to
* force a blocking commit.
*
* The same directory file may be referenced by more than
* one IndexCommitPoints, i.e. more than one SegmentInfos.
@ -260,7 +267,7 @@ final class IndexFileDeleter {
for(int i=0;i<size;i++) {
CommitPoint commit = (CommitPoint) commitsToDelete.get(i);
if (infoStream != null) {
message("deleteCommits: now remove commit \"" + commit.getSegmentsFileName() + "\"");
message("deleteCommits: now decRef commit \"" + commit.getSegmentsFileName() + "\"");
}
int size2 = commit.files.size();
for(int j=0;j<size2;j++) {
@ -382,13 +389,6 @@ final class IndexFileDeleter {
// Incref the files:
incRef(segmentInfos, isCommit);
final List docWriterFiles;
if (docWriter != null) {
docWriterFiles = docWriter.files();
if (docWriterFiles != null)
incRef(docWriterFiles);
} else
docWriterFiles = null;
if (isCommit) {
// Append to our commits list:
@ -399,17 +399,27 @@ final class IndexFileDeleter {
// Decref files for commits that were deleted by the policy:
deleteCommits();
}
} else {
// DecRef old files from the last checkpoint, if any:
int size = lastFiles.size();
if (size > 0) {
for(int i=0;i<size;i++)
decRef((List) lastFiles.get(i));
lastFiles.clear();
}
final List docWriterFiles;
if (docWriter != null) {
docWriterFiles = docWriter.files();
if (docWriterFiles != null)
// We must incRef thes files before decRef'ing
// last files to make sure we don't accidentally
// delete them:
incRef(docWriterFiles);
} else
docWriterFiles = null;
// DecRef old files from the last checkpoint, if any:
int size = lastFiles.size();
if (size > 0) {
for(int i=0;i<size;i++)
decRef((List) lastFiles.get(i));
lastFiles.clear();
}
if (!isCommit) {
// Save files so we can decr on next checkpoint/commit:
size = segmentInfos.size();
for(int i=0;i<size;i++) {
@ -418,9 +428,9 @@ final class IndexFileDeleter {
lastFiles.add(segmentInfo.files());
}
}
if (docWriterFiles != null)
lastFiles.add(docWriterFiles);
}
if (docWriterFiles != null)
lastFiles.add(docWriterFiles);
}
void incRef(SegmentInfos segmentInfos, boolean isCommit) throws IOException {
@ -458,7 +468,7 @@ final class IndexFileDeleter {
}
}
private void decRef(String fileName) throws IOException {
void decRef(String fileName) throws IOException {
RefCount rc = getRefCount(fileName);
if (infoStream != null && VERBOSE_REF_COUNTS) {
message(" DecRef \"" + fileName + "\": pre-decr count is " + rc.count);

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,8 @@ package org.apache.lucene.index;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
import org.apache.lucene.store.ChecksumIndexOutput;
import org.apache.lucene.store.ChecksumIndexInput;
import java.io.File;
import java.io.FileNotFoundException;
@ -55,8 +57,12 @@ final class SegmentInfos extends Vector {
* vectors and stored fields file. */
public static final int FORMAT_SHARED_DOC_STORE = -4;
/** This format adds a checksum at the end of the file to
* ensure all bytes were successfully written. */
public static final int FORMAT_CHECKSUM = -5;
/* This must always point to the most recent file format. */
private static final int CURRENT_FORMAT = FORMAT_SHARED_DOC_STORE;
private static final int CURRENT_FORMAT = FORMAT_CHECKSUM;
public int counter = 0; // used to name new segments
/**
@ -197,7 +203,7 @@ final class SegmentInfos extends Vector {
// Clear any previous segments:
clear();
IndexInput input = directory.openInput(segmentFileName);
ChecksumIndexInput input = new ChecksumIndexInput(directory.openInput(segmentFileName));
generation = generationFromSegmentsFileName(segmentFileName);
@ -226,6 +232,13 @@ final class SegmentInfos extends Vector {
else
version = input.readLong(); // read version
}
if (format <= FORMAT_CHECKSUM) {
final long checksumNow = input.getChecksum();
final long checksumThen = input.readLong();
if (checksumNow != checksumThen)
throw new CorruptIndexException("checksum mismatch in segments file");
}
success = true;
}
finally {
@ -257,7 +270,7 @@ final class SegmentInfos extends Vector {
}.run();
}
public final void write(Directory directory) throws IOException {
private final void write(Directory directory) throws IOException {
String segmentFileName = getNextSegmentFileName();
@ -268,7 +281,7 @@ final class SegmentInfos extends Vector {
generation++;
}
IndexOutput output = directory.createOutput(segmentFileName);
ChecksumIndexOutput output = new ChecksumIndexOutput(directory.createOutput(segmentFileName));
boolean success = false;
@ -280,29 +293,31 @@ final class SegmentInfos extends Vector {
output.writeInt(size()); // write infos
for (int i = 0; i < size(); i++) {
info(i).write(output);
}
}
finally {
}
final long checksum = output.getChecksum();
output.writeLong(checksum);
success = true;
} finally {
boolean success2 = false;
try {
output.close();
success = true;
success2 = true;
} finally {
if (!success) {
if (!success || !success2)
// Try not to leave a truncated segments_N file in
// the index:
directory.deleteFile(segmentFileName);
}
}
}
try {
output = directory.createOutput(IndexFileNames.SEGMENTS_GEN);
IndexOutput genOutput = directory.createOutput(IndexFileNames.SEGMENTS_GEN);
try {
output.writeInt(FORMAT_LOCKLESS);
output.writeLong(generation);
output.writeLong(generation);
genOutput.writeInt(FORMAT_LOCKLESS);
genOutput.writeLong(generation);
genOutput.writeLong(generation);
} finally {
output.close();
genOutput.close();
}
} catch (IOException e) {
// It's OK if we fail to write this file since it's
@ -620,7 +635,7 @@ final class SegmentInfos extends Vector {
retry = true;
}
} else {
} else if (0 == method) {
// Segment file has advanced since our last loop, so
// reset retry:
retry = false;
@ -701,4 +716,50 @@ final class SegmentInfos extends Vector {
infos.addAll(super.subList(first, last));
return infos;
}
// Carry over generation numbers from another SegmentInfos
void updateGeneration(SegmentInfos other) {
assert other.generation > generation;
lastGeneration = other.lastGeneration;
generation = other.generation;
}
/** Writes & syncs to the Directory dir, taking care to
* remove the segments file on exception */
public final void commit(Directory dir) throws IOException {
boolean success = false;
try {
write(dir);
success = true;
} finally {
if (!success) {
// Must carefully compute fileName from "generation"
// since lastGeneration isn't incremented:
final String segmentFileName = IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS,
"",
generation);
dir.deleteFile(segmentFileName);
}
}
// NOTE: if we crash here, we have left a segments_N
// file in the directory in a possibly corrupt state (if
// some bytes made it to stable storage and others
// didn't). But, the segments_N file now includes
// checksum at the end, which should catch this case.
// So when a reader tries to read it, it will throw a
// CorruptIndexException, which should cause the retry
// logic in SegmentInfos to kick in and load the last
// good (previous) segments_N-1 file.
final String fileName = getCurrentSegmentFileName();
success = false;
try {
dir.sync(fileName);
success = true;
} finally {
if (!success)
dir.deleteFile(fileName);
}
}
}

View File

@ -0,0 +1,67 @@
package org.apache.lucene.store;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/** Writes bytes through to a primary IndexOutput, computing
* checksum as it goes. Note that you cannot use seek(). */
public class ChecksumIndexInput extends IndexInput {
IndexInput main;
Checksum digest;
public ChecksumIndexInput(IndexInput main) {
this.main = main;
digest = new CRC32();
}
public byte readByte() throws IOException {
final byte b = main.readByte();
digest.update(b);
return b;
}
public void readBytes(byte[] b, int offset, int len)
throws IOException {
main.readBytes(b, offset, len);
digest.update(b, offset, len);
}
public long getChecksum() {
return digest.getValue();
}
public void close() throws IOException {
main.close();
}
public long getFilePointer() {
return main.getFilePointer();
}
public void seek(long pos) {
throw new RuntimeException("not allowed");
}
public long length() {
return main.length();
}
}

View File

@ -0,0 +1,68 @@
package org.apache.lucene.store;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/** Writes bytes through to a primary IndexOutput, computing
* checksum. Note that you cannot use seek().*/
public class ChecksumIndexOutput extends IndexOutput {
IndexOutput main;
Checksum digest;
public ChecksumIndexOutput(IndexOutput main) {
this.main = main;
digest = new CRC32();
}
public void writeByte(byte b) throws IOException {
digest.update(b);
main.writeByte(b);
}
public void writeBytes(byte[] b, int offset, int length) throws IOException {
digest.update(b, offset, length);
main.writeBytes(b, offset, length);
}
public long getChecksum() {
return digest.getValue();
}
public void flush() throws IOException {
main.flush();
}
public void close() throws IOException {
main.close();
}
public long getFilePointer() {
return main.getFilePointer();
}
public void seek(long pos) {
throw new RuntimeException("not allowed");
}
public long length() throws IOException {
return main.length();
}
}

View File

@ -83,6 +83,11 @@ public abstract class Directory {
Returns a stream writing this file. */
public abstract IndexOutput createOutput(String name) throws IOException;
/** Ensure that any writes to this file are moved to
* stable storage. Lucene uses this to properly commit
* changes to the index, to prevent a machine/OS crash
* from corrupting the index. */
public void sync(String name) throws IOException {}
/** Returns a stream reading an existing file. */
public abstract IndexInput openInput(String name)

View File

@ -435,6 +435,39 @@ public class FSDirectory extends Directory {
return new FSIndexOutput(file);
}
public void sync(String name) throws IOException {
File fullFile = new File(directory, name);
boolean success = false;
int retryCount = 0;
IOException exc = null;
while(!success && retryCount < 5) {
retryCount++;
RandomAccessFile file = null;
try {
try {
file = new RandomAccessFile(fullFile, "rw");
file.getFD().sync();
success = true;
} finally {
if (file != null)
file.close();
}
} catch (IOException ioe) {
if (exc == null)
exc = ioe;
try {
// Pause 5 msec
Thread.sleep(5);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
if (!success)
// Throw original exception
throw exc;
}
// Inherit javadoc
public IndexInput openInput(String name) throws IOException {
return openInput(name, BufferedIndexInput.BUFFER_SIZE);

View File

@ -819,18 +819,24 @@
IsCompoundFile&gt;<sup>SegCount</sup>
</p>
<p>
<b>2.3 and above:</b>
<b>2.3:</b>
Segments --&gt; Format, Version, NameCounter, SegCount, &lt;SegName, SegSize, DelGen, DocStoreOffset, [DocStoreSegment, DocStoreIsCompoundFile], HasSingleNormFile, NumField,
NormGen<sup>NumField</sup>,
IsCompoundFile&gt;<sup>SegCount</sup>
</p>
<p>
<b>2.4 and above:</b>
Segments --&gt; Format, Version, NameCounter, SegCount, &lt;SegName, SegSize, DelGen, DocStoreOffset, [DocStoreSegment, DocStoreIsCompoundFile], HasSingleNormFile, NumField,
NormGen<sup>NumField</sup>,
IsCompoundFile&gt;<sup>SegCount</sup>, Checksum
</p>
<p>
Format, NameCounter, SegCount, SegSize, NumField, DocStoreOffset --&gt; Int32
</p>
<p>
Version, DelGen, NormGen --&gt; Int64
Version, DelGen, NormGen, Checksum --&gt; Int64
</p>
<p>
@ -842,7 +848,7 @@
</p>
<p>
Format is -1 as of Lucene 1.4, -3 (SegmentInfos.FORMAT_SINGLE_NORM_FILE) as of Lucene 2.1 and 2.2, and -4 (SegmentInfos.FORMAT_SHARED_DOC_STORE) as of Lucene 2.3
Format is -1 as of Lucene 1.4, -3 (SegmentInfos.FORMAT_SINGLE_NORM_FILE) as of Lucene 2.1 and 2.2, -4 (SegmentInfos.FORMAT_SHARED_DOC_STORE) as of Lucene 2.3 and -5 (SegmentInfos.FORMAT_CHECKSUM) as of Lucene 2.4.
</p>
<p>
@ -925,6 +931,13 @@
shares a single set of these files with other
segments.
</p>
<p>
Checksum contains the CRC32 checksum of all bytes
in the segments_N file up until the checksum.
This is used to verify integrity of the file on
opening the index.
</p>
</section>

View File

@ -20,12 +20,8 @@ import org.apache.lucene.util.*;
import org.apache.lucene.store.*;
import org.apache.lucene.document.*;
import org.apache.lucene.analysis.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.queryParser.*;
import org.apache.lucene.util._TestUtil;
import org.apache.lucene.util.LuceneTestCase;
import java.util.Random;
import java.io.File;
@ -83,7 +79,6 @@ public class TestAtomicUpdate extends LuceneTestCase {
// Update all 100 docs...
for(int i=0; i<100; i++) {
Document d = new Document();
int n = RANDOM.nextInt();
d.add(new Field("id", Integer.toString(i), Field.Store.YES, Field.Index.UN_TOKENIZED));
d.add(new Field("contents", English.intToEnglish(i+10*count), Field.Store.NO, Field.Index.TOKENIZED));
writer.updateDocument(new Term("id", Integer.toString(i)), d);
@ -127,7 +122,7 @@ public class TestAtomicUpdate extends LuceneTestCase {
d.add(new Field("contents", English.intToEnglish(i), Field.Store.NO, Field.Index.TOKENIZED));
writer.addDocument(d);
}
writer.flush();
writer.commit();
IndexerThread indexerThread = new IndexerThread(writer, threads);
threads[0] = indexerThread;

View File

@ -349,7 +349,6 @@ public class TestBackwardsCompatibility extends LuceneTestCase
IndexWriter writer = new IndexWriter(dir, autoCommit, new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
writer.setRAMBufferSizeMB(16.0);
//IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true);
for(int i=0;i<35;i++) {
addDoc(writer, i);
}
@ -390,12 +389,9 @@ public class TestBackwardsCompatibility extends LuceneTestCase
expected = new String[] {"_0.cfs",
"_0_1.del",
"_0_1.s" + contentFieldIndex,
"segments_4",
"segments_3",
"segments.gen"};
if (!autoCommit)
expected[3] = "segments_3";
String[] actual = dir.list();
Arrays.sort(expected);
Arrays.sort(actual);

View File

@ -0,0 +1,181 @@
package org.apache.lucene.index;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.store.MockRAMDirectory;
import org.apache.lucene.store.NoLockFactory;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
public class TestCrash extends LuceneTestCase {
private IndexWriter initIndex() throws IOException {
return initIndex(new MockRAMDirectory());
}
private IndexWriter initIndex(MockRAMDirectory dir) throws IOException {
dir.setLockFactory(NoLockFactory.getNoLockFactory());
IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer());
//writer.setMaxBufferedDocs(2);
writer.setMaxBufferedDocs(10);
((ConcurrentMergeScheduler) writer.getMergeScheduler()).setSuppressExceptions();
Document doc = new Document();
doc.add(new Field("content", "aaa", Field.Store.YES, Field.Index.TOKENIZED));
doc.add(new Field("id", "0", Field.Store.YES, Field.Index.TOKENIZED));
for(int i=0;i<157;i++)
writer.addDocument(doc);
return writer;
}
private void crash(final IndexWriter writer) throws IOException {
final MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
ConcurrentMergeScheduler cms = (ConcurrentMergeScheduler) writer.getMergeScheduler();
dir.crash();
cms.sync();
dir.clearCrash();
}
public void testCrashWhileIndexing() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
crash(writer);
IndexReader reader = IndexReader.open(dir);
assertTrue(reader.numDocs() < 157);
}
public void testWriterAfterCrash() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
dir.setPreventDoubleWrite(false);
crash(writer);
writer = initIndex(dir);
writer.close();
IndexReader reader = IndexReader.open(dir);
assertTrue(reader.numDocs() < 314);
}
public void testCrashAfterReopen() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
writer.close();
writer = initIndex(dir);
assertEquals(314, writer.docCount());
crash(writer);
/*
System.out.println("\n\nTEST: open reader");
String[] l = dir.list();
Arrays.sort(l);
for(int i=0;i<l.length;i++)
System.out.println("file " + i + " = " + l[i] + " " +
dir.fileLength(l[i]) + " bytes");
*/
IndexReader reader = IndexReader.open(dir);
assertTrue(reader.numDocs() >= 157);
}
public void testCrashAfterClose() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
writer.close();
dir.crash();
/*
String[] l = dir.list();
Arrays.sort(l);
for(int i=0;i<l.length;i++)
System.out.println("file " + i + " = " + l[i] + " " + dir.fileLength(l[i]) + " bytes");
*/
IndexReader reader = IndexReader.open(dir);
assertEquals(157, reader.numDocs());
}
public void testCrashAfterCloseNoWait() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
writer.close(false);
dir.crash();
/*
String[] l = dir.list();
Arrays.sort(l);
for(int i=0;i<l.length;i++)
System.out.println("file " + i + " = " + l[i] + " " + dir.fileLength(l[i]) + " bytes");
*/
IndexReader reader = IndexReader.open(dir);
assertEquals(157, reader.numDocs());
}
public void testCrashReaderDeletes() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
writer.close(false);
IndexReader reader = IndexReader.open(dir);
reader.deleteDocument(3);
dir.crash();
/*
String[] l = dir.list();
Arrays.sort(l);
for(int i=0;i<l.length;i++)
System.out.println("file " + i + " = " + l[i] + " " + dir.fileLength(l[i]) + " bytes");
*/
reader = IndexReader.open(dir);
assertEquals(157, reader.numDocs());
}
public void testCrashReaderDeletesAfterClose() throws IOException {
IndexWriter writer = initIndex();
MockRAMDirectory dir = (MockRAMDirectory) writer.getDirectory();
writer.close(false);
IndexReader reader = IndexReader.open(dir);
reader.deleteDocument(3);
reader.close();
dir.crash();
/*
String[] l = dir.list();
Arrays.sort(l);
for(int i=0;i<l.length;i++)
System.out.println("file " + i + " = " + l[i] + " " + dir.fileLength(l[i]) + " bytes");
*/
reader = IndexReader.open(dir);
assertEquals(156, reader.numDocs());
}
}

View File

@ -270,13 +270,10 @@ public class TestDeletionPolicy extends LuceneTestCase
writer.close();
assertEquals(2, policy.numOnInit);
if (autoCommit) {
assertTrue(policy.numOnCommit > 2);
} else {
if (!autoCommit)
// If we are not auto committing then there should
// be exactly 2 commits (one per close above):
assertEquals(2, policy.numOnCommit);
}
// Simplistic check: just verify all segments_N's still
// exist, and, I can open a reader on each:
@ -334,13 +331,10 @@ public class TestDeletionPolicy extends LuceneTestCase
writer.close();
assertEquals(2, policy.numOnInit);
if (autoCommit) {
assertTrue(policy.numOnCommit > 2);
} else {
if (!autoCommit)
// If we are not auto committing then there should
// be exactly 2 commits (one per close above):
assertEquals(2, policy.numOnCommit);
}
// Simplistic check: just verify the index is in fact
// readable:
@ -459,11 +453,8 @@ public class TestDeletionPolicy extends LuceneTestCase
writer.close();
assertEquals(2*(N+2), policy.numOnInit);
if (autoCommit) {
assertTrue(policy.numOnCommit > 2*(N+2)-1);
} else {
if (!autoCommit)
assertEquals(2*(N+2)-1, policy.numOnCommit);
}
IndexSearcher searcher = new IndexSearcher(dir);
Hits hits = searcher.search(query);
@ -565,11 +556,8 @@ public class TestDeletionPolicy extends LuceneTestCase
}
assertEquals(1+3*(N+1), policy.numOnInit);
if (autoCommit) {
assertTrue(policy.numOnCommit > 3*(N+1)-1);
} else {
if (!autoCommit)
assertEquals(2*(N+1), policy.numOnCommit);
}
IndexSearcher searcher = new IndexSearcher(dir);
Hits hits = searcher.search(query);

View File

@ -18,17 +18,8 @@ package org.apache.lucene.index;
*/
import org.apache.lucene.util.LuceneTestCase;
import java.util.Vector;
import java.util.Arrays;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.File;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.Hits;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.IndexOutput;
@ -77,8 +68,8 @@ public class TestIndexFileDeleter extends LuceneTestCase
String[] files = dir.list();
/*
for(int i=0;i<files.length;i++) {
System.out.println(i + ": " + files[i]);
for(int j=0;j<files.length;j++) {
System.out.println(j + ": " + files[j]);
}
*/
@ -145,8 +136,8 @@ public class TestIndexFileDeleter extends LuceneTestCase
copyFile(dir, "_0.cfs", "deletable");
// Create some old segments file:
copyFile(dir, "segments_a", "segments");
copyFile(dir, "segments_a", "segments_2");
copyFile(dir, "segments_3", "segments");
copyFile(dir, "segments_3", "segments_2");
// Create a bogus cfs file shadowing a non-cfs segment:
copyFile(dir, "_2.cfs", "_3.cfs");

View File

@ -202,7 +202,7 @@ public class TestIndexModifier extends LuceneTestCase {
class IndexThread extends Thread {
private final static int ITERATIONS = 500; // iterations of thread test
private final static int TEST_SECONDS = 3; // how many seconds to run each test
static int id = 0;
static Stack idStack = new Stack();
@ -224,8 +224,10 @@ class IndexThread extends Thread {
}
public void run() {
final long endTime = System.currentTimeMillis() + 1000*TEST_SECONDS;
try {
for(int i = 0; i < ITERATIONS; i++) {
while(System.currentTimeMillis() < endTime) {
int rand = random.nextInt(101);
if (rand < 5) {
index.optimize();

View File

@ -463,7 +463,7 @@ public class TestIndexReader extends LuceneTestCase
fileDirName.mkdir();
}
try {
IndexReader reader = IndexReader.open(fileDirName);
IndexReader.open(fileDirName);
fail("opening IndexReader on empty directory failed to produce FileNotFoundException");
} catch (FileNotFoundException e) {
// GOOD
@ -779,6 +779,11 @@ public class TestIndexReader extends LuceneTestCase
// Iterate w/ ever increasing free disk space:
while(!done) {
MockRAMDirectory dir = new MockRAMDirectory(startDir);
// If IndexReader hits disk full, it can write to
// the same files again.
dir.setPreventDoubleWrite(false);
IndexReader reader = IndexReader.open(dir);
// For each disk size, first try to commit against
@ -838,6 +843,7 @@ public class TestIndexReader extends LuceneTestCase
} catch (IOException e) {
if (debug) {
System.out.println(" hit IOException: " + e);
e.printStackTrace(System.out);
}
err = e;
if (1 == x) {
@ -855,7 +861,7 @@ public class TestIndexReader extends LuceneTestCase
String[] startFiles = dir.list();
SegmentInfos infos = new SegmentInfos();
infos.read(dir);
IndexFileDeleter d = new IndexFileDeleter(dir, new KeepOnlyLastCommitDeletionPolicy(), infos, null, null);
new IndexFileDeleter(dir, new KeepOnlyLastCommitDeletionPolicy(), infos, null, null);
String[] endFiles = dir.list();
Arrays.sort(startFiles);
@ -1030,7 +1036,7 @@ public class TestIndexReader extends LuceneTestCase
"deletetest");
Directory dir = FSDirectory.getDirectory(dirFile);
try {
IndexReader reader = IndexReader.open(dir);
IndexReader.open(dir);
fail("expected FileNotFoundException");
} catch (FileNotFoundException e) {
// expected
@ -1040,7 +1046,7 @@ public class TestIndexReader extends LuceneTestCase
// Make sure we still get a CorruptIndexException (not NPE):
try {
IndexReader reader = IndexReader.open(dir);
IndexReader.open(dir);
fail("expected FileNotFoundException");
} catch (FileNotFoundException e) {
// expected

View File

@ -651,19 +651,19 @@ public class TestIndexWriter extends LuceneTestCase
writer.setMaxBufferedDocs(2);
for(int iter=0;iter<10;iter++) {
for(int i=0;i<19;i++)
writer.addDocument(doc);
writer.flush();
((ConcurrentMergeScheduler) writer.getMergeScheduler()).sync();
writer.commit();
SegmentInfos sis = new SegmentInfos();
((ConcurrentMergeScheduler) writer.getMergeScheduler()).sync();
sis.read(dir);
final int segCount = sis.size();
writer.optimize(7);
writer.commit();
sis = new SegmentInfos();
((ConcurrentMergeScheduler) writer.getMergeScheduler()).sync();
@ -1045,7 +1045,7 @@ public class TestIndexWriter extends LuceneTestCase
* and add docs to it.
*/
public void testCommitOnCloseAbort() throws IOException {
Directory dir = new RAMDirectory();
MockRAMDirectory dir = new MockRAMDirectory();
IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
writer.setMaxBufferedDocs(10);
for (int i = 0; i < 14; i++) {
@ -1086,6 +1086,11 @@ public class TestIndexWriter extends LuceneTestCase
// and all is good:
writer = new IndexWriter(dir, false, new WhitespaceAnalyzer(), false, IndexWriter.MaxFieldLength.LIMITED);
writer.setMaxBufferedDocs(10);
// On abort, writer in fact may write to the same
// segments_N file:
dir.setPreventDoubleWrite(false);
for(int i=0;i<12;i++) {
for(int j=0;j<17;j++) {
addDoc(writer);
@ -1273,48 +1278,48 @@ public class TestIndexWriter extends LuceneTestCase
writer.setMaxBufferedDocs(10);
writer.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
long lastGen = -1;
int lastFlushCount = -1;
for(int j=1;j<52;j++) {
Document doc = new Document();
doc.add(new Field("field", "aaa" + j, Field.Store.YES, Field.Index.TOKENIZED));
writer.addDocument(doc);
_TestUtil.syncConcurrentMerges(writer);
long gen = SegmentInfos.generationFromSegmentsFileName(SegmentInfos.getCurrentSegmentFileName(dir.list()));
int flushCount = writer.getFlushCount();
if (j == 1)
lastGen = gen;
lastFlushCount = flushCount;
else if (j < 10)
// No new files should be created
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
else if (10 == j) {
assertTrue(gen > lastGen);
lastGen = gen;
assertTrue(flushCount > lastFlushCount);
lastFlushCount = flushCount;
writer.setRAMBufferSizeMB(0.000001);
writer.setMaxBufferedDocs(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (j < 20) {
assertTrue(gen > lastGen);
lastGen = gen;
assertTrue(flushCount > lastFlushCount);
lastFlushCount = flushCount;
} else if (20 == j) {
writer.setRAMBufferSizeMB(16);
writer.setMaxBufferedDocs(IndexWriter.DISABLE_AUTO_FLUSH);
lastGen = gen;
lastFlushCount = flushCount;
} else if (j < 30) {
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
} else if (30 == j) {
writer.setRAMBufferSizeMB(0.000001);
writer.setMaxBufferedDocs(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (j < 40) {
assertTrue(gen> lastGen);
lastGen = gen;
assertTrue(flushCount> lastFlushCount);
lastFlushCount = flushCount;
} else if (40 == j) {
writer.setMaxBufferedDocs(10);
writer.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
lastGen = gen;
lastFlushCount = flushCount;
} else if (j < 50) {
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
writer.setMaxBufferedDocs(10);
writer.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (50 == j) {
assertTrue(gen > lastGen);
assertTrue(flushCount > lastFlushCount);
}
}
writer.close();
@ -1334,46 +1339,46 @@ public class TestIndexWriter extends LuceneTestCase
writer.addDocument(doc);
}
long lastGen = -1;
int lastFlushCount = -1;
for(int j=1;j<52;j++) {
writer.deleteDocuments(new Term("field", "aaa" + j));
_TestUtil.syncConcurrentMerges(writer);
long gen = SegmentInfos.generationFromSegmentsFileName(SegmentInfos.getCurrentSegmentFileName(dir.list()));
int flushCount = writer.getFlushCount();
if (j == 1)
lastGen = gen;
lastFlushCount = flushCount;
else if (j < 10) {
// No new files should be created
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
} else if (10 == j) {
assertTrue(gen > lastGen);
lastGen = gen;
assertTrue(flushCount > lastFlushCount);
lastFlushCount = flushCount;
writer.setRAMBufferSizeMB(0.000001);
writer.setMaxBufferedDeleteTerms(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (j < 20) {
assertTrue(gen > lastGen);
lastGen = gen;
assertTrue(flushCount > lastFlushCount);
lastFlushCount = flushCount;
} else if (20 == j) {
writer.setRAMBufferSizeMB(16);
writer.setMaxBufferedDeleteTerms(IndexWriter.DISABLE_AUTO_FLUSH);
lastGen = gen;
lastFlushCount = flushCount;
} else if (j < 30) {
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
} else if (30 == j) {
writer.setRAMBufferSizeMB(0.000001);
writer.setMaxBufferedDeleteTerms(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (j < 40) {
assertTrue(gen> lastGen);
lastGen = gen;
assertTrue(flushCount> lastFlushCount);
lastFlushCount = flushCount;
} else if (40 == j) {
writer.setMaxBufferedDeleteTerms(10);
writer.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
lastGen = gen;
lastFlushCount = flushCount;
} else if (j < 50) {
assertEquals(gen, lastGen);
assertEquals(flushCount, lastFlushCount);
writer.setMaxBufferedDeleteTerms(10);
writer.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
} else if (50 == j) {
assertTrue(gen > lastGen);
assertTrue(flushCount > lastFlushCount);
}
}
writer.close();
@ -1831,11 +1836,18 @@ public class TestIndexWriter extends LuceneTestCase
public void eval(MockRAMDirectory dir) throws IOException {
if (doFail) {
StackTraceElement[] trace = new Exception().getStackTrace();
boolean sawAppend = false;
boolean sawFlush = false;
for (int i = 0; i < trace.length; i++) {
if ("org.apache.lucene.index.DocumentsWriter".equals(trace[i].getClassName()) && "appendPostings".equals(trace[i].getMethodName()) && count++ == 30) {
doFail = false;
throw new IOException("now failing during flush");
}
if ("org.apache.lucene.index.DocumentsWriter".equals(trace[i].getClassName()) && "appendPostings".equals(trace[i].getMethodName()))
sawAppend = true;
if ("doFlush".equals(trace[i].getMethodName()))
sawFlush = true;
}
if (sawAppend && sawFlush && count++ >= 30) {
doFail = false;
throw new IOException("now failing during flush");
}
}
}
@ -2263,6 +2275,7 @@ public class TestIndexWriter extends LuceneTestCase
try {
writer.updateDocument(new Term("id", ""+(idUpto++)), doc);
} catch (IOException ioe) {
//ioe.printStackTrace(System.out);
if (ioe.getMessage().startsWith("fake disk full at") ||
ioe.getMessage().equals("now failing on purpose")) {
diskFull = true;
@ -2282,6 +2295,7 @@ public class TestIndexWriter extends LuceneTestCase
break;
}
} catch (Throwable t) {
//t.printStackTrace(System.out);
if (noErrors) {
System.out.println(Thread.currentThread().getName() + ": ERROR: unexpected Throwable:");
t.printStackTrace(System.out);
@ -2300,7 +2314,7 @@ public class TestIndexWriter extends LuceneTestCase
public void testCloseWithThreads() throws IOException {
int NUM_THREADS = 3;
for(int iter=0;iter<50;iter++) {
for(int iter=0;iter<20;iter++) {
MockRAMDirectory dir = new MockRAMDirectory();
IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
ConcurrentMergeScheduler cms = new ConcurrentMergeScheduler();
@ -2310,7 +2324,6 @@ public class TestIndexWriter extends LuceneTestCase
writer.setMergeFactor(4);
IndexerThread[] threads = new IndexerThread[NUM_THREADS];
boolean diskFull = false;
for(int i=0;i<NUM_THREADS;i++)
threads[i] = new IndexerThread(writer, false);
@ -2319,7 +2332,7 @@ public class TestIndexWriter extends LuceneTestCase
threads[i].start();
try {
Thread.sleep(50);
Thread.sleep(100);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
@ -2403,7 +2416,6 @@ public class TestIndexWriter extends LuceneTestCase
dir.setMaxSizeInBytes(4*1024+20*iter);
IndexerThread[] threads = new IndexerThread[NUM_THREADS];
boolean diskFull = false;
for(int i=0;i<NUM_THREADS;i++)
threads[i] = new IndexerThread(writer, true);
@ -2441,7 +2453,7 @@ public class TestIndexWriter extends LuceneTestCase
private static class FailOnlyOnAbortOrFlush extends MockRAMDirectory.Failure {
private boolean onlyOnce;
public FailOnlyOnAbortOrFlush(boolean onlyOnce) {
this.onlyOnce = true;
this.onlyOnce = onlyOnce;
}
public void eval(MockRAMDirectory dir) throws IOException {
if (doFail) {
@ -2501,7 +2513,6 @@ public class TestIndexWriter extends LuceneTestCase
writer.setMergeFactor(4);
IndexerThread[] threads = new IndexerThread[NUM_THREADS];
boolean diskFull = false;
for(int i=0;i<NUM_THREADS;i++)
threads[i] = new IndexerThread(writer, true);
@ -2538,6 +2549,8 @@ public class TestIndexWriter extends LuceneTestCase
writer.close(false);
success = true;
} catch (IOException ioe) {
failure.clearDoFail();
writer.close(false);
}
if (success) {
@ -2583,7 +2596,7 @@ public class TestIndexWriter extends LuceneTestCase
private static class FailOnlyInCloseDocStore extends MockRAMDirectory.Failure {
private boolean onlyOnce;
public FailOnlyInCloseDocStore(boolean onlyOnce) {
this.onlyOnce = true;
this.onlyOnce = onlyOnce;
}
public void eval(MockRAMDirectory dir) throws IOException {
if (doFail) {
@ -2623,7 +2636,7 @@ public class TestIndexWriter extends LuceneTestCase
private static class FailOnlyInWriteSegment extends MockRAMDirectory.Failure {
private boolean onlyOnce;
public FailOnlyInWriteSegment(boolean onlyOnce) {
this.onlyOnce = true;
this.onlyOnce = onlyOnce;
}
public void eval(MockRAMDirectory dir) throws IOException {
if (doFail) {
@ -2682,6 +2695,125 @@ public class TestIndexWriter extends LuceneTestCase
dir.close();
}
// LUCENE-1044: Simulate checksum error in segments_N
public void testSegmentsChecksumError() throws IOException {
Directory dir = new MockRAMDirectory();
IndexWriter writer = null;
writer = new IndexWriter(dir, new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
// add 100 documents
for (int i = 0; i < 100; i++) {
addDoc(writer);
}
// close
writer.close();
long gen = SegmentInfos.getCurrentSegmentGeneration(dir);
assertTrue("segment generation should be > 1 but got " + gen, gen > 1);
final String segmentsFileName = SegmentInfos.getCurrentSegmentFileName(dir);
IndexInput in = dir.openInput(segmentsFileName);
IndexOutput out = dir.createOutput(IndexFileNames.fileNameFromGeneration(IndexFileNames.SEGMENTS, "", 1+gen));
out.copyBytes(in, in.length()-1);
byte b = in.readByte();
out.writeByte((byte) (1+b));
out.close();
in.close();
IndexReader reader = null;
try {
reader = IndexReader.open(dir);
} catch (IOException e) {
e.printStackTrace(System.out);
fail("segmentInfos failed to retry fallback to correct segments_N file");
}
reader.close();
}
// LUCENE-1044: test writer.commit() when ac=false
public void testForceCommit() throws IOException {
Directory dir = new MockRAMDirectory();
IndexWriter writer = new IndexWriter(dir, false, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
writer.setMaxBufferedDocs(2);
writer.setMergeFactor(5);
for (int i = 0; i < 23; i++)
addDoc(writer);
IndexReader reader = IndexReader.open(dir);
assertEquals(0, reader.numDocs());
writer.commit();
IndexReader reader2 = reader.reopen();
assertEquals(0, reader.numDocs());
assertEquals(23, reader2.numDocs());
reader.close();
for (int i = 0; i < 17; i++)
addDoc(writer);
assertEquals(23, reader2.numDocs());
reader2.close();
reader = IndexReader.open(dir);
assertEquals(23, reader.numDocs());
reader.close();
writer.commit();
reader = IndexReader.open(dir);
assertEquals(40, reader.numDocs());
reader.close();
writer.close();
dir.close();
}
// Throws IOException during MockRAMDirectory.sync
private static class FailOnlyInSync extends MockRAMDirectory.Failure {
boolean didFail;
public void eval(MockRAMDirectory dir) throws IOException {
if (doFail) {
StackTraceElement[] trace = new Exception().getStackTrace();
for (int i = 0; i < trace.length; i++) {
if (doFail && "org.apache.lucene.store.MockRAMDirectory".equals(trace[i].getClassName()) && "sync".equals(trace[i].getMethodName())) {
didFail = true;
throw new IOException("now failing on purpose during sync");
}
}
}
}
}
// LUCENE-1044: test exception during sync
public void testExceptionDuringSync() throws IOException {
MockRAMDirectory dir = new MockRAMDirectory();
FailOnlyInSync failure = new FailOnlyInSync();
dir.failOn(failure);
IndexWriter writer = new IndexWriter(dir, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
failure.setDoFail();
ConcurrentMergeScheduler cms = new ConcurrentMergeScheduler();
// We expect sync exceptions in the merge threads
cms.setSuppressExceptions();
writer.setMergeScheduler(cms);
writer.setMaxBufferedDocs(2);
writer.setMergeFactor(5);
for (int i = 0; i < 23; i++)
addDoc(writer);
cms.sync();
assertTrue(failure.didFail);
failure.clearDoFail();
writer.close();
IndexReader reader = IndexReader.open(dir);
assertEquals(23, reader.numDocs());
reader.close();
dir.close();
}
// LUCENE-1168
public void testTermVectorCorruption() throws IOException {

View File

@ -30,7 +30,6 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.MockRAMDirectory;
import org.apache.lucene.store.RAMDirectory;
public class TestIndexWriterDelete extends LuceneTestCase {
@ -45,7 +44,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for(int pass=0;pass<2;pass++) {
boolean autoCommit = (0==pass);
Directory dir = new RAMDirectory();
Directory dir = new MockRAMDirectory();
IndexWriter modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
modifier.setUseCompoundFile(true);
@ -65,28 +64,17 @@ public class TestIndexWriterDelete extends LuceneTestCase {
modifier.addDocument(doc);
}
modifier.optimize();
if (!autoCommit) {
modifier.close();
}
modifier.commit();
Term term = new Term("city", "Amsterdam");
int hitCount = getHitCount(dir, term);
assertEquals(1, hitCount);
if (!autoCommit) {
modifier = new IndexWriter(dir, autoCommit, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
modifier.setUseCompoundFile(true);
}
modifier.deleteDocuments(term);
if (!autoCommit) {
modifier.close();
}
modifier.commit();
hitCount = getHitCount(dir, term);
assertEquals(0, hitCount);
if (autoCommit) {
modifier.close();
}
modifier.close();
dir.close();
}
}
@ -96,7 +84,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for(int pass=0;pass<2;pass++) {
boolean autoCommit = (0==pass);
Directory dir = new RAMDirectory();
Directory dir = new MockRAMDirectory();
IndexWriter modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(2);
@ -108,38 +96,26 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for (int i = 0; i < 7; i++) {
addDoc(modifier, ++id, value);
}
modifier.flush();
modifier.commit();
assertEquals(0, modifier.getNumBufferedDocuments());
assertTrue(0 < modifier.getSegmentCount());
if (!autoCommit) {
modifier.close();
}
modifier.commit();
IndexReader reader = IndexReader.open(dir);
assertEquals(7, reader.numDocs());
reader.close();
if (!autoCommit) {
modifier = new IndexWriter(dir, autoCommit, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(2);
modifier.setMaxBufferedDeleteTerms(2);
}
modifier.deleteDocuments(new Term("value", String.valueOf(value)));
modifier.deleteDocuments(new Term("value", String.valueOf(value)));
if (!autoCommit) {
modifier.close();
}
modifier.commit();
reader = IndexReader.open(dir);
assertEquals(0, reader.numDocs());
reader.close();
if (autoCommit) {
modifier.close();
}
modifier.close();
dir.close();
}
}
@ -148,7 +124,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
public void testRAMDeletes() throws IOException {
for(int pass=0;pass<2;pass++) {
boolean autoCommit = (0==pass);
Directory dir = new RAMDirectory();
Directory dir = new MockRAMDirectory();
IndexWriter modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(4);
@ -169,9 +145,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
assertEquals(0, modifier.getSegmentCount());
modifier.flush();
if (!autoCommit) {
modifier.close();
}
modifier.commit();
IndexReader reader = IndexReader.open(dir);
assertEquals(1, reader.numDocs());
@ -179,9 +153,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
int hitCount = getHitCount(dir, new Term("id", String.valueOf(id)));
assertEquals(1, hitCount);
reader.close();
if (autoCommit) {
modifier.close();
}
modifier.close();
dir.close();
}
}
@ -191,7 +163,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for(int pass=0;pass<2;pass++) {
boolean autoCommit = (0==pass);
Directory dir = new RAMDirectory();
Directory dir = new MockRAMDirectory();
IndexWriter modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(100);
@ -208,23 +180,18 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for (int i = 0; i < 5; i++) {
addDoc(modifier, ++id, value);
}
modifier.flush();
modifier.commit();
for (int i = 0; i < 5; i++) {
addDoc(modifier, ++id, value);
}
modifier.deleteDocuments(new Term("value", String.valueOf(value)));
modifier.flush();
if (!autoCommit) {
modifier.close();
}
modifier.commit();
IndexReader reader = IndexReader.open(dir);
assertEquals(5, reader.numDocs());
if (autoCommit) {
modifier.close();
}
modifier.close();
}
}
@ -232,7 +199,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
public void testBatchDeletes() throws IOException {
for(int pass=0;pass<2;pass++) {
boolean autoCommit = (0==pass);
Directory dir = new RAMDirectory();
Directory dir = new MockRAMDirectory();
IndexWriter modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(2);
@ -244,29 +211,17 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for (int i = 0; i < 7; i++) {
addDoc(modifier, ++id, value);
}
modifier.flush();
if (!autoCommit) {
modifier.close();
}
modifier.commit();
IndexReader reader = IndexReader.open(dir);
assertEquals(7, reader.numDocs());
reader.close();
if (!autoCommit) {
modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(2);
modifier.setMaxBufferedDeleteTerms(2);
}
id = 0;
modifier.deleteDocuments(new Term("id", String.valueOf(++id)));
modifier.deleteDocuments(new Term("id", String.valueOf(++id)));
if (!autoCommit) {
modifier.close();
}
modifier.commit();
reader = IndexReader.open(dir);
assertEquals(5, reader.numDocs());
@ -276,23 +231,13 @@ public class TestIndexWriterDelete extends LuceneTestCase {
for (int i = 0; i < terms.length; i++) {
terms[i] = new Term("id", String.valueOf(++id));
}
if (!autoCommit) {
modifier = new IndexWriter(dir, autoCommit,
new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
modifier.setMaxBufferedDocs(2);
modifier.setMaxBufferedDeleteTerms(2);
}
modifier.deleteDocuments(terms);
if (!autoCommit) {
modifier.close();
}
modifier.commit();
reader = IndexReader.open(dir);
assertEquals(2, reader.numDocs());
reader.close();
if (autoCommit) {
modifier.close();
}
modifier.close();
dir.close();
}
}
@ -338,7 +283,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
boolean autoCommit = (0==pass);
// First build up a starting index:
RAMDirectory startDir = new RAMDirectory();
MockRAMDirectory startDir = new MockRAMDirectory();
IndexWriter writer = new IndexWriter(startDir, autoCommit,
new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
for (int i = 0; i < 157; i++) {
@ -444,38 +389,10 @@ public class TestIndexWriterDelete extends LuceneTestCase {
}
}
// Whether we succeeded or failed, check that all
// un-referenced files were in fact deleted (ie,
// we did not create garbage). Just create a
// new IndexFileDeleter, have it delete
// unreferenced files, then verify that in fact
// no files were deleted:
String[] startFiles = dir.list();
SegmentInfos infos = new SegmentInfos();
infos.read(dir);
new IndexFileDeleter(dir, new KeepOnlyLastCommitDeletionPolicy(), infos, null, null);
String[] endFiles = dir.list();
Arrays.sort(startFiles);
Arrays.sort(endFiles);
// for(int i=0;i<startFiles.length;i++) {
// System.out.println(" startFiles: " + i + ": " + startFiles[i]);
// }
if (!Arrays.equals(startFiles, endFiles)) {
String successStr;
if (success) {
successStr = "success";
} else {
successStr = "IOException";
err.printStackTrace();
}
fail("reader.close() failed to delete unreferenced files after "
+ successStr + " (" + diskFree + " bytes): before delete:\n "
+ arrayToString(startFiles) + "\n after delete:\n "
+ arrayToString(endFiles));
}
// If the close() succeeded, make sure there are
// no unreferenced files.
if (success)
TestIndexWriter.assertNoUnreferencedFiles(dir, "after writer.close");
// Finally, verify index is not corrupt, and, if
// we succeeded, we see all docs changed, and if
@ -618,12 +535,8 @@ public class TestIndexWriterDelete extends LuceneTestCase {
// flush (and commit if ac)
modifier.optimize();
modifier.commit();
// commit if !ac
if (!autoCommit) {
modifier.close();
}
// one of the two files hits
Term term = new Term("city", "Amsterdam");
@ -632,11 +545,6 @@ public class TestIndexWriterDelete extends LuceneTestCase {
// open the writer again (closed above)
if (!autoCommit) {
modifier = new IndexWriter(dir, autoCommit, new WhitespaceAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
modifier.setUseCompoundFile(true);
}
// delete the doc
// max buf del terms is two, so this is buffered
@ -648,7 +556,7 @@ public class TestIndexWriterDelete extends LuceneTestCase {
Document doc = new Document();
modifier.addDocument(doc);
// flush the changes, the buffered deletes, and the new doc
// commit the changes, the buffered deletes, and the new doc
// The failure object will fail on the first write after the del
// file gets created when processing the buffered delete
@ -659,38 +567,28 @@ public class TestIndexWriterDelete extends LuceneTestCase {
// in the !ac case, a new segments file won't be created but in
// this case, creation of the cfs file happens next so we need
// the doc (to test that it's okay that we don't lose deletes if
// failing while creating the cfs file
// failing while creating the cfs file)
boolean failed = false;
try {
modifier.flush();
modifier.commit();
} catch (IOException ioe) {
failed = true;
}
assertTrue(failed);
// The flush above failed, so we need to retry it (which will
// The commit above failed, so we need to retry it (which will
// succeed, because the failure is a one-shot)
if (!autoCommit) {
modifier.close();
} else {
modifier.flush();
}
modifier.commit();
hitCount = getHitCount(dir, term);
// If the delete was not cleared then hit count will
// be 0. With autoCommit=false, we hit the exception
// on creating the compound file, so the delete was
// flushed successfully.
assertEquals(autoCommit ? 1:0, hitCount);
if (autoCommit) {
modifier.close();
}
// Make sure the delete was successfully flushed:
assertEquals(0, hitCount);
modifier.close();
dir.close();
}
}

View File

@ -46,8 +46,8 @@ public class TestMultiSegmentReader extends LuceneTestCase {
doc2 = new Document();
DocHelper.setupDoc(doc1);
DocHelper.setupDoc(doc2);
SegmentInfo info1 = DocHelper.writeDoc(dir, doc1);
SegmentInfo info2 = DocHelper.writeDoc(dir, doc2);
DocHelper.writeDoc(dir, doc1);
DocHelper.writeDoc(dir, doc2);
sis = new SegmentInfos();
sis.read(dir);
}
@ -102,7 +102,7 @@ public class TestMultiSegmentReader extends LuceneTestCase {
if (reader instanceof MultiReader)
// MultiReader does not "own" the directory so it does
// not write the changes to sis on commit:
sis.write(dir);
sis.commit(dir);
sis.read(dir);
reader = openReader();
@ -115,7 +115,7 @@ public class TestMultiSegmentReader extends LuceneTestCase {
if (reader instanceof MultiReader)
// MultiReader does not "own" the directory so it does
// not write the changes to sis on commit:
sis.write(dir);
sis.commit(dir);
sis.read(dir);
reader = openReader();
assertEquals( 1, reader.numDocs() );

View File

@ -20,12 +20,9 @@ import org.apache.lucene.util.*;
import org.apache.lucene.store.*;
import org.apache.lucene.document.*;
import org.apache.lucene.analysis.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.queryParser.*;
import org.apache.lucene.util.LuceneTestCase;
import java.util.Random;
import java.io.File;
@ -123,6 +120,7 @@ public class TestStressIndexing extends LuceneTestCase {
modifier.setMaxBufferedDocs(10);
TimedThread[] threads = new TimedThread[4];
int numThread = 0;
if (mergeScheduler != null)
modifier.setMergeScheduler(mergeScheduler);
@ -130,34 +128,30 @@ public class TestStressIndexing extends LuceneTestCase {
// One modifier that writes 10 docs then removes 5, over
// and over:
IndexerThread indexerThread = new IndexerThread(modifier, threads);
threads[0] = indexerThread;
threads[numThread++] = indexerThread;
indexerThread.start();
IndexerThread indexerThread2 = new IndexerThread(modifier, threads);
threads[2] = indexerThread2;
threads[numThread++] = indexerThread2;
indexerThread2.start();
// Two searchers that constantly just re-instantiate the
// searcher:
SearcherThread searcherThread1 = new SearcherThread(directory, threads);
threads[3] = searcherThread1;
threads[numThread++] = searcherThread1;
searcherThread1.start();
SearcherThread searcherThread2 = new SearcherThread(directory, threads);
threads[3] = searcherThread2;
threads[numThread++] = searcherThread2;
searcherThread2.start();
indexerThread.join();
indexerThread2.join();
searcherThread1.join();
searcherThread2.join();
for(int i=0;i<numThread;i++)
threads[i].join();
modifier.close();
assertTrue("hit unexpected exception in indexer", !indexerThread.failed);
assertTrue("hit unexpected exception in indexer2", !indexerThread2.failed);
assertTrue("hit unexpected exception in search1", !searcherThread1.failed);
assertTrue("hit unexpected exception in search2", !searcherThread2.failed);
for(int i=0;i<numThread;i++)
assertTrue(!((TimedThread) threads[i]).failed);
//System.out.println(" Writer: " + indexerThread.count + " iterations");
//System.out.println("Searcher 1: " + searcherThread1.count + " searchers created");

View File

@ -39,10 +39,10 @@ public class TestThreadedOptimize extends LuceneTestCase {
private final static int NUM_THREADS = 3;
//private final static int NUM_THREADS = 5;
private final static int NUM_ITER = 2;
private final static int NUM_ITER = 1;
//private final static int NUM_ITER = 10;
private final static int NUM_ITER2 = 2;
private final static int NUM_ITER2 = 1;
//private final static int NUM_ITER2 = 5;
private boolean failed;
@ -138,8 +138,8 @@ public class TestThreadedOptimize extends LuceneTestCase {
*/
public void testThreadedOptimize() throws Exception {
Directory directory = new MockRAMDirectory();
runTest(directory, false, null);
runTest(directory, true, null);
runTest(directory, false, new SerialMergeScheduler());
runTest(directory, true, new SerialMergeScheduler());
runTest(directory, false, new ConcurrentMergeScheduler());
runTest(directory, true, new ConcurrentMergeScheduler());
directory.close();
@ -150,8 +150,8 @@ public class TestThreadedOptimize extends LuceneTestCase {
String dirName = tempDir + "/luceneTestThreadedOptimize";
directory = FSDirectory.getDirectory(dirName);
runTest(directory, false, null);
runTest(directory, true, null);
runTest(directory, false, new SerialMergeScheduler());
runTest(directory, true, new SerialMergeScheduler());
runTest(directory, false, new ConcurrentMergeScheduler());
runTest(directory, true, new ConcurrentMergeScheduler());
directory.close();

View File

@ -24,7 +24,10 @@ import java.util.Iterator;
import java.util.Random;
import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import java.util.Arrays;
/**
* This is a subclass of RAMDirectory that adds methods
@ -40,6 +43,10 @@ public class MockRAMDirectory extends RAMDirectory {
double randomIOExceptionRate;
Random randomState;
boolean noDeleteOpenFile = true;
boolean preventDoubleWrite = true;
private Set unSyncedFiles;
private Set createdFiles;
volatile boolean crashed;
// NOTE: we cannot initialize the Map here due to the
// order in which our constructor actually does this
@ -47,29 +54,78 @@ public class MockRAMDirectory extends RAMDirectory {
// like super is called, then our members are initialized:
Map openFiles;
private void init() {
if (openFiles == null)
openFiles = new HashMap();
if (createdFiles == null)
createdFiles = new HashSet();
if (unSyncedFiles == null)
unSyncedFiles = new HashSet();
}
public MockRAMDirectory() {
super();
if (openFiles == null) {
openFiles = new HashMap();
}
init();
}
public MockRAMDirectory(String dir) throws IOException {
super(dir);
if (openFiles == null) {
openFiles = new HashMap();
}
init();
}
public MockRAMDirectory(Directory dir) throws IOException {
super(dir);
if (openFiles == null) {
openFiles = new HashMap();
}
init();
}
public MockRAMDirectory(File dir) throws IOException {
super(dir);
if (openFiles == null) {
init();
}
/** If set to true, we throw an IOException if the same
* file is opened by createOutput, ever. */
public void setPreventDoubleWrite(boolean value) {
preventDoubleWrite = value;
}
public synchronized void sync(String name) throws IOException {
maybeThrowDeterministicException();
if (crashed)
throw new IOException("cannot sync after crash");
if (unSyncedFiles.contains(name))
unSyncedFiles.remove(name);
}
/** Simulates a crash of OS or machine by overwriting
* unsycned files. */
public void crash() throws IOException {
synchronized(this) {
crashed = true;
openFiles = new HashMap();
}
Iterator it = unSyncedFiles.iterator();
unSyncedFiles = new HashSet();
int count = 0;
while(it.hasNext()) {
String name = (String) it.next();
RAMFile file = (RAMFile) fileMap.get(name);
if (count % 3 == 0) {
deleteFile(name, true);
} else if (count % 3 == 1) {
// Zero out file entirely
final int numBuffers = file.numBuffers();
for(int i=0;i<numBuffers;i++) {
byte[] buffer = file.getBuffer(i);
Arrays.fill(buffer, (byte) 0);
}
} else if (count % 3 == 2) {
// Truncate the file:
file.setLength(file.getLength()/2);
}
count++;
}
}
public synchronized void clearCrash() throws IOException {
crashed = false;
}
public void setMaxSizeInBytes(long maxSize) {
@ -126,24 +182,41 @@ public class MockRAMDirectory extends RAMDirectory {
}
public synchronized void deleteFile(String name) throws IOException {
synchronized(openFiles) {
if (noDeleteOpenFile && openFiles.containsKey(name)) {
throw new IOException("MockRAMDirectory: file \"" + name + "\" is still open: cannot delete");
deleteFile(name, false);
}
private synchronized void deleteFile(String name, boolean forced) throws IOException {
if (crashed && !forced)
throw new IOException("cannot delete after crash");
if (unSyncedFiles.contains(name))
unSyncedFiles.remove(name);
if (!forced) {
synchronized(openFiles) {
if (noDeleteOpenFile && openFiles.containsKey(name)) {
throw new IOException("MockRAMDirectory: file \"" + name + "\" is still open: cannot delete");
}
}
}
super.deleteFile(name);
}
public IndexOutput createOutput(String name) throws IOException {
if (openFiles == null) {
openFiles = new HashMap();
}
if (crashed)
throw new IOException("cannot createOutput after crash");
init();
synchronized(openFiles) {
if (preventDoubleWrite && createdFiles.contains(name))
throw new IOException("file \"" + name + "\" was already written to");
if (noDeleteOpenFile && openFiles.containsKey(name))
throw new IOException("MockRAMDirectory: file \"" + name + "\" is still open: cannot overwrite");
}
RAMFile file = new RAMFile(this);
synchronized (this) {
if (crashed)
throw new IOException("cannot createOutput after crash");
unSyncedFiles.add(name);
createdFiles.add(name);
RAMFile existing = (RAMFile)fileMap.get(name);
// Enforce write once:
if (existing!=null && !name.equals("segments.gen"))

View File

@ -45,11 +45,14 @@ public class MockRAMInputStream extends RAMInputStream {
if (!isClone) {
synchronized(dir.openFiles) {
Integer v = (Integer) dir.openFiles.get(name);
if (v.intValue() == 1) {
dir.openFiles.remove(name);
} else {
v = new Integer(v.intValue()-1);
dir.openFiles.put(name, v);
// Could be null when MockRAMDirectory.crash() was called
if (v != null) {
if (v.intValue() == 1) {
dir.openFiles.remove(name);
} else {
v = new Integer(v.intValue()-1);
dir.openFiles.put(name, v);
}
}
}
}

View File

@ -63,6 +63,11 @@ public class MockRAMOutputStream extends RAMOutputStream {
long freeSpace = dir.maxSize - dir.sizeInBytes();
long realUsage = 0;
// If MockRAMDir crashed since we were opened, then
// don't write anything:
if (dir.crashed)
throw new IOException("MockRAMDirectory was crashed");
// Enforce disk full:
if (dir.maxSize != 0 && freeSpace <= len) {
// Compute the real disk free. This will greatly slow

View File

@ -46,6 +46,9 @@ public abstract class LuceneTestCase extends TestCase {
protected void tearDown() throws Exception {
if (ConcurrentMergeScheduler.anyUnhandledExceptions()) {
// Clear the failure so that we don't just keep
// failing subsequent test cases
ConcurrentMergeScheduler.clearUnhandledExceptions();
fail("ConcurrentMergeScheduler hit unhandled exceptions");
}
}