diff --git a/build.xml b/build.xml index 8d9326d962e..4b23cc49e3e 100644 --- a/build.xml +++ b/build.xml @@ -124,6 +124,7 @@ + diff --git a/dev-tools/eclipse/dot.classpath b/dev-tools/eclipse/dot.classpath index 18b83d8f37f..809ab6c5952 100644 --- a/dev-tools/eclipse/dot.classpath +++ b/dev-tools/eclipse/dot.classpath @@ -102,7 +102,7 @@ - + @@ -112,14 +112,22 @@ - - - - + + + + + + + + + + + + diff --git a/dev-tools/maven/lucene/contrib/demo/pom.xml.template b/dev-tools/maven/lucene/contrib/demo/pom.xml.template index f73258be1f7..3ef308c0f5e 100644 --- a/dev-tools/maven/lucene/contrib/demo/pom.xml.template +++ b/dev-tools/maven/lucene/contrib/demo/pom.xml.template @@ -70,8 +70,8 @@ ${project.version} - javax.servlet - servlet-api + org.eclipse.jetty.orbit + javax.servlet provided diff --git a/dev-tools/maven/pom.xml.template b/dev-tools/maven/pom.xml.template index a250a9763cd..342c8bb7765 100644 --- a/dev-tools/maven/pom.xml.template +++ b/dev-tools/maven/pom.xml.template @@ -42,8 +42,7 @@ 4.0.0 yyyy-MM-dd HH:mm:ss 1.6 - 6.1.26 - 6.1.26-patched-JETTY-1340 + 8.1.2.v20120308 1.6.1 1.0 @@ -296,14 +295,24 @@ 2.2 - org.mortbay.jetty - jetty - ${patched.jetty.version} + org.eclipse.jetty + jetty-server + ${jetty.version} - org.mortbay.jetty + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.eclipse.jetty jetty-util - ${patched.jetty.version} + ${jetty.version} + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} org.slf4j @@ -331,9 +340,9 @@ ${slf4j.version} - javax.servlet - servlet-api - 2.4 + org.eclipse.jetty.orbit + javax.servlet + 3.0.0.v201112011016 com.spatial4j @@ -495,7 +504,7 @@ org.mortbay.jetty - maven-jetty-plugin + jetty-maven-plugin ${jetty.version} @@ -636,7 +645,7 @@ solr-commons-csv ${project.version} jar - solr/lib/commons-csv-1.0-SNAPSHOT-r966014.jar + solr/lib/apache-solr-commons-csv-1.0-SNAPSHOT-r966014.jar @@ -653,34 +662,6 @@ solr/lib/apache-solr-noggit-r1211150.jar - - install-jetty - install - - install-file - - - org.mortbay.jetty - jetty - ${patched.jetty.version} - jar - solr/example/lib/jetty-${patched.jetty.version}.jar - - - - install-jetty-util - install - - install-file - - - org.mortbay.jetty - jetty-util - ${patched.jetty.version} - jar - solr/example/lib/jetty-util-${patched.jetty.version}.jar - - install-jsonic install diff --git a/dev-tools/maven/solr/contrib/clustering/pom.xml.template b/dev-tools/maven/solr/contrib/clustering/pom.xml.template index 277966ad333..bcd51e9d8b0 100644 --- a/dev-tools/maven/solr/contrib/clustering/pom.xml.template +++ b/dev-tools/maven/solr/contrib/clustering/pom.xml.template @@ -94,12 +94,17 @@ - org.mortbay.jetty - jetty + org.eclipse.jetty + jetty-server test - org.mortbay.jetty + org.eclipse.jetty + jetty-servlet + test + + + org.eclipse.jetty jetty-util test diff --git a/dev-tools/maven/solr/contrib/dataimporthandler/pom.xml.template b/dev-tools/maven/solr/contrib/dataimporthandler/pom.xml.template index e6abe764e86..f0e0369f6de 100644 --- a/dev-tools/maven/solr/contrib/dataimporthandler/pom.xml.template +++ b/dev-tools/maven/solr/contrib/dataimporthandler/pom.xml.template @@ -73,12 +73,17 @@ test - org.mortbay.jetty - jetty + org.eclipse.jetty + jetty-server test - org.mortbay.jetty + org.eclipse.jetty + jetty-servlet + test + + + org.eclipse.jetty jetty-util test diff --git a/dev-tools/maven/solr/core/pom.xml.template b/dev-tools/maven/solr/core/pom.xml.template index 9d786e21577..97840259d61 100644 --- a/dev-tools/maven/solr/core/pom.xml.template +++ b/dev-tools/maven/solr/core/pom.xml.template @@ -172,15 +172,20 @@ guava - org.mortbay.jetty - jetty + org.eclipse.jetty + jetty-server true - org.mortbay.jetty + org.eclipse.jetty jetty-util true + + org.eclipse.jetty + jetty-webapp + true + org.codehaus.woodstox wstx-asl @@ -193,8 +198,8 @@ - javax.servlet - servlet-api + org.eclipse.jetty.orbit + javax.servlet diff --git a/dev-tools/maven/solr/webapp/pom.xml.template b/dev-tools/maven/solr/webapp/pom.xml.template index 105ef63ac52..f6c82f643c9 100644 --- a/dev-tools/maven/solr/webapp/pom.xml.template +++ b/dev-tools/maven/solr/webapp/pom.xml.template @@ -58,8 +58,8 @@ ${project.version} - javax.servlet - servlet-api + org.eclipse.jetty.orbit + javax.servlet provided @@ -98,9 +98,9 @@ - + org.mortbay.jetty - maven-jetty-plugin + jetty-maven-plugin 10 diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 87678750af4..33dd4e7d2e8 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -410,6 +410,10 @@ API Changes method maybeReopen has been deprecated in favor of maybeRefresh(). (Shai Erera, Mike McCandless, Simon Willnauer) +* LUCENE-3859: AtomicReader.hasNorms(field) is deprecated, instead you + can inspect the FieldInfo yourself to see if norms are present, which + also allows you to get the type. (Robert Muir) + New features * LUCENE-2604: Added RegexpQuery support to QueryParser. Regular expressions @@ -919,7 +923,13 @@ Bug fixes from the delegate DocIdSet.iterator(), which is allowed to return null by DocIdSet specification when no documents match. (Shay Banon via Uwe Schindler) - + +* LUCENE-3821: SloppyPhraseScorer missed documents that ExactPhraseScorer finds + When phrase queru had repeating terms (e.g. "yes ho yes") + sloppy query missed documents that exact query matched. + Fixed except when for repeating multiterms (e.g. "yes ho yes|no"). + (Robert Muir, Doron Cohen) + Optimizations * LUCENE-3653: Improve concurrency in VirtualMethod and AttributeSource by @@ -932,6 +942,9 @@ Documentation Build +* LUCENE-3857: exceptions from other threads in beforeclass/etc do not fail + the test (Dawid Weiss) + * LUCENE-3847: LuceneTestCase will now check for modifications of System properties before and after each test (and suite). If changes are detected, the test will fail. A rule can be used to reset system properties to diff --git a/lucene/common-build.xml b/lucene/common-build.xml index ffb174afc27..51b18cfbe18 100644 --- a/lucene/common-build.xml +++ b/lucene/common-build.xml @@ -170,7 +170,7 @@ - + diff --git a/lucene/contrib/CHANGES.txt b/lucene/contrib/CHANGES.txt index a48d2fc82c2..20b8042e9cb 100644 --- a/lucene/contrib/CHANGES.txt +++ b/lucene/contrib/CHANGES.txt @@ -72,6 +72,8 @@ New Features start/endOffset, if offsets are indexed. (Alan Woodward via Mike McCandless) + * LUCENE-3802: Support for grouped faceting. (Martijn van Groningen) + API Changes * LUCENE-2606: Changed RegexCapabilities interface to fix thread @@ -242,6 +244,10 @@ Bug Fixes that take stopwords and stem exclusion tables also initialize the default stem overrides (e.g. kind/kinder, fiets). (Robert Muir) + * LUCENE-3831: avoid NPE if the SpanQuery has a null field (eg a + SpanOrQuery with no clauses added). (Alan Woodward via Mike + McCandless). + Documentation * LUCENE-3599: Javadocs for DistanceUtils.haversine() were incorrectly diff --git a/lucene/contrib/memory/src/java/org/apache/lucene/index/memory/MemoryIndexNormDocValues.java b/lucene/contrib/memory/src/java/org/apache/lucene/index/memory/MemoryIndexNormDocValues.java index d5f8222f653..36a3f9d5d8e 100644 --- a/lucene/contrib/memory/src/java/org/apache/lucene/index/memory/MemoryIndexNormDocValues.java +++ b/lucene/contrib/memory/src/java/org/apache/lucene/index/memory/MemoryIndexNormDocValues.java @@ -43,8 +43,8 @@ class MemoryIndexNormDocValues extends DocValues { } @Override - public Type type() { - return source.type(); + public Type getType() { + return source.getType(); } @Override diff --git a/lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java b/lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java index d3ccd61bc6e..8ae25e0b5b4 100644 --- a/lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java +++ b/lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java @@ -21,6 +21,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.StringReader; import java.util.HashSet; import java.util.Set; @@ -40,11 +41,16 @@ import org.apache.lucene.index.DocsEnum; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.Term; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; +import org.apache.lucene.search.spans.SpanOrQuery; +import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util._TestUtil; @@ -225,4 +231,28 @@ public class MemoryIndexTest extends BaseTokenStreamTestCase { assertTrue(disi.nextDoc() != DocIdSetIterator.NO_MORE_DOCS); reader.close(); } + + // LUCENE-3831 + public void testNullPointerException() throws IOException { + RegexpQuery regex = new RegexpQuery(new Term("field", "worl.")); + SpanQuery wrappedquery = new SpanMultiTermQueryWrapper(regex); + + MemoryIndex mindex = new MemoryIndex(); + mindex.addField("field", new MockAnalyzer(random).tokenStream("field", new StringReader("hello there"))); + + // This throws an NPE + assertEquals(0, mindex.search(wrappedquery), 0.00001f); + } + + // LUCENE-3831 + public void testPassesIfWrapped() throws IOException { + RegexpQuery regex = new RegexpQuery(new Term("field", "worl.")); + SpanQuery wrappedquery = new SpanOrQuery(new SpanMultiTermQueryWrapper(regex)); + + MemoryIndex mindex = new MemoryIndex(); + mindex.addField("field", new MockAnalyzer(random).tokenStream("field", new StringReader("hello there"))); + + // This passes though + assertEquals(0, mindex.search(wrappedquery), 0.00001f); + } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java index d2e42c63a18..72be59cca46 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/BlockTermsReader.java @@ -197,6 +197,7 @@ public class BlockTermsReader extends FieldsProducer { @Override public Terms terms(String field) throws IOException { + assert field != null; return fields.get(field); } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java index f773e1e72ee..00a0c2a696a 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java @@ -211,6 +211,7 @@ public class BlockTreeTermsReader extends FieldsProducer { @Override public Terms terms(String field) throws IOException { + assert field != null; return fields.get(field); } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/Codec.java b/lucene/core/src/java/org/apache/lucene/codecs/Codec.java index 8e960da2adf..98f130ae825 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/Codec.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/Codec.java @@ -19,6 +19,7 @@ package org.apache.lucene.codecs; import java.io.IOException; import java.util.Set; +import java.util.ServiceLoader; // javadocs import org.apache.lucene.index.IndexFileNames; import org.apache.lucene.index.IndexWriterConfig; // javadocs @@ -26,7 +27,15 @@ import org.apache.lucene.index.SegmentInfo; import org.apache.lucene.util.NamedSPILoader; /** - * Encodes/decodes an inverted index segment + * Encodes/decodes an inverted index segment. + *

+ * Note, when extending this class, the name ({@link #getName}) is + * written into the index. In order for the segment to be read, the + * name must resolve to your implementation via {@link #forName(String)}. + * This method uses Java's + * {@link ServiceLoader Service Provider Interface} to resolve codec names. + *

+ * @see ServiceLoader */ public abstract class Codec implements NamedSPILoader.NamedSPI { diff --git a/lucene/core/src/java/org/apache/lucene/codecs/DocValuesArraySource.java b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesArraySource.java new file mode 100644 index 00000000000..e83ab2a48db --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesArraySource.java @@ -0,0 +1,513 @@ +package org.apache.lucene.codecs; + +import java.io.IOException; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; + +import org.apache.lucene.index.DocValues.Source; +import org.apache.lucene.index.DocValues.Type; +import org.apache.lucene.store.IndexInput; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.RamUsageEstimator; + +/** + * 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. + */ + +/** + * @lucene.experimental + * @lucene.internal + */ +public abstract class DocValuesArraySource extends Source { + + private static final Map TEMPLATES; + + static { + EnumMap templates = new EnumMap( + Type.class); + templates.put(Type.FIXED_INTS_16, new ShortValues()); + templates.put(Type.FIXED_INTS_32, new IntValues()); + templates.put(Type.FIXED_INTS_64, new LongValues()); + templates.put(Type.FIXED_INTS_8, new ByteValues()); + templates.put(Type.FLOAT_32, new FloatValues()); + templates.put(Type.FLOAT_64, new DoubleValues()); + TEMPLATES = Collections.unmodifiableMap(templates); + } + + public static DocValuesArraySource forType(Type type) { + return TEMPLATES.get(type); + } + + protected final int bytesPerValue; + + DocValuesArraySource(int bytesPerValue, Type type) { + super(type); + this.bytesPerValue = bytesPerValue; + } + + @Override + public abstract BytesRef getBytes(int docID, BytesRef ref); + + + public abstract DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException; + + public abstract DocValuesArraySource newFromArray(Object array); + + @Override + public final boolean hasArray() { + return true; + } + + public void toBytes(long value, BytesRef bytesRef) { + copyLong(bytesRef, value); + } + + public void toBytes(double value, BytesRef bytesRef) { + copyLong(bytesRef, Double.doubleToRawLongBits(value)); + } + + final static class ByteValues extends DocValuesArraySource { + private final byte[] values; + + ByteValues() { + super(1, Type.FIXED_INTS_8); + values = new byte[0]; + } + private ByteValues(byte[] array) { + super(1, Type.FIXED_INTS_8); + values = array; + } + + private ByteValues(IndexInput input, int numDocs) throws IOException { + super(1, Type.FIXED_INTS_8); + values = new byte[numDocs]; + input.readBytes(values, 0, values.length, false); + } + + @Override + public byte[] getArray() { + return values; + } + + @Override + public long getInt(int docID) { + assert docID >= 0 && docID < values.length; + return values[docID]; + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new ByteValues(input, numDocs); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof byte[]; + return new ByteValues((byte[]) array); + } + + public void toBytes(long value, BytesRef bytesRef) { + if (bytesRef.bytes.length == 0) { + bytesRef.bytes = new byte[1]; + } + bytesRef.bytes[0] = (byte) (0xFFL & value); + bytesRef.offset = 0; + bytesRef.length = 1; + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getInt(docID), ref); + return ref; + } + + }; + + final static class ShortValues extends DocValuesArraySource { + private final short[] values; + + ShortValues() { + super(RamUsageEstimator.NUM_BYTES_SHORT, Type.FIXED_INTS_16); + values = new short[0]; + } + + private ShortValues(short[] array) { + super(RamUsageEstimator.NUM_BYTES_SHORT, Type.FIXED_INTS_16); + values = array; + } + + private ShortValues(IndexInput input, int numDocs) throws IOException { + super(RamUsageEstimator.NUM_BYTES_SHORT, Type.FIXED_INTS_16); + values = new short[numDocs]; + for (int i = 0; i < values.length; i++) { + values[i] = input.readShort(); + } + } + + @Override + public short[] getArray() { + return values; + } + + @Override + public long getInt(int docID) { + assert docID >= 0 && docID < values.length; + return values[docID]; + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new ShortValues(input, numDocs); + } + + public void toBytes(long value, BytesRef bytesRef) { + copyShort(bytesRef, (short) (0xFFFFL & value)); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof short[]; + return new ShortValues((short[]) array); + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getInt(docID), ref); + return ref; + } + + }; + + final static class IntValues extends DocValuesArraySource { + private final int[] values; + + IntValues() { + super(RamUsageEstimator.NUM_BYTES_INT, Type.FIXED_INTS_32); + values = new int[0]; + } + + private IntValues(IndexInput input, int numDocs) throws IOException { + super(RamUsageEstimator.NUM_BYTES_INT, Type.FIXED_INTS_32); + values = new int[numDocs]; + for (int i = 0; i < values.length; i++) { + values[i] = input.readInt(); + } + } + + private IntValues(int[] array) { + super(RamUsageEstimator.NUM_BYTES_INT, Type.FIXED_INTS_32); + values = array; + } + + @Override + public int[] getArray() { + return values; + } + + @Override + public long getInt(int docID) { + assert docID >= 0 && docID < values.length; + return 0xFFFFFFFF & values[docID]; + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new IntValues(input, numDocs); + } + + public void toBytes(long value, BytesRef bytesRef) { + copyInt(bytesRef, (int) (0xFFFFFFFF & value)); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof int[]; + return new IntValues((int[]) array); + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getInt(docID), ref); + return ref; + } + + }; + + final static class LongValues extends DocValuesArraySource { + private final long[] values; + + LongValues() { + super(RamUsageEstimator.NUM_BYTES_LONG, Type.FIXED_INTS_64); + values = new long[0]; + } + + private LongValues(IndexInput input, int numDocs) throws IOException { + super(RamUsageEstimator.NUM_BYTES_LONG, Type.FIXED_INTS_64); + values = new long[numDocs]; + for (int i = 0; i < values.length; i++) { + values[i] = input.readLong(); + } + } + + private LongValues(long[] array) { + super(RamUsageEstimator.NUM_BYTES_LONG, Type.FIXED_INTS_64); + values = array; + } + + @Override + public long[] getArray() { + return values; + } + + @Override + public long getInt(int docID) { + assert docID >= 0 && docID < values.length; + return values[docID]; + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new LongValues(input, numDocs); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof long[]; + return new LongValues((long[])array); + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getInt(docID), ref); + return ref; + } + + }; + + final static class FloatValues extends DocValuesArraySource { + private final float[] values; + + FloatValues() { + super(RamUsageEstimator.NUM_BYTES_FLOAT, Type.FLOAT_32); + values = new float[0]; + } + + private FloatValues(IndexInput input, int numDocs) throws IOException { + super(RamUsageEstimator.NUM_BYTES_FLOAT, Type.FLOAT_32); + values = new float[numDocs]; + /* + * we always read BIG_ENDIAN here since the writer serialized plain bytes + * we can simply read the ints / longs back in using readInt / readLong + */ + for (int i = 0; i < values.length; i++) { + values[i] = Float.intBitsToFloat(input.readInt()); + } + } + + private FloatValues(float[] array) { + super(RamUsageEstimator.NUM_BYTES_FLOAT, Type.FLOAT_32); + values = array; + } + + @Override + public float[] getArray() { + return values; + } + + @Override + public double getFloat(int docID) { + assert docID >= 0 && docID < values.length; + return values[docID]; + } + + @Override + public void toBytes(double value, BytesRef bytesRef) { + copyInt(bytesRef, Float.floatToRawIntBits((float)value)); + + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new FloatValues(input, numDocs); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof float[]; + return new FloatValues((float[]) array); + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getFloat(docID), ref); + return ref; + } + }; + + final static class DoubleValues extends DocValuesArraySource { + private final double[] values; + + DoubleValues() { + super(RamUsageEstimator.NUM_BYTES_DOUBLE, Type.FLOAT_64); + values = new double[0]; + } + + private DoubleValues(IndexInput input, int numDocs) throws IOException { + super(RamUsageEstimator.NUM_BYTES_DOUBLE, Type.FLOAT_64); + values = new double[numDocs]; + /* + * we always read BIG_ENDIAN here since the writer serialized plain bytes + * we can simply read the ints / longs back in using readInt / readLong + */ + for (int i = 0; i < values.length; i++) { + values[i] = Double.longBitsToDouble(input.readLong()); + } + } + + private DoubleValues(double[] array) { + super(RamUsageEstimator.NUM_BYTES_DOUBLE, Type.FLOAT_64); + values = array; + } + + @Override + public double[] getArray() { + return values; + } + + @Override + public double getFloat(int docID) { + assert docID >= 0 && docID < values.length; + return values[docID]; + } + + @Override + public DocValuesArraySource newFromInput(IndexInput input, int numDocs) + throws IOException { + return new DoubleValues(input, numDocs); + } + + @Override + public DocValuesArraySource newFromArray(Object array) { + assert array instanceof double[]; + return new DoubleValues((double[]) array); + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + toBytes(getFloat(docID), ref); + return ref; + } + + }; + + /** + * Copies the given long value and encodes it as 8 byte Big-Endian. + *

+ * NOTE: this method resets the offset to 0, length to 8 and resizes the + * reference array if needed. + */ + public static void copyLong(BytesRef ref, long value) { + if (ref.bytes.length < 8) { + ref.bytes = new byte[8]; + } + copyInternal(ref, (int) (value >> 32), ref.offset = 0); + copyInternal(ref, (int) value, 4); + ref.length = 8; + } + + /** + * Copies the given int value and encodes it as 4 byte Big-Endian. + *

+ * NOTE: this method resets the offset to 0, length to 4 and resizes the + * reference array if needed. + */ + public static void copyInt(BytesRef ref, int value) { + if (ref.bytes.length < 4) { + ref.bytes = new byte[4]; + } + copyInternal(ref, value, ref.offset = 0); + ref.length = 4; + + } + + /** + * Copies the given short value and encodes it as a 2 byte Big-Endian. + *

+ * NOTE: this method resets the offset to 0, length to 2 and resizes the + * reference array if needed. + */ + public static void copyShort(BytesRef ref, short value) { + if (ref.bytes.length < 2) { + ref.bytes = new byte[2]; + } + ref.offset = 0; + ref.bytes[ref.offset] = (byte) (value >> 8); + ref.bytes[ref.offset + 1] = (byte) (value); + ref.length = 2; + } + + private static void copyInternal(BytesRef ref, int value, int startOffset) { + ref.bytes[startOffset] = (byte) (value >> 24); + ref.bytes[startOffset + 1] = (byte) (value >> 16); + ref.bytes[startOffset + 2] = (byte) (value >> 8); + ref.bytes[startOffset + 3] = (byte) (value); + } + + /** + * Converts 2 consecutive bytes from the current offset to a short. Bytes are + * interpreted as Big-Endian (most significant bit first) + *

+ * NOTE: this method does NOT check the bounds of the referenced array. + */ + public static short asShort(BytesRef b) { + return (short) (0xFFFF & ((b.bytes[b.offset] & 0xFF) << 8) | (b.bytes[b.offset + 1] & 0xFF)); + } + + /** + * Converts 4 consecutive bytes from the current offset to an int. Bytes are + * interpreted as Big-Endian (most significant bit first) + *

+ * NOTE: this method does NOT check the bounds of the referenced array. + */ + public static int asInt(BytesRef b) { + return asIntInternal(b, b.offset); + } + + /** + * Converts 8 consecutive bytes from the current offset to a long. Bytes are + * interpreted as Big-Endian (most significant bit first) + *

+ * NOTE: this method does NOT check the bounds of the referenced array. + */ + public static long asLong(BytesRef b) { + return (((long) asIntInternal(b, b.offset) << 32) | asIntInternal(b, + b.offset + 4) & 0xFFFFFFFFL); + } + + private static int asIntInternal(BytesRef b, int pos) { + return ((b.bytes[pos++] & 0xFF) << 24) | ((b.bytes[pos++] & 0xFF) << 16) + | ((b.bytes[pos++] & 0xFF) << 8) | (b.bytes[pos] & 0xFF); + } + + +} \ No newline at end of file diff --git a/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java index e59ffbb481a..5b621395512 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java @@ -22,6 +22,7 @@ import org.apache.lucene.document.DocValuesField; import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.MergeState; import org.apache.lucene.util.Bits; @@ -40,6 +41,7 @@ public abstract class DocValuesConsumer { protected final BytesRef spare = new BytesRef(); + protected abstract Type getType(); /** * Adds the given {@link IndexableField} instance to this * {@link DocValuesConsumer} @@ -110,7 +112,7 @@ public abstract class DocValuesConsumer { final Source source = reader.getDirectSource(); assert source != null; int docID = docBase; - final DocValues.Type type = reader.type(); + final Type type = getType(); final Field scratchField; switch(type) { case VAR_INTS: @@ -160,7 +162,7 @@ public abstract class DocValuesConsumer { */ protected void mergeDoc(Field scratchField, Source source, int docID, int sourceDoc) throws IOException { - switch(source.type()) { + switch(getType()) { case BYTES_FIXED_DEREF: case BYTES_FIXED_SORTED: case BYTES_FIXED_STRAIGHT: diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesReaderBase.java b/lucene/core/src/java/org/apache/lucene/codecs/PerDocProducerBase.java similarity index 67% rename from lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesReaderBase.java rename to lucene/core/src/java/org/apache/lucene/codecs/PerDocProducerBase.java index 71fb02f0387..d2bfb46b9f6 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesReaderBase.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/PerDocProducerBase.java @@ -1,4 +1,4 @@ -package org.apache.lucene.codecs.lucene40.values; +package org.apache.lucene.codecs; /** * Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,10 +24,6 @@ import java.util.Comparator; import java.util.Map; import java.util.TreeMap; -import org.apache.lucene.codecs.PerDocProducer; -import org.apache.lucene.codecs.lucene40.values.Bytes; -import org.apache.lucene.codecs.lucene40.values.Floats; -import org.apache.lucene.codecs.lucene40.values.Ints; import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfos; import org.apache.lucene.index.DocValues; @@ -40,7 +36,7 @@ import org.apache.lucene.util.BytesRef; * Abstract base class for PerDocProducer implementations * @lucene.experimental */ -public abstract class DocValuesReaderBase extends PerDocProducer { +public abstract class PerDocProducerBase extends PerDocProducer { protected abstract void closeInternal(Collection closeables) throws IOException; protected abstract Map docValues(); @@ -70,9 +66,7 @@ public abstract class DocValuesReaderBase extends PerDocProducer { for (FieldInfo fieldInfo : fieldInfos) { if (canLoad(fieldInfo)) { final String field = fieldInfo.name; - // TODO can we have a compound file per segment and codec for - // docvalues? - final String id = DocValuesWriterBase.docValuesId(segment, + final String id = docValuesId(segment, fieldInfo.number); values.put(field, loadDocValues(docCount, dir, id, getDocValuesType(fieldInfo), context)); @@ -97,7 +91,11 @@ public abstract class DocValuesReaderBase extends PerDocProducer { } protected boolean anyDocValuesFields(FieldInfos infos) { - return infos.anyDocValuesFields(); + return infos.hasDocValues(); + } + + public static String docValuesId(String segmentsName, int fieldId) { + return segmentsName + "_" + fieldId; } /** @@ -119,33 +117,6 @@ public abstract class DocValuesReaderBase extends PerDocProducer { * @throws IllegalArgumentException * if the given {@link Type} is not supported */ - protected DocValues loadDocValues(int docCount, Directory dir, String id, - DocValues.Type type, IOContext context) throws IOException { - switch (type) { - case FIXED_INTS_16: - case FIXED_INTS_32: - case FIXED_INTS_64: - case FIXED_INTS_8: - case VAR_INTS: - return Ints.getValues(dir, id, docCount, type, context); - case FLOAT_32: - return Floats.getValues(dir, id, docCount, context, type); - case FLOAT_64: - return Floats.getValues(dir, id, docCount, context, type); - case BYTES_FIXED_STRAIGHT: - return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, true, docCount, getComparator(), context); - case BYTES_FIXED_DEREF: - return Bytes.getValues(dir, id, Bytes.Mode.DEREF, true, docCount, getComparator(), context); - case BYTES_FIXED_SORTED: - return Bytes.getValues(dir, id, Bytes.Mode.SORTED, true, docCount, getComparator(), context); - case BYTES_VAR_STRAIGHT: - return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, false, docCount, getComparator(), context); - case BYTES_VAR_DEREF: - return Bytes.getValues(dir, id, Bytes.Mode.DEREF, false, docCount, getComparator(), context); - case BYTES_VAR_SORTED: - return Bytes.getValues(dir, id, Bytes.Mode.SORTED, false, docCount, getComparator(), context); - default: - throw new IllegalStateException("unrecognized index values mode " + type); - } - } + protected abstract DocValues loadDocValues(int docCount, Directory dir, String id, + DocValues.Type type, IOContext context) throws IOException; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xFieldInfosReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xFieldInfosReader.java index 8cacae53379..fcad39cf482 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xFieldInfosReader.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xFieldInfosReader.java @@ -107,7 +107,7 @@ class Lucene3xFieldInfosReader extends FieldInfosReader { hasProx |= isIndexed && indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS; hasFreq |= isIndexed && indexOptions != IndexOptions.DOCS_ONLY; infos[i] = new FieldInfo(name, isIndexed, fieldNumber, storeTermVector, - omitNorms, storePayloads, indexOptions, null, isIndexed && !omitNorms? Type.BYTES_VAR_STRAIGHT : null); + omitNorms, storePayloads, indexOptions, null, isIndexed && !omitNorms? Type.FIXED_INTS_8 : null); } if (input.getFilePointer() != input.length()) { diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xNormsProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xNormsProducer.java index 8d0ec06125f..045173d5e00 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xNormsProducer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene3x/Lucene3xNormsProducer.java @@ -76,7 +76,7 @@ class Lucene3xNormsProducer extends PerDocProducer { try { long nextNormSeek = NORMS_HEADER.length; //skip header (header unused for now) for (FieldInfo fi : fields) { - if (fi.normsPresent()) { + if (fi.hasNorms()) { String fileName = getNormFilename(segmentName, normGen, fi.number); Directory d = hasSeparateNorms(normGen, fi.number) ? separateNormsDir : dir; @@ -235,7 +235,7 @@ class Lucene3xNormsProducer extends PerDocProducer { } @Override - public Type type() { + public Type getType() { return Type.FIXED_INTS_8; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40DocValuesProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40DocValuesProducer.java index 67521f64e62..aea22072bb7 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40DocValuesProducer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40DocValuesProducer.java @@ -24,19 +24,24 @@ import java.util.Collection; import java.util.Map; import java.util.TreeMap; -import org.apache.lucene.codecs.lucene40.values.DocValuesReaderBase; +import org.apache.lucene.codecs.PerDocProducerBase; +import org.apache.lucene.codecs.lucene40.values.Bytes; +import org.apache.lucene.codecs.lucene40.values.Floats; +import org.apache.lucene.codecs.lucene40.values.Ints; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.IndexFileNames; import org.apache.lucene.index.SegmentReadState; import org.apache.lucene.store.CompoundFileDirectory; import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; import org.apache.lucene.util.IOUtils; /** * Default PerDocProducer implementation that uses compound file. * @lucene.experimental */ -public class Lucene40DocValuesProducer extends DocValuesReaderBase { +public class Lucene40DocValuesProducer extends PerDocProducerBase { protected final TreeMap docValues; private final Directory cfs; /** @@ -71,4 +76,35 @@ public class Lucene40DocValuesProducer extends DocValuesReaderBase { IOUtils.close(closeables); } } + + @Override + protected DocValues loadDocValues(int docCount, Directory dir, String id, + Type type, IOContext context) throws IOException { + switch (type) { + case FIXED_INTS_16: + case FIXED_INTS_32: + case FIXED_INTS_64: + case FIXED_INTS_8: + case VAR_INTS: + return Ints.getValues(dir, id, docCount, type, context); + case FLOAT_32: + return Floats.getValues(dir, id, docCount, context, type); + case FLOAT_64: + return Floats.getValues(dir, id, docCount, context, type); + case BYTES_FIXED_STRAIGHT: + return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, true, docCount, getComparator(), context); + case BYTES_FIXED_DEREF: + return Bytes.getValues(dir, id, Bytes.Mode.DEREF, true, docCount, getComparator(), context); + case BYTES_FIXED_SORTED: + return Bytes.getValues(dir, id, Bytes.Mode.SORTED, true, docCount, getComparator(), context); + case BYTES_VAR_STRAIGHT: + return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, false, docCount, getComparator(), context); + case BYTES_VAR_DEREF: + return Bytes.getValues(dir, id, Bytes.Mode.DEREF, false, docCount, getComparator(), context); + case BYTES_VAR_SORTED: + return Bytes.getValues(dir, id, Bytes.Mode.SORTED, false, docCount, getComparator(), context); + default: + throw new IllegalStateException("unrecognized index values mode " + type); + } + } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40NormsFormat.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40NormsFormat.java index 8bf729eb41c..a085a4840d0 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40NormsFormat.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40NormsFormat.java @@ -62,7 +62,7 @@ public class Lucene40NormsFormat extends NormsFormat { @Override protected boolean canLoad(FieldInfo info) { - return info.normsPresent(); + return info.hasNorms(); } @Override @@ -92,7 +92,7 @@ public class Lucene40NormsFormat extends NormsFormat { @Override protected boolean canMerge(FieldInfo info) { - return info.normsPresent(); + return info.hasNorms(); } @Override @@ -104,7 +104,7 @@ public class Lucene40NormsFormat extends NormsFormat { final String normsFileName = IndexFileNames.segmentFileName(segmentInfo.name, NORMS_SEGMENT_SUFFIX, IndexFileNames.COMPOUND_FILE_EXTENSION); FieldInfos fieldInfos = segmentInfo.getFieldInfos(); for (FieldInfo fieldInfo : fieldInfos) { - if (fieldInfo.normsPresent()) { + if (fieldInfo.hasNorms()) { final String normsEntriesFileName = IndexFileNames.segmentFileName(segmentInfo.name, NORMS_SEGMENT_SUFFIX, IndexFileNames.COMPOUND_FILE_ENTRIES_EXTENSION); files.add(normsFileName); files.add(normsEntriesFileName); diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Bytes.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Bytes.java index 06bf58e5e40..4ab1bd9e4b9 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Bytes.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Bytes.java @@ -23,7 +23,6 @@ import java.util.Comparator; import java.util.concurrent.atomic.AtomicLong; import org.apache.lucene.codecs.DocValuesConsumer; -import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues.SortedSource; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; @@ -64,7 +63,7 @@ import org.apache.lucene.util.packed.PackedInts; * * @lucene.experimental */ -final class Bytes { +public final class Bytes { static final String DV_SEGMENT_SUFFIX = "dv"; @@ -242,8 +241,8 @@ final class Bytes { private final IOContext context; protected BytesWriterBase(Directory dir, String id, String codecName, - int version, Counter bytesUsed, IOContext context) throws IOException { - super(bytesUsed); + int version, Counter bytesUsed, IOContext context, Type type) throws IOException { + super(bytesUsed, type); this.id = id; this.dir = dir; this.codecName = codecName; @@ -292,25 +291,11 @@ final class Bytes { } return idxOut; } - /** - * Must be called only with increasing docIDs. It's OK for some docIDs to be - * skipped; they will be filled with 0 bytes. - */ - protected - abstract void add(int docID, BytesRef bytes) throws IOException; + @Override public abstract void finish(int docCount) throws IOException; - @Override - protected void mergeDoc(Field scratchField, Source source, int docID, int sourceDoc) throws IOException { - add(docID, source.getBytes(sourceDoc, bytesRef)); - } - - @Override - public void add(int docID, IndexableField docValue) throws IOException { - add(docID, docValue.binaryValue()); - } } /** @@ -378,7 +363,7 @@ final class Bytes { } @Override - public Type type() { + public Type getType() { return type; } @@ -393,22 +378,22 @@ final class Bytes { protected long maxBytes = 0; protected DerefBytesWriterBase(Directory dir, String id, String codecName, - int codecVersion, Counter bytesUsed, IOContext context) + int codecVersion, Counter bytesUsed, IOContext context, Type type) throws IOException { this(dir, id, codecName, codecVersion, new DirectTrackingAllocator( - ByteBlockPool.BYTE_BLOCK_SIZE, bytesUsed), bytesUsed, context, false); + ByteBlockPool.BYTE_BLOCK_SIZE, bytesUsed), bytesUsed, context, false, type); } protected DerefBytesWriterBase(Directory dir, String id, String codecName, - int codecVersion, Counter bytesUsed, IOContext context, boolean fasterButMoreRam) + int codecVersion, Counter bytesUsed, IOContext context, boolean fasterButMoreRam, Type type) throws IOException { this(dir, id, codecName, codecVersion, new DirectTrackingAllocator( - ByteBlockPool.BYTE_BLOCK_SIZE, bytesUsed), bytesUsed, context, fasterButMoreRam); + ByteBlockPool.BYTE_BLOCK_SIZE, bytesUsed), bytesUsed, context, fasterButMoreRam,type); } protected DerefBytesWriterBase(Directory dir, String id, String codecName, int codecVersion, Allocator allocator, - Counter bytesUsed, IOContext context, boolean fasterButMoreRam) throws IOException { - super(dir, id, codecName, codecVersion, bytesUsed, context); + Counter bytesUsed, IOContext context, boolean fasterButMoreRam, Type type) throws IOException { + super(dir, id, codecName, codecVersion, bytesUsed, context, type); hash = new BytesRefHash(new ByteBlockPool(allocator), BytesRefHash.DEFAULT_CAPACITY, new TrackingDirectBytesStartArray( BytesRefHash.DEFAULT_CAPACITY, bytesUsed)); @@ -430,7 +415,9 @@ final class Bytes { } @Override - protected void add(int docID, BytesRef bytes) throws IOException { + public void add(int docID, IndexableField value) throws IOException { + BytesRef bytes = value.binaryValue(); + assert bytes != null; if (bytes.length == 0) { // default value - skip it return; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/BytesRefUtils.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/BytesRefUtils.java deleted file mode 100644 index 4d4e7eae957..00000000000 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/BytesRefUtils.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.apache.lucene.codecs.lucene40.values; - -/** - * 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 org.apache.lucene.util.BytesRef; - -/** - * Package private BytesRefUtils - can move this into the o.a.l.utils package if - * needed. - * - * @lucene.internal - */ -final class BytesRefUtils { - - private BytesRefUtils() { - } - - /** - * Copies the given long value and encodes it as 8 byte Big-Endian. - *

- * NOTE: this method resets the offset to 0, length to 8 and resizes the - * reference array if needed. - */ - public static void copyLong(BytesRef ref, long value) { - if (ref.bytes.length < 8) { - ref.bytes = new byte[8]; - } - copyInternal(ref, (int) (value >> 32), ref.offset = 0); - copyInternal(ref, (int) value, 4); - ref.length = 8; - } - - /** - * Copies the given int value and encodes it as 4 byte Big-Endian. - *

- * NOTE: this method resets the offset to 0, length to 4 and resizes the - * reference array if needed. - */ - public static void copyInt(BytesRef ref, int value) { - if (ref.bytes.length < 4) { - ref.bytes = new byte[4]; - } - copyInternal(ref, value, ref.offset = 0); - ref.length = 4; - } - - /** - * Copies the given short value and encodes it as a 2 byte Big-Endian. - *

- * NOTE: this method resets the offset to 0, length to 2 and resizes the - * reference array if needed. - */ - public static void copyShort(BytesRef ref, short value) { - if (ref.bytes.length < 2) { - ref.bytes = new byte[2]; - } - ref.bytes[ref.offset] = (byte) (value >> 8); - ref.bytes[ref.offset + 1] = (byte) (value); - ref.length = 2; - } - - private static void copyInternal(BytesRef ref, int value, int startOffset) { - ref.bytes[startOffset] = (byte) (value >> 24); - ref.bytes[startOffset + 1] = (byte) (value >> 16); - ref.bytes[startOffset + 2] = (byte) (value >> 8); - ref.bytes[startOffset + 3] = (byte) (value); - } - - /** - * Converts 2 consecutive bytes from the current offset to a short. Bytes are - * interpreted as Big-Endian (most significant bit first) - *

- * NOTE: this method does NOT check the bounds of the referenced array. - */ - public static short asShort(BytesRef b) { - return (short) (0xFFFF & ((b.bytes[b.offset] & 0xFF) << 8) | (b.bytes[b.offset + 1] & 0xFF)); - } - - /** - * Converts 4 consecutive bytes from the current offset to an int. Bytes are - * interpreted as Big-Endian (most significant bit first) - *

- * NOTE: this method does NOT check the bounds of the referenced array. - */ - public static int asInt(BytesRef b) { - return asIntInternal(b, b.offset); - } - - /** - * Converts 8 consecutive bytes from the current offset to a long. Bytes are - * interpreted as Big-Endian (most significant bit first) - *

- * NOTE: this method does NOT check the bounds of the referenced array. - */ - public static long asLong(BytesRef b) { - return (((long) asIntInternal(b, b.offset) << 32) | asIntInternal(b, - b.offset + 4) & 0xFFFFFFFFL); - } - - private static int asIntInternal(BytesRef b, int pos) { - return ((b.bytes[pos++] & 0xFF) << 24) | ((b.bytes[pos++] & 0xFF) << 16) - | ((b.bytes[pos++] & 0xFF) << 8) | (b.bytes[pos] & 0xFF); - } - -} diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesArray.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesArray.java deleted file mode 100644 index 668f094f2cb..00000000000 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesArray.java +++ /dev/null @@ -1,306 +0,0 @@ -package org.apache.lucene.codecs.lucene40.values; - -import java.io.IOException; -import java.util.Collections; -import java.util.EnumMap; -import java.util.Map; - -import org.apache.lucene.index.DocValues.Source; -import org.apache.lucene.index.DocValues.Type; -import org.apache.lucene.store.IndexInput; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.RamUsageEstimator; - -/** - * 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. - */ - -/** - * @lucene.experimental - */ -abstract class DocValuesArray extends Source { - - static final Map TEMPLATES; - - static { - EnumMap templates = new EnumMap( - Type.class); - templates.put(Type.FIXED_INTS_16, new ShortValues()); - templates.put(Type.FIXED_INTS_32, new IntValues()); - templates.put(Type.FIXED_INTS_64, new LongValues()); - templates.put(Type.FIXED_INTS_8, new ByteValues()); - templates.put(Type.FLOAT_32, new FloatValues()); - templates.put(Type.FLOAT_64, new DoubleValues()); - TEMPLATES = Collections.unmodifiableMap(templates); - } - - protected final int bytesPerValue; - - DocValuesArray(int bytesPerValue, Type type) { - super(type); - this.bytesPerValue = bytesPerValue; - } - - public abstract DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException; - - @Override - public final boolean hasArray() { - return true; - } - - void toBytes(long value, BytesRef bytesRef) { - BytesRefUtils.copyLong(bytesRef, value); - } - - void toBytes(double value, BytesRef bytesRef) { - BytesRefUtils.copyLong(bytesRef, Double.doubleToRawLongBits(value)); - } - - final static class ByteValues extends DocValuesArray { - private final byte[] values; - - ByteValues() { - super(1, Type.FIXED_INTS_8); - values = new byte[0]; - } - - private ByteValues(IndexInput input, int numDocs) throws IOException { - super(1, Type.FIXED_INTS_8); - values = new byte[numDocs]; - input.readBytes(values, 0, values.length, false); - } - - @Override - public byte[] getArray() { - return values; - } - - @Override - public long getInt(int docID) { - assert docID >= 0 && docID < values.length; - return values[docID]; - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new ByteValues(input, numDocs); - } - - void toBytes(long value, BytesRef bytesRef) { - bytesRef.bytes[0] = (byte) (0xFFL & value); - } - - }; - - final static class ShortValues extends DocValuesArray { - private final short[] values; - - ShortValues() { - super(RamUsageEstimator.NUM_BYTES_SHORT, Type.FIXED_INTS_16); - values = new short[0]; - } - - private ShortValues(IndexInput input, int numDocs) throws IOException { - super(RamUsageEstimator.NUM_BYTES_SHORT, Type.FIXED_INTS_16); - values = new short[numDocs]; - for (int i = 0; i < values.length; i++) { - values[i] = input.readShort(); - } - } - - @Override - public short[] getArray() { - return values; - } - - @Override - public long getInt(int docID) { - assert docID >= 0 && docID < values.length; - return values[docID]; - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new ShortValues(input, numDocs); - } - - void toBytes(long value, BytesRef bytesRef) { - BytesRefUtils.copyShort(bytesRef, (short) (0xFFFFL & value)); - } - - }; - - final static class IntValues extends DocValuesArray { - private final int[] values; - - IntValues() { - super(RamUsageEstimator.NUM_BYTES_INT, Type.FIXED_INTS_32); - values = new int[0]; - } - - private IntValues(IndexInput input, int numDocs) throws IOException { - super(RamUsageEstimator.NUM_BYTES_INT, Type.FIXED_INTS_32); - values = new int[numDocs]; - for (int i = 0; i < values.length; i++) { - values[i] = input.readInt(); - } - } - - @Override - public int[] getArray() { - return values; - } - - @Override - public long getInt(int docID) { - assert docID >= 0 && docID < values.length; - return 0xFFFFFFFF & values[docID]; - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new IntValues(input, numDocs); - } - - void toBytes(long value, BytesRef bytesRef) { - BytesRefUtils.copyInt(bytesRef, (int) (0xFFFFFFFF & value)); - } - - }; - - final static class LongValues extends DocValuesArray { - private final long[] values; - - LongValues() { - super(RamUsageEstimator.NUM_BYTES_LONG, Type.FIXED_INTS_64); - values = new long[0]; - } - - private LongValues(IndexInput input, int numDocs) throws IOException { - super(RamUsageEstimator.NUM_BYTES_LONG, Type.FIXED_INTS_64); - values = new long[numDocs]; - for (int i = 0; i < values.length; i++) { - values[i] = input.readLong(); - } - } - - @Override - public long[] getArray() { - return values; - } - - @Override - public long getInt(int docID) { - assert docID >= 0 && docID < values.length; - return values[docID]; - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new LongValues(input, numDocs); - } - - }; - - final static class FloatValues extends DocValuesArray { - private final float[] values; - - FloatValues() { - super(RamUsageEstimator.NUM_BYTES_FLOAT, Type.FLOAT_32); - values = new float[0]; - } - - private FloatValues(IndexInput input, int numDocs) throws IOException { - super(RamUsageEstimator.NUM_BYTES_FLOAT, Type.FLOAT_32); - values = new float[numDocs]; - /* - * we always read BIG_ENDIAN here since the writer serialized plain bytes - * we can simply read the ints / longs back in using readInt / readLong - */ - for (int i = 0; i < values.length; i++) { - values[i] = Float.intBitsToFloat(input.readInt()); - } - } - - @Override - public float[] getArray() { - return values; - } - - @Override - public double getFloat(int docID) { - assert docID >= 0 && docID < values.length; - return values[docID]; - } - - @Override - void toBytes(double value, BytesRef bytesRef) { - BytesRefUtils.copyInt(bytesRef, Float.floatToRawIntBits((float)value)); - - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new FloatValues(input, numDocs); - } - }; - - final static class DoubleValues extends DocValuesArray { - private final double[] values; - - DoubleValues() { - super(RamUsageEstimator.NUM_BYTES_DOUBLE, Type.FLOAT_64); - values = new double[0]; - } - - private DoubleValues(IndexInput input, int numDocs) throws IOException { - super(RamUsageEstimator.NUM_BYTES_DOUBLE, Type.FLOAT_64); - values = new double[numDocs]; - /* - * we always read BIG_ENDIAN here since the writer serialized plain bytes - * we can simply read the ints / longs back in using readInt / readLong - */ - for (int i = 0; i < values.length; i++) { - values[i] = Double.longBitsToDouble(input.readLong()); - } - } - - @Override - public double[] getArray() { - return values; - } - - @Override - public double getFloat(int docID) { - assert docID >= 0 && docID < values.length; - return values[docID]; - } - - @Override - public DocValuesArray newFromInput(IndexInput input, int numDocs) - throws IOException { - return new DoubleValues(input, numDocs); - } - - }; - -} diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesWriterBase.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesWriterBase.java index 7b8483f2c37..aeeb23083e5 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesWriterBase.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/DocValuesWriterBase.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.Comparator; import org.apache.lucene.codecs.DocValuesConsumer; +import org.apache.lucene.codecs.PerDocProducerBase; import org.apache.lucene.codecs.PerDocConsumer; import org.apache.lucene.codecs.lucene40.values.Writer; import org.apache.lucene.index.FieldInfo; @@ -81,14 +82,10 @@ public abstract class DocValuesWriterBase extends PerDocConsumer { @Override public DocValuesConsumer addValuesField(Type valueType, FieldInfo field) throws IOException { return Writer.create(valueType, - docValuesId(segmentName, field.number), + PerDocProducerBase.docValuesId(segmentName, field.number), getDirectory(), getComparator(), bytesUsed, context, fasterButMoreRam); } - public static String docValuesId(String segmentsName, int fieldId) { - return segmentsName + "_" + fieldId; - } - public Comparator getComparator() throws IOException { return BytesRef.getUTF8SortedAsUnicodeComparator(); diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedDerefBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedDerefBytesImpl.java index 72efc15a084..7c745b93b0c 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedDerefBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedDerefBytesImpl.java @@ -46,7 +46,7 @@ class FixedDerefBytesImpl { public static class Writer extends DerefBytesWriterBase { public Writer(Directory dir, String id, Counter bytesUsed, IOContext context) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, Type.BYTES_FIXED_DEREF); } @Override @@ -84,7 +84,7 @@ class FixedDerefBytesImpl { @Override public Source getDirectSource() throws IOException { - return new DirectFixedDerefSource(cloneData(), cloneIndex(), size, type()); + return new DirectFixedDerefSource(cloneData(), cloneIndex(), size, getType()); } @Override diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedSortedBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedSortedBytesImpl.java index 7e12c9c97cf..2ab1700421b 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedSortedBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedSortedBytesImpl.java @@ -58,7 +58,7 @@ class FixedSortedBytesImpl { public Writer(Directory dir, String id, Comparator comp, Counter bytesUsed, IOContext context, boolean fasterButMoreRam) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, fasterButMoreRam); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, fasterButMoreRam, Type.BYTES_FIXED_SORTED); this.comp = comp; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedStraightBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedStraightBytesImpl.java index 1b0c2e7a896..4ea4b46e257 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedStraightBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/FixedStraightBytesImpl.java @@ -22,10 +22,12 @@ import java.io.IOException; import org.apache.lucene.codecs.lucene40.values.Bytes.BytesReaderBase; import org.apache.lucene.codecs.lucene40.values.Bytes.BytesSourceBase; import org.apache.lucene.codecs.lucene40.values.Bytes.BytesWriterBase; +import org.apache.lucene.document.DocValuesField; import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; @@ -52,6 +54,7 @@ class FixedStraightBytesImpl { static final int VERSION_CURRENT = VERSION_START; static abstract class FixedBytesWriterBase extends BytesWriterBase { + protected final DocValuesField bytesSpareField = new DocValuesField("", new BytesRef(), Type.BYTES_FIXED_STRAIGHT); protected int lastDocID = -1; // start at -1 if the first added value is > 0 protected int size = -1; @@ -60,13 +63,20 @@ class FixedStraightBytesImpl { protected FixedBytesWriterBase(Directory dir, String id, String codecName, int version, Counter bytesUsed, IOContext context) throws IOException { - super(dir, id, codecName, version, bytesUsed, context); + this(dir, id, codecName, version, bytesUsed, context, Type.BYTES_FIXED_STRAIGHT); + } + + protected FixedBytesWriterBase(Directory dir, String id, String codecName, + int version, Counter bytesUsed, IOContext context, Type type) throws IOException { + super(dir, id, codecName, version, bytesUsed, context, type); pool = new ByteBlockPool(new DirectTrackingAllocator(bytesUsed)); pool.nextBuffer(); } @Override - protected void add(int docID, BytesRef bytes) throws IOException { + public void add(int docID, IndexableField value) throws IOException { + final BytesRef bytes = value.binaryValue(); + assert bytes != null; assert lastDocID < docID; if (size == -1) { @@ -277,7 +287,7 @@ class FixedStraightBytesImpl { @Override public Source getDirectSource() throws IOException { - return new DirectFixedStraightSource(cloneData(), size, type()); + return new DirectFixedStraightSource(cloneData(), size, getType()); } @Override diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Floats.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Floats.java index 8a2d073c9c8..4d3e0548e7d 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Floats.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Floats.java @@ -18,6 +18,7 @@ package org.apache.lucene.codecs.lucene40.values; */ import java.io.IOException; +import org.apache.lucene.codecs.DocValuesArraySource; import org.apache.lucene.codecs.DocValuesConsumer; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; @@ -39,7 +40,7 @@ import org.apache.lucene.util.IOUtils; * * @lucene.experimental */ -class Floats { +public class Floats { protected static final String CODEC_NAME = "Floats"; protected static final int VERSION_START = 0; @@ -69,31 +70,28 @@ class Floats { final static class FloatsWriter extends FixedStraightBytesImpl.Writer { private final int size; - private final DocValuesArray template; + private final DocValuesArraySource template; public FloatsWriter(Directory dir, String id, Counter bytesUsed, IOContext context, Type type) throws IOException { super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context); size = typeToSize(type); this.bytesRef = new BytesRef(size); bytesRef.length = size; - template = DocValuesArray.TEMPLATES.get(type); + template = DocValuesArraySource.forType(type); assert template != null; } - protected void add(int docID, double v) throws IOException { - template.toBytes(v, bytesRef); - add(docID, bytesRef); - } - - @Override - public void add(int docID, IndexableField docValue) throws IOException { - add(docID, docValue.numericValue().doubleValue()); - } - @Override protected boolean tryBulkMerge(DocValues docValues) { // only bulk merge if value type is the same otherwise size differs - return super.tryBulkMerge(docValues) && docValues.type() == template.type(); + return super.tryBulkMerge(docValues) && docValues.getType() == template.getType(); + } + + @Override + public void add(int docID, IndexableField value) throws IOException { + template.toBytes(value.numericValue().doubleValue(), bytesRef); + bytesSpareField.setBytesValue(bytesRef); + super.add(docID, bytesSpareField); } @Override @@ -104,11 +102,11 @@ class Floats { } final static class FloatsReader extends FixedStraightBytesImpl.FixedStraightReader { - final DocValuesArray arrayTemplate; + final DocValuesArraySource arrayTemplate; FloatsReader(Directory dir, String id, int maxDoc, IOContext context, Type type) throws IOException { super(dir, id, CODEC_NAME, VERSION_CURRENT, maxDoc, context, type); - arrayTemplate = DocValuesArray.TEMPLATES.get(type); + arrayTemplate = DocValuesArraySource.forType(type); assert size == 4 || size == 8: "wrong size=" + size + " type=" + type + " id=" + id; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Ints.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Ints.java index a9ba6c3e069..22875ad5a73 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Ints.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Ints.java @@ -19,6 +19,7 @@ package org.apache.lucene.codecs.lucene40.values; import java.io.IOException; +import org.apache.lucene.codecs.DocValuesArraySource; import org.apache.lucene.codecs.DocValuesConsumer; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; @@ -36,7 +37,7 @@ import org.apache.lucene.util.IOUtils; * * @lucene.experimental */ -final class Ints { +public final class Ints { protected static final String CODEC_NAME = "Ints"; protected static final int VERSION_START = 0; protected static final int VERSION_CURRENT = VERSION_START; @@ -88,7 +89,7 @@ final class Ints { static class IntsWriter extends FixedStraightBytesImpl.Writer { - private final DocValuesArray template; + private final DocValuesArraySource template; public IntsWriter(Directory dir, String id, Counter bytesUsed, IOContext context, Type valueType) throws IOException { @@ -101,17 +102,7 @@ final class Ints { size = typeToSize(valueType); this.bytesRef = new BytesRef(size); bytesRef.length = size; - template = DocValuesArray.TEMPLATES.get(valueType); - } - - protected void add(int docID, long v) throws IOException { - template.toBytes(v, bytesRef); - add(docID, bytesRef); - } - - @Override - public void add(int docID, IndexableField docValue) throws IOException { - add(docID, docValue.numericValue().longValue()); + template = DocValuesArraySource.forType(valueType); } @Override @@ -120,21 +111,28 @@ final class Ints { template.toBytes(value, bytesRef); } + @Override + public void add(int docID, IndexableField value) throws IOException { + template.toBytes(value.numericValue().longValue(), bytesRef); + bytesSpareField.setBytesValue(bytesRef); + super.add(docID, bytesSpareField); + } + @Override protected boolean tryBulkMerge(DocValues docValues) { // only bulk merge if value type is the same otherwise size differs - return super.tryBulkMerge(docValues) && docValues.type() == template.type(); + return super.tryBulkMerge(docValues) && docValues.getType() == template.getType(); } } final static class IntsReader extends FixedStraightBytesImpl.FixedStraightReader { - private final DocValuesArray arrayTemplate; + private final DocValuesArraySource arrayTemplate; IntsReader(Directory dir, String id, int maxDoc, IOContext context, Type type) throws IOException { super(dir, id, CODEC_NAME, VERSION_CURRENT, maxDoc, context, type); - arrayTemplate = DocValuesArray.TEMPLATES.get(type); + arrayTemplate = DocValuesArraySource.forType(type); assert arrayTemplate != null; assert type == sizeToType(size); } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/PackedIntValues.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/PackedIntValues.java index eb2acf70b05..ffa46d799bf 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/PackedIntValues.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/PackedIntValues.java @@ -18,9 +18,8 @@ package org.apache.lucene.codecs.lucene40.values; */ import java.io.IOException; -import org.apache.lucene.codecs.lucene40.values.DocValuesArray.LongValues; +import org.apache.lucene.codecs.DocValuesArraySource; import org.apache.lucene.codecs.lucene40.values.FixedStraightBytesImpl.FixedBytesWriterBase; -import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.DocValues; @@ -59,27 +58,10 @@ class PackedIntValues { protected PackedIntsWriter(Directory dir, String id, Counter bytesUsed, IOContext context) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, Type.VAR_INTS); bytesRef = new BytesRef(8); } - - protected void add(int docID, long v) throws IOException { - assert lastDocId < docID; - if (!started) { - started = true; - minValue = maxValue = v; - } else { - if (v < minValue) { - minValue = v; - } else if (v > maxValue) { - maxValue = v; - } - } - lastDocId = docID; - BytesRefUtils.copyLong(bytesRef, v); - add(docID, bytesRef); - } - + @Override public void finish(int docCount) throws IOException { boolean success = false; @@ -112,13 +94,6 @@ class PackedIntValues { } } - @Override - protected void mergeDoc(Field scratchField, Source source, int docID, int sourceDoc) throws IOException { - assert docID > lastDocId : "docID: " + docID - + " must be greater than the last added doc id: " + lastDocId; - add(docID, source.getInt(sourceDoc)); - } - private void writePackedInts(IndexOutput datOut, int docCount) throws IOException { datOut.writeLong(minValue); @@ -149,10 +124,25 @@ class PackedIntValues { } w.finish(); } - + @Override public void add(int docID, IndexableField docValue) throws IOException { - add(docID, docValue.numericValue().longValue()); + final long v = docValue.numericValue().longValue(); + assert lastDocId < docID; + if (!started) { + started = true; + minValue = maxValue = v; + } else { + if (v < minValue) { + minValue = v; + } else if (v > maxValue) { + maxValue = v; + } + } + lastDocId = docID; + DocValuesArraySource.copyLong(bytesRef, v); + bytesSpareField.setBytesValue(bytesRef); + super.add(docID, bytesSpareField); } } @@ -164,7 +154,7 @@ class PackedIntValues { private final IndexInput datIn; private final byte type; private final int numDocs; - private final LongValues values; + private final DocValuesArraySource values; protected PackedIntsReader(Directory dir, String id, int numDocs, IOContext context) throws IOException { @@ -176,7 +166,7 @@ class PackedIntValues { try { CodecUtil.checkHeader(datIn, CODEC_NAME, VERSION_START, VERSION_START); type = datIn.readByte(); - values = type == FIXED_64 ? new LongValues() : null; + values = type == FIXED_64 ? DocValuesArraySource.forType(Type.FIXED_INTS_64) : null; success = true; } finally { if (!success) { @@ -220,7 +210,7 @@ class PackedIntValues { @Override - public Type type() { + public Type getType() { return Type.VAR_INTS; } @@ -247,7 +237,7 @@ class PackedIntValues { @Override public BytesRef getBytes(int docID, BytesRef ref) { ref.grow(8); - BytesRefUtils.copyLong(ref, getInt(docID)); + DocValuesArraySource.copyLong(ref, getInt(docID)); return ref; } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarDerefBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarDerefBytesImpl.java index 19a7bd71bb6..43bff795af4 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarDerefBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarDerefBytesImpl.java @@ -57,7 +57,7 @@ class VarDerefBytesImpl { static class Writer extends DerefBytesWriterBase { public Writer(Directory dir, String id, Counter bytesUsed, IOContext context) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, Type.BYTES_VAR_DEREF); size = 0; } @@ -105,7 +105,7 @@ class VarDerefBytesImpl { @Override public Source getDirectSource() throws IOException { - return new DirectVarDerefSource(cloneData(), cloneIndex(), type()); + return new DirectVarDerefSource(cloneData(), cloneIndex(), getType()); } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarSortedBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarSortedBytesImpl.java index 0229199f0c8..9a8e87dcaf6 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarSortedBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarSortedBytesImpl.java @@ -59,7 +59,7 @@ final class VarSortedBytesImpl { public Writer(Directory dir, String id, Comparator comp, Counter bytesUsed, IOContext context, boolean fasterButMoreRam) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, fasterButMoreRam); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, fasterButMoreRam, Type.BYTES_VAR_SORTED); this.comp = comp; size = 0; } @@ -166,7 +166,7 @@ final class VarSortedBytesImpl { @Override public Source getDirectSource() throws IOException { - return new DirectSortedSource(cloneData(), cloneIndex(), comparator, type()); + return new DirectSortedSource(cloneData(), cloneIndex(), comparator, getType()); } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarStraightBytesImpl.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarStraightBytesImpl.java index 2902801c259..cfb9d78cfb6 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarStraightBytesImpl.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/VarStraightBytesImpl.java @@ -26,6 +26,7 @@ import org.apache.lucene.document.Field; import org.apache.lucene.index.DocValues.Source; import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.IndexableField; import org.apache.lucene.store.Directory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexInput; @@ -63,7 +64,7 @@ class VarStraightBytesImpl { private boolean merge = false; public Writer(Directory dir, String id, Counter bytesUsed, IOContext context) throws IOException { - super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context); + super(dir, id, CODEC_NAME, VERSION_CURRENT, bytesUsed, context, Type.BYTES_VAR_STRAIGHT); pool = new ByteBlockPool(new DirectTrackingAllocator(bytesUsed)); docToAddress = new long[1]; pool.nextBuffer(); // init @@ -84,7 +85,9 @@ class VarStraightBytesImpl { } @Override - protected void add(int docID, BytesRef bytes) throws IOException { + public void add(int docID, IndexableField value) throws IOException { + final BytesRef bytes = value.binaryValue(); + assert bytes != null; assert !merge; if (bytes.length == 0) { return; // default @@ -245,7 +248,7 @@ class VarStraightBytesImpl { @Override public Source getDirectSource() throws IOException { - return new DirectVarStraightSource(cloneData(), cloneIndex(), type()); + return new DirectVarStraightSource(cloneData(), cloneIndex(), getType()); } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Writer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Writer.java index 13529d3d487..77c317ca816 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Writer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/values/Writer.java @@ -40,6 +40,7 @@ import org.apache.lucene.util.Counter; */ abstract class Writer extends DocValuesConsumer { protected final Counter bytesUsed; + protected Type type; /** * Creates a new {@link Writer}. @@ -49,9 +50,19 @@ abstract class Writer extends DocValuesConsumer { * internally allocated memory. All tracked bytes must be released * once {@link #finish(int)} has been called. */ - protected Writer(Counter bytesUsed) { + protected Writer(Counter bytesUsed, Type type) { this.bytesUsed = bytesUsed; + this.type = type; } + + + + @Override + protected Type getType() { + return type; + } + + /** * Factory method to create a {@link Writer} instance for a given type. This diff --git a/lucene/core/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java b/lucene/core/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java index 7794e6dfc3c..89d8bb359f1 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java @@ -98,8 +98,6 @@ public class MemoryPostingsFormat extends PostingsFormat { return "PostingsFormat(name=" + getName() + " doPackFST= " + doPackFST + ")"; } - private static final boolean VERBOSE = false; - private final static class TermsWriter extends TermsConsumer { private final IndexOutput out; private final FieldInfo field; @@ -123,10 +121,13 @@ public class MemoryPostingsFormat extends PostingsFormat { // NOTE: not private so we don't pay access check at runtime: int docCount; RAMOutputStream buffer = new RAMOutputStream(); + + int lastOffsetLength; + int lastOffset; @Override public void startDoc(int docID, int termDocFreq) throws IOException { - if (VERBOSE) System.out.println(" startDoc docID=" + docID + " freq=" + termDocFreq); + //System.out.println(" startDoc docID=" + docID + " freq=" + termDocFreq); final int delta = docID - lastDocID; assert docID == 0 || delta > 0; lastDocID = docID; @@ -143,20 +144,23 @@ public class MemoryPostingsFormat extends PostingsFormat { } lastPos = 0; + lastOffset = 0; } @Override public void addPosition(int pos, BytesRef payload, int startOffset, int endOffset) throws IOException { assert payload == null || field.storePayloads; - if (VERBOSE) System.out.println(" addPos pos=" + pos + " payload=" + payload); + //System.out.println(" addPos pos=" + pos + " payload=" + payload); final int delta = pos - lastPos; assert delta >= 0; lastPos = pos; + int payloadLen = 0; + if (field.storePayloads) { - final int payloadLen = payload == null ? 0 : payload.length; + payloadLen = payload == null ? 0 : payload.length; if (payloadLen != lastPayloadLen) { lastPayloadLen = payloadLen; buffer.writeVInt((delta<<1)|1); @@ -164,13 +168,28 @@ public class MemoryPostingsFormat extends PostingsFormat { } else { buffer.writeVInt(delta<<1); } - - if (payloadLen > 0) { - buffer.writeBytes(payload.bytes, payload.offset, payloadLen); - } } else { buffer.writeVInt(delta); } + + if (field.indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0) { + // don't use startOffset - lastEndOffset, because this creates lots of negative vints for synonyms, + // and the numbers aren't that much smaller anyways. + int offsetDelta = startOffset - lastOffset; + int offsetLength = endOffset - startOffset; + if (offsetLength != lastOffsetLength) { + buffer.writeVInt(offsetDelta << 1 | 1); + buffer.writeVInt(offsetLength); + } else { + buffer.writeVInt(offsetDelta << 1); + } + lastOffset = startOffset; + lastOffsetLength = offsetLength; + } + + if (payloadLen > 0) { + buffer.writeBytes(payload.bytes, payload.offset, payloadLen); + } } @Override @@ -182,6 +201,8 @@ public class MemoryPostingsFormat extends PostingsFormat { lastDocID = 0; docCount = 0; lastPayloadLen = 0; + // force first offset to write its length + lastOffsetLength = -1; return this; } } @@ -190,7 +211,7 @@ public class MemoryPostingsFormat extends PostingsFormat { @Override public PostingsConsumer startTerm(BytesRef text) { - if (VERBOSE) System.out.println(" startTerm term=" + text.utf8ToString()); + //System.out.println(" startTerm term=" + text.utf8ToString()); return postingsWriter.reset(); } @@ -224,12 +245,12 @@ public class MemoryPostingsFormat extends PostingsFormat { spare.bytes = finalBuffer; spare.length = totalBytes; - if (VERBOSE) { - System.out.println(" finishTerm term=" + text.utf8ToString() + " " + totalBytes + " bytes totalTF=" + stats.totalTermFreq); - for(int i=0;i>> 1; - if (VERBOSE) System.out.println(" docID=" + accum + " code=" + code); + //System.out.println(" docID=" + accum + " code=" + code); if ((code & 1) != 0) { freq = 1; } else { @@ -352,8 +368,8 @@ public class MemoryPostingsFormat extends PostingsFormat { assert freq > 0; } - if (indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0) { - // Skip positions + if (indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) { + // Skip positions/payloads for(int posUpto=0;posUpto= 0; + if (needsOffsets && !hasOffsets) { + return null; // not available } if (field.indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) { @@ -665,14 +724,14 @@ public class MemoryPostingsFormat extends PostingsFormat { decodeMetaData(); FSTDocsAndPositionsEnum docsAndPositionsEnum; if (reuse == null || !(reuse instanceof FSTDocsAndPositionsEnum)) { - docsAndPositionsEnum = new FSTDocsAndPositionsEnum(field.storePayloads); + docsAndPositionsEnum = new FSTDocsAndPositionsEnum(field.storePayloads, hasOffsets); } else { docsAndPositionsEnum = (FSTDocsAndPositionsEnum) reuse; - if (!docsAndPositionsEnum.canReuse(field.storePayloads)) { - docsAndPositionsEnum = new FSTDocsAndPositionsEnum(field.storePayloads); + if (!docsAndPositionsEnum.canReuse(field.storePayloads, hasOffsets)) { + docsAndPositionsEnum = new FSTDocsAndPositionsEnum(field.storePayloads, hasOffsets); } } - if (VERBOSE) System.out.println("D&P reset this=" + this); + //System.out.println("D&P reset this=" + this); return docsAndPositionsEnum.reset(current.output, liveDocs, docFreq); } @@ -683,14 +742,14 @@ public class MemoryPostingsFormat extends PostingsFormat { @Override public BytesRef next() throws IOException { - if (VERBOSE) System.out.println("te.next"); + //System.out.println("te.next"); current = fstEnum.next(); if (current == null) { - if (VERBOSE) System.out.println(" END"); + //System.out.println(" END"); return null; } didDecode = false; - if (VERBOSE) System.out.println(" term=" + field.name + ":" + current.input.utf8ToString()); + //System.out.println(" term=" + field.name + ":" + current.input.utf8ToString()); return current.input; } @@ -794,9 +853,7 @@ public class MemoryPostingsFormat extends PostingsFormat { break; } final TermsReader termsReader = new TermsReader(state.fieldInfos, in, termCount); - if (VERBOSE) { - System.out.println("load field=" + termsReader.field.name); - } + // System.out.println("load field=" + termsReader.field.name); fields.put(termsReader.field.name, termsReader); } } finally { diff --git a/lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldPostingsFormat.java b/lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldPostingsFormat.java index f8e37ef9852..e4436835af2 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldPostingsFormat.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/perfield/PerFieldPostingsFormat.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; +import java.util.ServiceLoader; // javadocs import java.util.Set; import java.util.TreeMap; @@ -47,7 +48,14 @@ import org.apache.lucene.util.IOUtils; /** * Enables per field format support. - * + *

+ * Note, when extending this class, the name ({@link #getName}) is + * written into the index. In order for the field to be read, the + * name must resolve to your implementation via {@link #forName(String)}. + * This method uses Java's + * {@link ServiceLoader Service Provider Interface} to resolve format names. + *

+ * @see ServiceLoader * @lucene.experimental */ diff --git a/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesConsumer.java index 5a8472b9e48..b28b40e4581 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesConsumer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesConsumer.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.HashSet; import java.util.Set; +import org.apache.lucene.codecs.PerDocProducerBase; import org.apache.lucene.codecs.lucene40.values.DocValuesWriterBase; import org.apache.lucene.index.FieldInfo; import org.apache.lucene.index.FieldInfos; @@ -58,7 +59,7 @@ public class SepDocValuesConsumer extends DocValuesWriterBase { private static void files(Directory dir,FieldInfos fieldInfos, String segmentName, Set files) { for (FieldInfo fieldInfo : fieldInfos) { if (fieldInfo.hasDocValues()) { - String filename = docValuesId(segmentName, fieldInfo.number); + String filename = PerDocProducerBase.docValuesId(segmentName, fieldInfo.number); switch (fieldInfo.getDocValuesType()) { case BYTES_FIXED_DEREF: case BYTES_VAR_DEREF: diff --git a/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesProducer.java index 6cfb2f6b620..0406c26aa0f 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesProducer.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/sep/SepDocValuesProducer.java @@ -22,16 +22,22 @@ import java.util.Collection; import java.util.Map; import java.util.TreeMap; -import org.apache.lucene.codecs.lucene40.values.DocValuesReaderBase; +import org.apache.lucene.codecs.PerDocProducerBase; +import org.apache.lucene.codecs.lucene40.values.Bytes; +import org.apache.lucene.codecs.lucene40.values.Floats; +import org.apache.lucene.codecs.lucene40.values.Ints; import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.index.SegmentReadState; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; import org.apache.lucene.util.IOUtils; /** * Implementation of PerDocProducer that uses separate files. * @lucene.experimental */ -public class SepDocValuesProducer extends DocValuesReaderBase { +public class SepDocValuesProducer extends PerDocProducerBase { private final TreeMap docValues; /** @@ -51,4 +57,35 @@ public class SepDocValuesProducer extends DocValuesReaderBase { protected void closeInternal(Collection closeables) throws IOException { IOUtils.close(closeables); } + + @Override + protected DocValues loadDocValues(int docCount, Directory dir, String id, + Type type, IOContext context) throws IOException { + switch (type) { + case FIXED_INTS_16: + case FIXED_INTS_32: + case FIXED_INTS_64: + case FIXED_INTS_8: + case VAR_INTS: + return Ints.getValues(dir, id, docCount, type, context); + case FLOAT_32: + return Floats.getValues(dir, id, docCount, context, type); + case FLOAT_64: + return Floats.getValues(dir, id, docCount, context, type); + case BYTES_FIXED_STRAIGHT: + return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, true, docCount, getComparator(), context); + case BYTES_FIXED_DEREF: + return Bytes.getValues(dir, id, Bytes.Mode.DEREF, true, docCount, getComparator(), context); + case BYTES_FIXED_SORTED: + return Bytes.getValues(dir, id, Bytes.Mode.SORTED, true, docCount, getComparator(), context); + case BYTES_VAR_STRAIGHT: + return Bytes.getValues(dir, id, Bytes.Mode.STRAIGHT, false, docCount, getComparator(), context); + case BYTES_VAR_DEREF: + return Bytes.getValues(dir, id, Bytes.Mode.DEREF, false, docCount, getComparator(), context); + case BYTES_VAR_SORTED: + return Bytes.getValues(dir, id, Bytes.Mode.SORTED, false, docCount, getComparator(), context); + default: + throw new IllegalStateException("unrecognized index values mode " + type); + } + } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCodec.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCodec.java index 39b53e4f2ef..23defd7b9bc 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCodec.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextCodec.java @@ -26,7 +26,6 @@ import org.apache.lucene.codecs.PostingsFormat; import org.apache.lucene.codecs.SegmentInfosFormat; import org.apache.lucene.codecs.StoredFieldsFormat; import org.apache.lucene.codecs.TermVectorsFormat; -import org.apache.lucene.codecs.lucene40.Lucene40DocValuesFormat; /** * plain text index format. @@ -41,7 +40,7 @@ public final class SimpleTextCodec extends Codec { private final FieldInfosFormat fieldInfosFormat = new SimpleTextFieldInfosFormat(); private final TermVectorsFormat vectorsFormat = new SimpleTextTermVectorsFormat(); // TODO: need a plain-text impl - private final DocValuesFormat docValues = new Lucene40DocValuesFormat(); + private final DocValuesFormat docValues = new SimpleTextDocValuesFormat(); // TODO: need a plain-text impl (using the above) private final NormsFormat normsFormat = new SimpleTextNormsFormat(); private final LiveDocsFormat liveDocs = new SimpleTextLiveDocsFormat(); diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesConsumer.java new file mode 100644 index 00000000000..477e87b7142 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesConsumer.java @@ -0,0 +1,288 @@ +package org.apache.lucene.codecs.simpletext; +/** + * 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.codecs.DocValuesArraySource; +import org.apache.lucene.codecs.DocValuesConsumer; +import org.apache.lucene.index.DocValues.Type; +import org.apache.lucene.index.IndexFileNames; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; +import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.util.ArrayUtil; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefHash; +import org.apache.lucene.util.IOUtils; + +/** + * @lucene.experimental + */ +public class SimpleTextDocValuesConsumer extends DocValuesConsumer { + + static final BytesRef ZERO_DOUBLE = new BytesRef(Double.toString(0d)); + static final BytesRef ZERO_INT = new BytesRef(Integer.toString(0)); + static final BytesRef HEADER = new BytesRef("SimpleTextDocValues"); + + static final BytesRef END = new BytesRef("END"); + static final BytesRef VALUE_SIZE = new BytesRef("valuesize "); + static final BytesRef DOC = new BytesRef(" doc "); + static final BytesRef VALUE = new BytesRef(" value "); + protected BytesRef scratch = new BytesRef(); + protected int maxDocId = -1; + protected final String segment; + protected final Directory dir; + protected final IOContext ctx; + protected final Type type; + protected final BytesRefHash hash; + private int[] ords; + private int fixedSize = Integer.MIN_VALUE; + private BytesRef zeroBytes; + private final String segmentSuffix; + + + public SimpleTextDocValuesConsumer(String segment, Directory dir, + IOContext ctx, Type type, String segmentSuffix) { + this.ctx = ctx; + this.dir = dir; + this.segment = segment; + this.type = type; + hash = new BytesRefHash(); + ords = new int[0]; + this.segmentSuffix = segmentSuffix; + + } + + @Override + public void add(int docID, IndexableField value) throws IOException { + assert docID >= 0; + int ord = -1; + int vSize = -1; + switch (type) { + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + vSize = value.binaryValue().length; + ord = hash.add(value.binaryValue()); + break; + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + vSize = -1; + try { + ord = hash.add(value.binaryValue()); + } catch (NullPointerException e) { + System.err.println(); + } + break; + case FIXED_INTS_16: + vSize = 2; + scratch.grow(2); + DocValuesArraySource.copyShort(scratch, value.numericValue().shortValue()); + ord = hash.add(scratch); + break; + case FIXED_INTS_32: + vSize = 4; + scratch.grow(4); + DocValuesArraySource.copyInt(scratch, value.numericValue().intValue()); + ord = hash.add(scratch); + break; + case FIXED_INTS_8: + vSize = 1; + scratch.grow(1); + scratch.bytes[scratch.offset] = value.numericValue().byteValue(); + scratch.length = 1; + ord = hash.add(scratch); + break; + case FIXED_INTS_64: + vSize = 8; + case VAR_INTS: + scratch.grow(8); + DocValuesArraySource.copyLong(scratch, value.numericValue().longValue()); + ord = hash.add(scratch); + break; + case FLOAT_32: + vSize = 4; + scratch.grow(4); + DocValuesArraySource.copyInt(scratch, + Float.floatToRawIntBits(value.numericValue().floatValue())); + ord = hash.add(scratch); + break; + case FLOAT_64: + vSize = 8; + scratch.grow(8); + DocValuesArraySource.copyLong(scratch, + Double.doubleToRawLongBits(value.numericValue().doubleValue())); + ord = hash.add(scratch); + break; + + } + + if (fixedSize == Integer.MIN_VALUE) { + assert maxDocId == -1; + fixedSize = vSize; + } else { + if (fixedSize != vSize) { + throw new IllegalArgumentException("value size must be " + fixedSize + " but was: " + vSize); + } + } + maxDocId = Math.max(docID, maxDocId); + ords = grow(ords, docID); + + ords[docID] = (ord < 0 ? (-ord)-1 : ord) + 1; + } + + protected BytesRef getHeader() { + return HEADER; + } + + private int[] grow(int[] array, int upto) { + if (array.length <= upto) { + return ArrayUtil.grow(array, 1 + upto); + } + return array; + } + + private void prepareFlush(int docCount) { + assert ords != null; + ords = grow(ords, docCount); + } + + @Override + public void finish(int docCount) throws IOException { + final String fileName = IndexFileNames.segmentFileName(segment, "", + segmentSuffix); + IndexOutput output = dir.createOutput(fileName, ctx); + boolean success = false; + BytesRef spare = new BytesRef(); + try { + SimpleTextUtil.write(output, getHeader()); + SimpleTextUtil.writeNewline(output); + SimpleTextUtil.write(output, VALUE_SIZE); + SimpleTextUtil.write(output, Integer.toString(this.fixedSize), scratch); + SimpleTextUtil.writeNewline(output); + prepareFlush(docCount); + for (int i = 0; i < docCount; i++) { + SimpleTextUtil.write(output, DOC); + SimpleTextUtil.write(output, Integer.toString(i), scratch); + SimpleTextUtil.writeNewline(output); + SimpleTextUtil.write(output, VALUE); + writeDoc(output, i, spare); + SimpleTextUtil.writeNewline(output); + } + SimpleTextUtil.write(output, END); + SimpleTextUtil.writeNewline(output); + success = true; + } finally { + hash.close(); + if (success) { + IOUtils.close(output); + } else { + IOUtils.closeWhileHandlingException(output); + } + } + } + + protected void writeDoc(IndexOutput output, int docId, BytesRef spare) throws IOException { + int ord = ords[docId] - 1; + if (ord != -1) { + assert ord >= 0; + hash.get(ord, spare); + + switch (type) { + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + SimpleTextUtil.write(output, spare); + break; + case FIXED_INTS_16: + SimpleTextUtil.write(output, + Short.toString(DocValuesArraySource.asShort(spare)), scratch); + break; + case FIXED_INTS_32: + SimpleTextUtil.write(output, + Integer.toString(DocValuesArraySource.asInt(spare)), scratch); + break; + case VAR_INTS: + case FIXED_INTS_64: + SimpleTextUtil.write(output, + Long.toString(DocValuesArraySource.asLong(spare)), scratch); + break; + case FIXED_INTS_8: + assert spare.length == 1 : spare.length; + SimpleTextUtil.write(output, + Integer.toString(spare.bytes[spare.offset]), scratch); + break; + case FLOAT_32: + float valueFloat = Float.intBitsToFloat(DocValuesArraySource.asInt(spare)); + SimpleTextUtil.write(output, Float.toString(valueFloat), scratch); + break; + case FLOAT_64: + double valueDouble = Double.longBitsToDouble(DocValuesArraySource + .asLong(spare)); + SimpleTextUtil.write(output, Double.toString(valueDouble), scratch); + break; + default: + throw new IllegalArgumentException("unsupported type: " + type); + } + } else { + switch (type) { + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + if(zeroBytes == null) { + assert fixedSize > 0; + zeroBytes = new BytesRef(new byte[fixedSize]); + } + SimpleTextUtil.write(output, zeroBytes); + break; + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + scratch.length = 0; + SimpleTextUtil.write(output, scratch); + break; + case FIXED_INTS_16: + case FIXED_INTS_32: + case FIXED_INTS_64: + case FIXED_INTS_8: + case VAR_INTS: + SimpleTextUtil.write(output, ZERO_INT); + break; + case FLOAT_32: + case FLOAT_64: + SimpleTextUtil.write(output, ZERO_DOUBLE); + break; + default: + throw new IllegalArgumentException("unsupported type: " + type); + } + } + + } + + @Override + protected Type getType() { + return type; + } + + + +} \ No newline at end of file diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesFormat.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesFormat.java new file mode 100644 index 00000000000..b33186783a4 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesFormat.java @@ -0,0 +1,53 @@ +package org.apache.lucene.codecs.simpletext; + +/** + * 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.Set; + +import org.apache.lucene.codecs.DocValuesFormat; +import org.apache.lucene.codecs.PerDocConsumer; +import org.apache.lucene.codecs.PerDocProducer; +import org.apache.lucene.index.PerDocWriteState; +import org.apache.lucene.index.SegmentInfo; +import org.apache.lucene.index.SegmentReadState; +import org.apache.lucene.util.BytesRef; +/** + * @lucene.experimental + */ +public class SimpleTextDocValuesFormat extends DocValuesFormat { + private static final String DOC_VALUES_SEG_SUFFIX = "dv"; + @Override + public PerDocConsumer docsConsumer(PerDocWriteState state) throws IOException { + return new SimpleTextPerDocConsumer(state, DOC_VALUES_SEG_SUFFIX); + } + + @Override + public PerDocProducer docsProducer(SegmentReadState state) throws IOException { + return new SimpleTextPerDocProducer(state, BytesRef.getUTF8SortedAsUnicodeComparator(), DOC_VALUES_SEG_SUFFIX); + } + + static String docValuesId(String segmentsName, int fieldId) { + return segmentsName + "_" + fieldId; + } + + @Override + public void files(SegmentInfo info, Set files) + throws IOException { + SimpleTextPerDocConsumer.files(info, files, DOC_VALUES_SEG_SUFFIX); + } +} diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsConsumer.java deleted file mode 100644 index 086e770f6f2..00000000000 --- a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsConsumer.java +++ /dev/null @@ -1,294 +0,0 @@ -package org.apache.lucene.codecs.simpletext; - -/** - * 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.Closeable; -import java.io.IOException; -import java.util.Set; - -import org.apache.lucene.codecs.DocValuesConsumer; -import org.apache.lucene.codecs.PerDocConsumer; -import org.apache.lucene.index.DocValues.Type; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.FieldInfo; -import org.apache.lucene.index.FieldInfos; -import org.apache.lucene.index.IndexFileNames; -import org.apache.lucene.index.AtomicReader; -import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.SegmentInfo; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IOContext; -import org.apache.lucene.store.IndexOutput; -import org.apache.lucene.util.ArrayUtil; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.IOUtils; - -/** - * Writes plain-text norms - *

- * FOR RECREATIONAL USE ONLY - * - * @lucene.experimental - */ -public class SimpleTextNormsConsumer extends PerDocConsumer { - - /** Extension of norms file */ - static final String NORMS_EXTENSION = "len"; - final static BytesRef END = new BytesRef("END"); - final static BytesRef FIELD = new BytesRef("field "); - final static BytesRef DOC = new BytesRef(" doc "); - final static BytesRef NORM = new BytesRef(" norm "); - - private NormsWriter writer; - - private final Directory directory; - - private final String segment; - - private final IOContext context; - - public SimpleTextNormsConsumer(Directory directory, String segment, - IOContext context) throws IOException { - this.directory = directory; - this.segment = segment; - this.context = context; - } - - @Override - public void close() throws IOException { - if (writer != null) { - boolean success = false; - try { - writer.finish(); - success = true; - } finally { - if (success) { - IOUtils.close(writer); - } else { - IOUtils.closeWhileHandlingException(writer); - } - } - } - } - - @Override - protected DocValues getDocValuesForMerge(AtomicReader reader, FieldInfo info) - throws IOException { - return reader.normValues(info.name); - } - - @Override - protected boolean canMerge(FieldInfo info) { - return info.normsPresent(); - } - - @Override - protected Type getDocValuesType(FieldInfo info) { - return info.getNormType(); - } - - @Override - public DocValuesConsumer addValuesField(Type type, FieldInfo fieldInfo) - throws IOException { - if (type != Type.FIXED_INTS_8) { - throw new UnsupportedOperationException("Codec only supports single byte norm values. Type give: " + type); - } - return new SimpleTextNormsDocValuesConsumer(fieldInfo); - } - - @Override - public void abort() { - if (writer != null) { - try { - writer.abort(); - } catch (IOException e) { - } - } - } - - private class SimpleTextNormsDocValuesConsumer extends DocValuesConsumer { - // Holds all docID/norm pairs we've seen - int[] docIDs = new int[1]; - byte[] norms = new byte[1]; - int upto; - private final FieldInfo fi; - - public SimpleTextNormsDocValuesConsumer(FieldInfo fieldInfo) { - fi = fieldInfo; - } - - @Override - public void add(int docID, IndexableField docValue) throws IOException { - add(docID, docValue.numericValue().longValue()); - } - - public void add(int docID, long value) { - if (docIDs.length <= upto) { - assert docIDs.length == upto; - docIDs = ArrayUtil.grow(docIDs, 1 + upto); - } - if (norms.length <= upto) { - assert norms.length == upto; - norms = ArrayUtil.grow(norms, 1 + upto); - } - norms[upto] = (byte) value; - - docIDs[upto] = docID; - upto++; - } - - @Override - public void finish(int docCount) throws IOException { - final NormsWriter normsWriter = getNormsWriter(); - boolean success = false; - try { - int uptoDoc = 0; - normsWriter.setNumTotalDocs(docCount); - if (upto > 0) { - normsWriter.startField(fi); - int docID = 0; - for (; docID < docCount; docID++) { - if (uptoDoc < upto && docIDs[uptoDoc] == docID) { - normsWriter.writeNorm(norms[uptoDoc]); - uptoDoc++; - } else { - normsWriter.writeNorm((byte) 0); - } - } - // we should have consumed every norm - assert uptoDoc == upto; - - } else { - // Fill entire field with default norm: - normsWriter.startField(fi); - for (; upto < docCount; upto++) - normsWriter.writeNorm((byte) 0); - } - success = true; - } finally { - if (!success) { - normsWriter.abort(); - } - } - } - } - - public NormsWriter getNormsWriter() throws IOException { - if (writer == null) { - writer = new NormsWriter(directory, segment, context); - } - return writer; - } - - private static class NormsWriter implements Closeable{ - - private final IndexOutput output; - private int numTotalDocs = 0; - private int docid = 0; - - private final BytesRef scratch = new BytesRef(); - - - public NormsWriter(Directory directory, String segment, IOContext context) - throws IOException { - final String normsFileName = IndexFileNames.segmentFileName(segment, "", - NORMS_EXTENSION); - output = directory.createOutput(normsFileName, context); - - } - - public void startField(FieldInfo info) throws IOException { - assert info.omitNorms == false; - docid = 0; - write(FIELD); - write(info.name); - newLine(); - } - - public void writeNorm(byte norm) throws IOException { - write(DOC); - write(Integer.toString(docid)); - newLine(); - - write(NORM); - write(norm); - newLine(); - docid++; - } - - public void finish(int numDocs) throws IOException { - if (docid != numDocs) { - throw new RuntimeException( - "mergeNorms produced an invalid result: docCount is " + numDocs - + " but only saw " + docid + " file=" + output.toString() - + "; now aborting this merge to prevent index corruption"); - } - write(END); - newLine(); - } - - private void write(String s) throws IOException { - SimpleTextUtil.write(output, s, scratch); - } - - private void write(BytesRef bytes) throws IOException { - SimpleTextUtil.write(output, bytes); - } - - private void write(byte b) throws IOException { - scratch.grow(1); - scratch.bytes[scratch.offset] = b; - scratch.length = 1; - SimpleTextUtil.write(output, scratch); - } - - private void newLine() throws IOException { - SimpleTextUtil.writeNewline(output); - } - - public void setNumTotalDocs(int numTotalDocs) { - assert this.numTotalDocs == 0 || numTotalDocs == this.numTotalDocs; - this.numTotalDocs = numTotalDocs; - } - - public void abort() throws IOException { - close(); - } - - public void finish() throws IOException { - finish(numTotalDocs); - } - - @Override - public void close() throws IOException { - output.close(); - } - } - - public static void files(SegmentInfo info, Set files) throws IOException { - FieldInfos fieldInfos = info.getFieldInfos(); - - for (FieldInfo fieldInfo : fieldInfos) { - if (fieldInfo.normsPresent()) { - files.add(IndexFileNames.segmentFileName(info.name, "", - NORMS_EXTENSION)); - break; - } - } - } -} diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsFormat.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsFormat.java index 596dd715ca5..598a1f0f943 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsFormat.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsFormat.java @@ -18,35 +18,123 @@ package org.apache.lucene.codecs.simpletext; */ import java.io.IOException; +import java.util.Comparator; +import java.util.HashSet; import java.util.Set; import org.apache.lucene.codecs.NormsFormat; import org.apache.lucene.codecs.PerDocConsumer; import org.apache.lucene.codecs.PerDocProducer; +import org.apache.lucene.index.AtomicReader; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.Type; +import org.apache.lucene.index.FieldInfo; +import org.apache.lucene.index.FieldInfos; +import org.apache.lucene.index.IndexFileNames; import org.apache.lucene.index.PerDocWriteState; import org.apache.lucene.index.SegmentInfo; import org.apache.lucene.index.SegmentReadState; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.IOUtils; /** * plain-text norms format *

* FOR RECREATIONAL USE ONLY + * * @lucene.experimental */ public class SimpleTextNormsFormat extends NormsFormat { + private static final String NORMS_SEG_SUFFIX = "len"; @Override public PerDocConsumer docsConsumer(PerDocWriteState state) throws IOException { - return new SimpleTextNormsConsumer(state.directory, state.segmentName, state.context); + return new SimpleTextNormsPerDocConsumer(state, NORMS_SEG_SUFFIX); } - + @Override public PerDocProducer docsProducer(SegmentReadState state) throws IOException { - return new SimpleTextNormsProducer(state.dir, state.segmentInfo, state.fieldInfos, state.context); + return new SimpleTextNormsPerDocProducer(state, + BytesRef.getUTF8SortedAsUnicodeComparator(), NORMS_SEG_SUFFIX); } - + @Override public void files(SegmentInfo info, Set files) throws IOException { - SimpleTextNormsConsumer.files(info, files); - } + SimpleTextNormsPerDocConsumer.files(info, files); + } + + public static class SimpleTextNormsPerDocProducer extends + SimpleTextPerDocProducer { + + public SimpleTextNormsPerDocProducer(SegmentReadState state, + Comparator comp, String segmentSuffix) throws IOException { + super(state, comp, segmentSuffix); + } + + @Override + protected boolean canLoad(FieldInfo info) { + return info.hasNorms(); + } + + @Override + protected Type getDocValuesType(FieldInfo info) { + return info.getNormType(); + } + + @Override + protected boolean anyDocValuesFields(FieldInfos infos) { + return infos.hasNorms(); + } + + } + + public static class SimpleTextNormsPerDocConsumer extends + SimpleTextPerDocConsumer { + + public SimpleTextNormsPerDocConsumer(PerDocWriteState state, + String segmentSuffix) throws IOException { + super(state, segmentSuffix); + } + + @Override + protected DocValues getDocValuesForMerge(AtomicReader reader, FieldInfo info) + throws IOException { + return reader.normValues(info.name); + } + + @Override + protected boolean canMerge(FieldInfo info) { + return info.hasNorms(); + } + + @Override + protected Type getDocValuesType(FieldInfo info) { + return info.getNormType(); + } + + @Override + public void abort() { + Set files = new HashSet(); + filesInternal(state.fieldInfos, state.segmentName, files, segmentSuffix); + IOUtils.deleteFilesIgnoringExceptions(state.directory, + files.toArray(new String[0])); + } + + public static void files(SegmentInfo segmentInfo, Set files) + throws IOException { + filesInternal(segmentInfo.getFieldInfos(), segmentInfo.name, files, + NORMS_SEG_SUFFIX); + } + + public static void filesInternal(FieldInfos fieldInfos, String segmentName, + Set files, String segmentSuffix) { + for (FieldInfo fieldInfo : fieldInfos) { + if (fieldInfo.hasNorms()) { + String id = docValuesId(segmentName, fieldInfo.number); + files.add(IndexFileNames.segmentFileName(id, "", + segmentSuffix)); + } + } + } + } } diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsProducer.java deleted file mode 100644 index 126770b1c8b..00000000000 --- a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextNormsProducer.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.apache.lucene.codecs.simpletext; - -/** - * 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 static org.apache.lucene.codecs.simpletext.SimpleTextNormsConsumer.DOC; -import static org.apache.lucene.codecs.simpletext.SimpleTextNormsConsumer.END; -import static org.apache.lucene.codecs.simpletext.SimpleTextNormsConsumer.FIELD; -import static org.apache.lucene.codecs.simpletext.SimpleTextNormsConsumer.NORM; -import static org.apache.lucene.codecs.simpletext.SimpleTextNormsConsumer.NORMS_EXTENSION; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.lucene.codecs.PerDocProducer; -import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.DocValues.Source; -import org.apache.lucene.index.DocValues.Type; -import org.apache.lucene.index.FieldInfo; -import org.apache.lucene.index.FieldInfos; -import org.apache.lucene.index.IndexFileNames; -import org.apache.lucene.index.SegmentInfo; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IOContext; -import org.apache.lucene.store.IndexInput; -import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.IOUtils; -import org.apache.lucene.util.StringHelper; - -/** - * Reads plain-text norms - *

- * FOR RECREATIONAL USE ONLY - * @lucene.experimental - */ -public class SimpleTextNormsProducer extends PerDocProducer { - - Map norms = new HashMap(); - - public SimpleTextNormsProducer(Directory directory, SegmentInfo si, FieldInfos fields, IOContext context) throws IOException { - if (fields.hasNorms()) { - readNorms(directory.openInput(IndexFileNames.segmentFileName(si.name, "", NORMS_EXTENSION), context), si.docCount); - } - } - - // we read in all the norms up front into a hashmap - private void readNorms(IndexInput in, int maxDoc) throws IOException { - BytesRef scratch = new BytesRef(); - boolean success = false; - try { - SimpleTextUtil.readLine(in, scratch); - while (!scratch.equals(END)) { - assert StringHelper.startsWith(scratch, FIELD); - final String fieldName = readString(FIELD.length, scratch); - byte bytes[] = new byte[maxDoc]; - for (int i = 0; i < bytes.length; i++) { - SimpleTextUtil.readLine(in, scratch); - assert StringHelper.startsWith(scratch, DOC); - SimpleTextUtil.readLine(in, scratch); - assert StringHelper.startsWith(scratch, NORM); - bytes[i] = scratch.bytes[scratch.offset + NORM.length]; - } - norms.put(fieldName, new NormsDocValues(new Norm(bytes))); - SimpleTextUtil.readLine(in, scratch); - assert StringHelper.startsWith(scratch, FIELD) || scratch.equals(END); - } - success = true; - } finally { - if (success) { - IOUtils.close(in); - } else { - IOUtils.closeWhileHandlingException(in); - } - } - } - - @Override - public void close() throws IOException { - norms = null; - } - - static void files(Directory dir, SegmentInfo info, Set files) throws IOException { - FieldInfos fieldInfos = info.getFieldInfos(); - for (FieldInfo fieldInfo : fieldInfos) { - if (fieldInfo.normsPresent()) { - files.add(IndexFileNames.segmentFileName(info.name, "", SimpleTextNormsConsumer.NORMS_EXTENSION)); - break; - } - } - } - - private String readString(int offset, BytesRef scratch) { - return new String(scratch.bytes, scratch.offset+offset, scratch.length-offset, IOUtils.CHARSET_UTF_8); - } - - @Override - public DocValues docValues(String field) throws IOException { - return norms.get(field); - } - - private class NormsDocValues extends DocValues { - private final Source source; - public NormsDocValues(Source source) { - this.source = source; - } - - @Override - public Source load() throws IOException { - return source; - } - - @Override - public Source getDirectSource() throws IOException { - return getSource(); - } - - @Override - public Type type() { - return Type.FIXED_INTS_8; - } - - @Override - public int getValueSize() { - return 1; - } - } - - static final class Norm extends Source { - protected Norm(byte[] bytes) { - super(Type.FIXED_INTS_8); - this.bytes = bytes; - } - final byte bytes[]; - - @Override - public BytesRef getBytes(int docID, BytesRef ref) { - ref.bytes = bytes; - ref.offset = docID; - ref.length = 1; - return ref; - } - - @Override - public long getInt(int docID) { - return bytes[docID]; - } - - @Override - public boolean hasArray() { - return true; - } - - @Override - public Object getArray() { - return bytes; - } - - } -} diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocConsumer.java new file mode 100644 index 00000000000..d3dfd4485bb --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocConsumer.java @@ -0,0 +1,94 @@ +package org.apache.lucene.codecs.simpletext; +/** + * 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.HashSet; +import java.util.Set; + +import org.apache.lucene.codecs.DocValuesConsumer; +import org.apache.lucene.codecs.PerDocConsumer; +import org.apache.lucene.index.FieldInfo; +import org.apache.lucene.index.FieldInfos; +import org.apache.lucene.index.IndexFileNames; +import org.apache.lucene.index.PerDocWriteState; +import org.apache.lucene.index.SegmentInfo; +import org.apache.lucene.index.DocValues.Type; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.IOUtils; + +/** + * @lucene.experimental + */ +class SimpleTextPerDocConsumer extends PerDocConsumer { + + protected final PerDocWriteState state; + protected final String segmentSuffix; + public SimpleTextPerDocConsumer(PerDocWriteState state, String segmentSuffix) + throws IOException { + this.state = state; + this.segmentSuffix = segmentSuffix; + } + + @Override + public void close() throws IOException { + + } + + @Override + public DocValuesConsumer addValuesField(Type type, FieldInfo field) + throws IOException { + return new SimpleTextDocValuesConsumer(SimpleTextDocValuesFormat.docValuesId(state.segmentName, + field.number), state.directory, state.context, type, segmentSuffix); + } + + @Override + public void abort() { + Set files = new HashSet(); + files(state.directory, state.fieldInfos, state.segmentName, files, segmentSuffix); + IOUtils.deleteFilesIgnoringExceptions(state.directory, + files.toArray(new String[0])); + } + + + static void files(SegmentInfo info, Set files, String segmentSuffix) throws IOException { + files(info.dir, info.getFieldInfos(), info.name, files, segmentSuffix); + } + + static String docValuesId(String segmentsName, int fieldId) { + return segmentsName + "_" + fieldId; + } + + @SuppressWarnings("fallthrough") + private static void files(Directory dir, FieldInfos fieldInfos, + String segmentName, Set files, String segmentSuffix) { + for (FieldInfo fieldInfo : fieldInfos) { + if (fieldInfo.hasDocValues()) { + String filename = docValuesId(segmentName, fieldInfo.number); + files.add(IndexFileNames.segmentFileName(filename, "", + segmentSuffix)); + try { + assert dir.fileExists(IndexFileNames.segmentFileName(filename, "", + segmentSuffix)); + } catch (IOException e) { + // don't throw checked exception - dir is only used in assert + throw new RuntimeException(e); + } + } + } + } + +} \ No newline at end of file diff --git a/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocProducer.java new file mode 100644 index 00000000000..59e49c615a4 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/codecs/simpletext/SimpleTextPerDocProducer.java @@ -0,0 +1,431 @@ +package org.apache.lucene.codecs.simpletext; + +/** + * 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 static org.apache.lucene.codecs.simpletext.SimpleTextDocValuesConsumer.DOC; +import static org.apache.lucene.codecs.simpletext.SimpleTextDocValuesConsumer.END; +import static org.apache.lucene.codecs.simpletext.SimpleTextDocValuesConsumer.HEADER; +import static org.apache.lucene.codecs.simpletext.SimpleTextDocValuesConsumer.VALUE; +import static org.apache.lucene.codecs.simpletext.SimpleTextDocValuesConsumer.VALUE_SIZE; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.lucene.codecs.DocValuesArraySource; +import org.apache.lucene.codecs.PerDocProducerBase; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.SortedSource; +import org.apache.lucene.index.DocValues.Source; +import org.apache.lucene.index.DocValues.Type; +import org.apache.lucene.index.IndexFileNames; +import org.apache.lucene.index.SegmentReadState; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IOContext; +import org.apache.lucene.store.IndexInput; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.BytesRefHash; +import org.apache.lucene.util.IOUtils; +import org.apache.lucene.util.StringHelper; +import org.apache.lucene.util.packed.PackedInts.Reader; + +/** + * @lucene.experimental + */ +public class SimpleTextPerDocProducer extends PerDocProducerBase { + protected final TreeMap docValues; + private Comparator comp; + private final String segmentSuffix; + + /** + * Creates a new {@link SimpleTextPerDocProducer} instance and loads all + * {@link DocValues} instances for this segment and codec. + */ + public SimpleTextPerDocProducer(SegmentReadState state, + Comparator comp, String segmentSuffix) throws IOException { + this.comp = comp; + this.segmentSuffix = segmentSuffix; + if (anyDocValuesFields(state.fieldInfos)) { + docValues = load(state.fieldInfos, state.segmentInfo.name, + state.segmentInfo.docCount, state.dir, state.context); + } else { + docValues = new TreeMap(); + } + } + + @Override + protected Map docValues() { + return docValues; + } + + protected DocValues loadDocValues(int docCount, Directory dir, String id, + DocValues.Type type, IOContext context) throws IOException { + return new SimpleTextDocValues(dir, context, type, id, docCount, comp, segmentSuffix); + } + + @Override + protected void closeInternal(Collection closeables) + throws IOException { + IOUtils.close(closeables); + } + + private static class SimpleTextDocValues extends DocValues { + + private int docCount; + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + IOUtils.close(input); + } + } + + private Type type; + private Comparator comp; + private int valueSize; + private final IndexInput input; + + public SimpleTextDocValues(Directory dir, IOContext ctx, Type type, + String id, int docCount, Comparator comp, String segmentSuffix) throws IOException { + this.type = type; + this.docCount = docCount; + this.comp = comp; + final String fileName = IndexFileNames.segmentFileName(id, "", segmentSuffix); + boolean success = false; + IndexInput in = null; + try { + in = dir.openInput(fileName, ctx); + valueSize = readHeader(in); + success = true; + } finally { + if (!success) { + IOUtils.closeWhileHandlingException(in); + } + } + input = in; + + } + + @Override + public Source load() throws IOException { + boolean success = false; + IndexInput in = (IndexInput) input.clone(); + try { + Source source = null; + switch (type) { + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + source = read(in, new ValueReader(type, docCount, comp)); + break; + case FIXED_INTS_16: + case FIXED_INTS_32: + case VAR_INTS: + case FIXED_INTS_64: + case FIXED_INTS_8: + case FLOAT_32: + case FLOAT_64: + source = read(in, new ValueReader(type, docCount, null)); + break; + default: + throw new IllegalArgumentException("unknown type: " + type); + } + assert source != null; + success = true; + return source; + } finally { + if (!success) { + IOUtils.closeWhileHandlingException(in); + } else { + IOUtils.close(in); + } + } + } + + private int readHeader(IndexInput in) throws IOException { + BytesRef scratch = new BytesRef(); + SimpleTextUtil.readLine(in, scratch); + assert StringHelper.startsWith(scratch, HEADER); + SimpleTextUtil.readLine(in, scratch); + assert StringHelper.startsWith(scratch, VALUE_SIZE); + return Integer.parseInt(readString(scratch.offset + VALUE_SIZE.length, + scratch)); + } + + private Source read(IndexInput in, ValueReader reader) throws IOException { + BytesRef scratch = new BytesRef(); + for (int i = 0; i < docCount; i++) { + SimpleTextUtil.readLine(in, scratch); + + assert StringHelper.startsWith(scratch, DOC) : scratch.utf8ToString(); + SimpleTextUtil.readLine(in, scratch); + assert StringHelper.startsWith(scratch, VALUE); + reader.fromString(i, scratch, scratch.offset + VALUE.length); + } + SimpleTextUtil.readLine(in, scratch); + assert scratch.equals(END); + return reader.getSource(); + } + + @Override + public Source getDirectSource() throws IOException { + return this.getSource(); + } + + @Override + public int getValueSize() { + return valueSize; + } + + @Override + public Type getType() { + return type; + } + + } + + public static String readString(int offset, BytesRef scratch) { + return new String(scratch.bytes, scratch.offset + offset, scratch.length + - offset, IOUtils.CHARSET_UTF_8); + } + + private static final class ValueReader { + private final Type type; + private byte[] bytes; + private short[] shorts; + private int[] ints; + private long[] longs; + private float[] floats; + private double[] doubles; + private Source source; + private BytesRefHash hash; + private BytesRef scratch; + + public ValueReader(Type type, int maxDocs, Comparator comp) { + super(); + this.type = type; + Source docValuesArray = null; + switch (type) { + case FIXED_INTS_16: + shorts = new short[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type) + .newFromArray(shorts); + break; + case FIXED_INTS_32: + ints = new int[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type).newFromArray(ints); + break; + case FIXED_INTS_64: + longs = new long[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type) + .newFromArray(longs); + break; + case VAR_INTS: + longs = new long[maxDocs]; + docValuesArray = new VarIntsArraySource(type, longs); + break; + case FIXED_INTS_8: + bytes = new byte[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type).newFromArray(bytes); + break; + case FLOAT_32: + floats = new float[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type) + .newFromArray(floats); + break; + case FLOAT_64: + doubles = new double[maxDocs]; + docValuesArray = DocValuesArraySource.forType(type).newFromArray( + doubles); + break; + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + assert comp != null; + hash = new BytesRefHash(); + BytesSource bytesSource = new BytesSource(type, comp, maxDocs, hash); + ints = bytesSource.docIdToEntry; + source = bytesSource; + scratch = new BytesRef(); + break; + + } + if (docValuesArray != null) { + assert source == null; + this.source = docValuesArray; + } + } + + public void fromString(int ord, BytesRef ref, int offset) { + switch (type) { + case FIXED_INTS_16: + assert shorts != null; + shorts[ord] = Short.parseShort(readString(offset, ref)); + break; + case FIXED_INTS_32: + assert ints != null; + ints[ord] = Integer.parseInt(readString(offset, ref)); + break; + case FIXED_INTS_64: + case VAR_INTS: + assert longs != null; + longs[ord] = Long.parseLong(readString(offset, ref)); + break; + case FIXED_INTS_8: + assert bytes != null; + bytes[ord] = (byte) Integer.parseInt(readString(offset, ref)); + break; + case FLOAT_32: + assert floats != null; + floats[ord] = Float.parseFloat(readString(offset, ref)); + break; + case FLOAT_64: + assert doubles != null; + doubles[ord] = Double.parseDouble(readString(offset, ref)); + break; + case BYTES_FIXED_DEREF: + case BYTES_FIXED_SORTED: + case BYTES_FIXED_STRAIGHT: + case BYTES_VAR_DEREF: + case BYTES_VAR_SORTED: + case BYTES_VAR_STRAIGHT: + scratch.bytes = ref.bytes; + scratch.length = ref.length - offset; + scratch.offset = ref.offset + offset; + int key = hash.add(scratch); + ints[ord] = key < 0 ? (-key) - 1 : key; + break; + } + } + + public Source getSource() { + if (source instanceof BytesSource) { + ((BytesSource) source).maybeSort(); + } + return source; + } + } + + private static final class BytesSource extends SortedSource { + + private final BytesRefHash hash; + int[] docIdToEntry; + int[] sortedEntries; + int[] adresses; + private final boolean isSorted; + + protected BytesSource(Type type, Comparator comp, int maxDoc, + BytesRefHash hash) { + super(type, comp); + docIdToEntry = new int[maxDoc]; + this.hash = hash; + isSorted = type == Type.BYTES_FIXED_SORTED + || type == Type.BYTES_VAR_SORTED; + } + + void maybeSort() { + if (isSorted) { + adresses = new int[hash.size()]; + sortedEntries = hash.sort(getComparator()); + for (int i = 0; i < adresses.length; i++) { + int entry = sortedEntries[i]; + adresses[entry] = i; + } + } + + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + if (isSorted) { + return hash.get(sortedEntries[ord(docID)], ref); + } else { + return hash.get(docIdToEntry[docID], ref); + } + } + + @Override + public SortedSource asSortedSource() { + if (isSorted) { + return this; + } + return null; + } + + @Override + public int ord(int docID) { + assert isSorted; + try { + return adresses[docIdToEntry[docID]]; + } catch (Exception e) { + + return 0; + } + } + + @Override + public BytesRef getByOrd(int ord, BytesRef bytesRef) { + assert isSorted; + return hash.get(sortedEntries[ord], bytesRef); + } + + @Override + public Reader getDocToOrd() { + return null; + } + + @Override + public int getValueCount() { + return hash.size(); + } + + } + + private static class VarIntsArraySource extends Source { + + private final long[] array; + + protected VarIntsArraySource(Type type, long[] array) { + super(type); + this.array = array; + } + + @Override + public long getInt(int docID) { + return array[docID]; + } + + @Override + public BytesRef getBytes(int docID, BytesRef ref) { + DocValuesArraySource.copyLong(ref, getInt(docID)); + return ref; + } + + } + +} diff --git a/lucene/core/src/java/org/apache/lucene/index/AtomicReader.java b/lucene/core/src/java/org/apache/lucene/index/AtomicReader.java index a3840a13c96..fd4428c52fc 100644 --- a/lucene/core/src/java/org/apache/lucene/index/AtomicReader.java +++ b/lucene/core/src/java/org/apache/lucene/index/AtomicReader.java @@ -60,12 +60,17 @@ public abstract class AtomicReader extends IndexReader { return readerContext; } - /** Returns true if there are norms stored for this field. */ - public boolean hasNorms(String field) throws IOException { - // backward compatible implementation. - // SegmentReader has an efficient implementation. + /** + * Returns true if there are norms stored for this field. + * @deprecated (4.0) use {@link #getFieldInfos()} and check {@link FieldInfo#hasNorms()} + * for the field instead. + */ + @Deprecated + public final boolean hasNorms(String field) throws IOException { ensureOpen(); - return normValues(field) != null; + // note: using normValues(field) != null would potentially cause i/o + FieldInfo fi = getFieldInfos().fieldInfo(field); + return fi != null && fi.hasNorms(); } /** diff --git a/lucene/core/src/java/org/apache/lucene/index/BaseCompositeReader.java b/lucene/core/src/java/org/apache/lucene/index/BaseCompositeReader.java index c9bb2001455..d97e0b6b453 100644 --- a/lucene/core/src/java/org/apache/lucene/index/BaseCompositeReader.java +++ b/lucene/core/src/java/org/apache/lucene/index/BaseCompositeReader.java @@ -53,6 +53,14 @@ public abstract class BaseCompositeReader extends Composi private final int numDocs; private final boolean hasDeletions; + /** + * Constructs a {@code BaseCompositeReader} on the given subReaders. + * @param subReaders the wrapped sub-readers. This array is returned by + * {@link #getSequentialSubReaders} and used to resolve the correct + * subreader for docID-based methods. Please note: This array is not + * cloned and not protected for modification, the subclass is responsible + * to do this. + */ protected BaseCompositeReader(R[] subReaders) throws IOException { this.subReaders = subReaders; starts = new int[subReaders.length + 1]; // build starts array diff --git a/lucene/core/src/java/org/apache/lucene/index/BufferedDeletesStream.java b/lucene/core/src/java/org/apache/lucene/index/BufferedDeletesStream.java index ad55e08c053..78f80f11fa7 100644 --- a/lucene/core/src/java/org/apache/lucene/index/BufferedDeletesStream.java +++ b/lucene/core/src/java/org/apache/lucene/index/BufferedDeletesStream.java @@ -210,7 +210,7 @@ class BufferedDeletesStream { // Lock order: IW -> BD -> RP assert readerPool.infoIsLive(info); - final IndexWriter.ReadersAndLiveDocs rld = readerPool.get(info, true); + final ReadersAndLiveDocs rld = readerPool.get(info, true); final SegmentReader reader = rld.getReader(IOContext.READ); int delCount = 0; final boolean segAllDeletes; @@ -224,11 +224,12 @@ class BufferedDeletesStream { // Don't delete by Term here; DocumentsWriterPerThread // already did that on flush: delCount += applyQueryDeletes(packet.queriesIterable(), rld, reader); - final int fullDelCount = rld.info.getDelCount() + rld.pendingDeleteCount; + final int fullDelCount = rld.info.getDelCount() + rld.getPendingDeleteCount(); assert fullDelCount <= rld.info.docCount; segAllDeletes = fullDelCount == rld.info.docCount; } finally { - readerPool.release(reader, false); + rld.release(reader); + readerPool.release(rld); } anyNewDeletes |= delCount > 0; @@ -262,18 +263,19 @@ class BufferedDeletesStream { if (coalescedDeletes != null) { // Lock order: IW -> BD -> RP assert readerPool.infoIsLive(info); - final IndexWriter.ReadersAndLiveDocs rld = readerPool.get(info, true); + final ReadersAndLiveDocs rld = readerPool.get(info, true); final SegmentReader reader = rld.getReader(IOContext.READ); int delCount = 0; final boolean segAllDeletes; try { delCount += applyTermDeletes(coalescedDeletes.termsIterable(), rld, reader); delCount += applyQueryDeletes(coalescedDeletes.queriesIterable(), rld, reader); - final int fullDelCount = rld.info.getDelCount() + rld.pendingDeleteCount; + final int fullDelCount = rld.info.getDelCount() + rld.getPendingDeleteCount(); assert fullDelCount <= rld.info.docCount; segAllDeletes = fullDelCount == rld.info.docCount; - } finally { - readerPool.release(reader, false); + } finally { + rld.release(reader); + readerPool.release(rld); } anyNewDeletes |= delCount > 0; @@ -353,7 +355,7 @@ class BufferedDeletesStream { } // Delete by Term - private synchronized long applyTermDeletes(Iterable termsIter, IndexWriter.ReadersAndLiveDocs rld, SegmentReader reader) throws IOException { + private synchronized long applyTermDeletes(Iterable termsIter, ReadersAndLiveDocs rld, SegmentReader reader) throws IOException { long delCount = 0; Fields fields = reader.fields(); if (fields == null) { @@ -394,7 +396,7 @@ class BufferedDeletesStream { // System.out.println(" term=" + term); if (termsEnum.seekExact(term.bytes(), false)) { - DocsEnum docsEnum = termsEnum.docs(rld.liveDocs, docs, false); + DocsEnum docsEnum = termsEnum.docs(rld.getLiveDocs(), docs, false); //System.out.println("BDS: got docsEnum=" + docsEnum); if (docsEnum != null) { @@ -434,7 +436,7 @@ class BufferedDeletesStream { } // Delete by query - private static long applyQueryDeletes(Iterable queriesIter, IndexWriter.ReadersAndLiveDocs rld, final SegmentReader reader) throws IOException { + private static long applyQueryDeletes(Iterable queriesIter, ReadersAndLiveDocs rld, final SegmentReader reader) throws IOException { long delCount = 0; final AtomicReaderContext readerContext = reader.getTopReaderContext(); boolean any = false; diff --git a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java index 2c7cd03f655..0aa837fb75d 100644 --- a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java +++ b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java @@ -651,28 +651,17 @@ public class CheckIndex { if (infoStream != null) { infoStream.print(" test: field norms........."); } - DocValues dv; for (FieldInfo info : fieldInfos) { - if (reader.hasNorms(info.name)) { - dv = reader.normValues(info.name); - assert dv != null; - if (dv.getSource().hasArray()) { - Object array = dv.getSource().getArray(); - if (Array.getLength(array) != reader.maxDoc()) { - throw new RuntimeException("norms for field: " + info.name + " are of the wrong size"); - } - } - if (!info.isIndexed || info.omitNorms) { - throw new RuntimeException("field: " + info.name + " should omit norms but has them!"); - } + if (info.hasNorms()) { + assert reader.hasNorms(info.name); // deprecated path + DocValues dv = reader.normValues(info.name); + checkDocValues(dv, info.name, info.getNormType(), reader.maxDoc()); ++status.totFields; } else { + assert !reader.hasNorms(info.name); // deprecated path if (reader.normValues(info.name) != null) { throw new RuntimeException("field: " + info.name + " should omit norms but has them!"); } - if (info.normsPresent()) { - throw new RuntimeException("field: " + info.name + " should have norms but omits them!"); - } } } @@ -1171,6 +1160,92 @@ public class CheckIndex { return status; } + /** Helper method to verify values (either docvalues or norms), also checking + * type and size against fieldinfos/segmentinfo + */ + private void checkDocValues(DocValues docValues, String fieldName, DocValues.Type expectedType, int expectedDocs) throws IOException { + if (docValues == null) { + throw new RuntimeException("field: " + fieldName + " omits docvalues but should have them!"); + } + DocValues.Type type = docValues.getType(); + if (type != expectedType) { + throw new RuntimeException("field: " + fieldName + " has type: " + type + " but fieldInfos says:" + expectedType); + } + final Source values = docValues.getDirectSource(); + int size = docValues.getValueSize(); + for (int i = 0; i < expectedDocs; i++) { + switch (type) { + case BYTES_FIXED_SORTED: + case BYTES_VAR_SORTED: + case BYTES_FIXED_DEREF: + case BYTES_FIXED_STRAIGHT: + case BYTES_VAR_DEREF: + case BYTES_VAR_STRAIGHT: + BytesRef bytes = new BytesRef(); + values.getBytes(i, bytes); + if (size != -1 && size != bytes.length) { + throw new RuntimeException("field: " + fieldName + " returned wrongly sized bytes, was: " + bytes.length + " should be: " + size); + } + break; + case FLOAT_32: + assert size == 4; + values.getFloat(i); + break; + case FLOAT_64: + assert size == 8; + values.getFloat(i); + break; + case VAR_INTS: + assert size == -1; + values.getInt(i); + break; + case FIXED_INTS_16: + assert size == 2; + values.getInt(i); + break; + case FIXED_INTS_32: + assert size == 4; + values.getInt(i); + break; + case FIXED_INTS_64: + assert size == 8; + values.getInt(i); + break; + case FIXED_INTS_8: + assert size == 1; + values.getInt(i); + break; + default: + throw new IllegalArgumentException("Field: " + fieldName + + " - no such DocValues type: " + type); + } + } + if (type == DocValues.Type.BYTES_FIXED_SORTED || type == DocValues.Type.BYTES_VAR_SORTED) { + // check sorted bytes + SortedSource sortedValues = values.asSortedSource(); + Comparator comparator = sortedValues.getComparator(); + int lastOrd = -1; + BytesRef lastBytes = new BytesRef(); + for (int i = 0; i < expectedDocs; i++) { + int ord = sortedValues.ord(i); + if (ord < 0 || ord > expectedDocs) { + throw new RuntimeException("field: " + fieldName + " ord is out of bounds: " + ord); + } + BytesRef bytes = new BytesRef(); + sortedValues.getByOrd(ord, bytes); + if (lastOrd != -1) { + int ordComp = Integer.signum(new Integer(ord).compareTo(new Integer(lastOrd))); + int bytesComp = Integer.signum(comparator.compare(bytes, lastBytes)); + if (ordComp != bytesComp) { + throw new RuntimeException("field: " + fieldName + " ord comparison is wrong: " + ordComp + " comparator claims: " + bytesComp); + } + } + lastOrd = ord; + lastBytes = bytes; + } + } + } + private Status.DocValuesStatus testDocValues(SegmentInfo info, SegmentReader reader) { final Status.DocValuesStatus status = new Status.DocValuesStatus(); @@ -1183,87 +1258,7 @@ public class CheckIndex { if (fieldInfo.hasDocValues()) { status.totalValueFields++; final DocValues docValues = reader.docValues(fieldInfo.name); - if (docValues == null) { - throw new RuntimeException("field: " + fieldInfo.name + " omits docvalues but should have them!"); - } - DocValues.Type type = docValues.type(); - if (type != fieldInfo.getDocValuesType()) { - throw new RuntimeException("field: " + fieldInfo.name + " has type: " + type + " but fieldInfos says:" + fieldInfo.getDocValuesType()); - } - final Source values = docValues.getDirectSource(); - final int maxDoc = reader.maxDoc(); - int size = docValues.getValueSize(); - for (int i = 0; i < maxDoc; i++) { - switch (fieldInfo.getDocValuesType()) { - case BYTES_FIXED_SORTED: - case BYTES_VAR_SORTED: - case BYTES_FIXED_DEREF: - case BYTES_FIXED_STRAIGHT: - case BYTES_VAR_DEREF: - case BYTES_VAR_STRAIGHT: - BytesRef bytes = new BytesRef(); - values.getBytes(i, bytes); - if (size != -1 && size != bytes.length) { - throw new RuntimeException("field: " + fieldInfo.name + " returned wrongly sized bytes, was: " + bytes.length + " should be: " + size); - } - break; - case FLOAT_32: - assert size == 4; - values.getFloat(i); - break; - case FLOAT_64: - assert size == 8; - values.getFloat(i); - break; - case VAR_INTS: - assert size == -1; - values.getInt(i); - break; - case FIXED_INTS_16: - assert size == 2; - values.getInt(i); - break; - case FIXED_INTS_32: - assert size == 4; - values.getInt(i); - break; - case FIXED_INTS_64: - assert size == 8; - values.getInt(i); - break; - case FIXED_INTS_8: - assert size == 1; - values.getInt(i); - break; - default: - throw new IllegalArgumentException("Field: " + fieldInfo.name - + " - no such DocValues type: " + fieldInfo.getDocValuesType()); - } - } - if (type == DocValues.Type.BYTES_FIXED_SORTED || type == DocValues.Type.BYTES_VAR_SORTED) { - // check sorted bytes - SortedSource sortedValues = values.asSortedSource(); - Comparator comparator = sortedValues.getComparator(); - int lastOrd = -1; - BytesRef lastBytes = new BytesRef(); - for (int i = 0; i < maxDoc; i++) { - int ord = sortedValues.ord(i); - if (ord < 0 || ord > maxDoc) { - throw new RuntimeException("field: " + fieldInfo.name + " ord is out of bounds: " + ord); - } - BytesRef bytes = new BytesRef(); - sortedValues.getByOrd(ord, bytes); - if (lastOrd != -1) { - int ordComp = Integer.signum(new Integer(ord).compareTo(new Integer(lastOrd))); - int bytesComp = Integer.signum(comparator.compare(bytes, lastBytes)); - if (ordComp != bytesComp) { - throw new RuntimeException("field: " + fieldInfo.name + " ord comparison is wrong: " + ordComp + " comparator claims: " + bytesComp); - } - } - lastOrd = ord; - lastBytes = bytes; - } - } + checkDocValues(docValues, fieldInfo.name, fieldInfo.getDocValuesType(), reader.maxDoc()); } else { if (reader.docValues(fieldInfo.name) != null) { throw new RuntimeException("field: " + fieldInfo.name + " has docvalues but should omit them!"); diff --git a/lucene/core/src/java/org/apache/lucene/index/CompositeReader.java b/lucene/core/src/java/org/apache/lucene/index/CompositeReader.java index 055557daa5c..47b48220882 100644 --- a/lucene/core/src/java/org/apache/lucene/index/CompositeReader.java +++ b/lucene/core/src/java/org/apache/lucene/index/CompositeReader.java @@ -81,6 +81,9 @@ public abstract class CompositeReader extends IndexReader { * If this method returns an empty array, that means this * reader is a null reader (for example a MultiReader * that has no sub readers). + *

Warning: Don't modify the returned array! + * Doing so will corrupt the internal structure of this + * {@code CompositeReader}. */ public abstract IndexReader[] getSequentialSubReaders(); diff --git a/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java b/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java index 48ed5bf45b6..30785b0fcf2 100644 --- a/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java +++ b/lucene/core/src/java/org/apache/lucene/index/DirectoryReader.java @@ -323,8 +323,17 @@ public abstract class DirectoryReader extends BaseCompositeReader } } - protected DirectoryReader(Directory directory, AtomicReader[] readers) throws CorruptIndexException, IOException { - super(readers); + /** + * Expert: Constructs a {@code DirectoryReader} on the given subReaders. + * @param segmentReaders the wrapped atomic index segment readers. This array is + * returned by {@link #getSequentialSubReaders} and used to resolve the correct + * subreader for docID-based methods. Please note: This array is not + * cloned and not protected for modification outside of this reader. + * Subclasses of {@code DirectoryReader} should take care to not allow + * modification of this internal array, e.g. {@link #doOpenIfChanged()}. + */ + protected DirectoryReader(Directory directory, AtomicReader[] segmentReaders) throws CorruptIndexException, IOException { + super(segmentReaders); this.directory = directory; } diff --git a/lucene/core/src/java/org/apache/lucene/index/DocTermOrds.java b/lucene/core/src/java/org/apache/lucene/index/DocTermOrds.java index 038c62f4de2..677facbf6e1 100644 --- a/lucene/core/src/java/org/apache/lucene/index/DocTermOrds.java +++ b/lucene/core/src/java/org/apache/lucene/index/DocTermOrds.java @@ -216,6 +216,13 @@ public class DocTermOrds { } } + /** + * @return The number of terms in this field + */ + public int numTerms() { + return numTermsInField; + } + /** Subclass can override this */ protected void visitTerm(TermsEnum te, int termNum) throws IOException { } diff --git a/lucene/core/src/java/org/apache/lucene/index/DocValues.java b/lucene/core/src/java/org/apache/lucene/index/DocValues.java index 23999f49e0c..7d77326012d 100644 --- a/lucene/core/src/java/org/apache/lucene/index/DocValues.java +++ b/lucene/core/src/java/org/apache/lucene/index/DocValues.java @@ -90,7 +90,7 @@ public abstract class DocValues implements Closeable { /** * Returns the {@link Type} of this {@link DocValues} instance */ - public abstract Type type(); + public abstract Type getType(); /** * Closes this {@link DocValues} instance. This method should only be called @@ -191,7 +191,7 @@ public abstract class DocValues implements Closeable { * * @return the {@link Type} of this source. */ - public Type type() { + public Type getType() { return type; } diff --git a/lucene/core/src/java/org/apache/lucene/index/FieldInfo.java b/lucene/core/src/java/org/apache/lucene/index/FieldInfo.java index 60ccf4a65f8..34f9587008d 100644 --- a/lucene/core/src/java/org/apache/lucene/index/FieldInfo.java +++ b/lucene/core/src/java/org/apache/lucene/index/FieldInfo.java @@ -122,14 +122,23 @@ public final class FieldInfo { } } + /** + * @return true if this field has any docValues. + */ public boolean hasDocValues() { return docValueType != null; } + /** + * @return {@link DocValues.Type} of the docValues. this may be null if the field has no docvalues. + */ public DocValues.Type getDocValuesType() { return docValueType; } + /** + * @return {@link DocValues.Type} of the norm. this may be null if the field has no norms. + */ public DocValues.Type getNormType() { return normType; } @@ -146,11 +155,17 @@ public final class FieldInfo { } } + /** + * @return true if norms are explicitly omitted for this field + */ public boolean omitNorms() { return omitNorms; } - public boolean normsPresent() { + /** + * @return true if this field actually has any norms. + */ + public boolean hasNorms() { return isIndexed && !omitNorms && normType != null; } diff --git a/lucene/core/src/java/org/apache/lucene/index/FieldInfos.java b/lucene/core/src/java/org/apache/lucene/index/FieldInfos.java index 5fbc9511ae1..e2af36f4f4e 100644 --- a/lucene/core/src/java/org/apache/lucene/index/FieldInfos.java +++ b/lucene/core/src/java/org/apache/lucene/index/FieldInfos.java @@ -178,7 +178,7 @@ public final class FieldInfos implements Iterable { return fis; } - /** Returns true if any fields do not positions */ + /** Returns true if any fields have positions */ public boolean hasProx() { if (isReadOnly()) { return hasProx; @@ -349,6 +349,12 @@ public final class FieldInfos implements Iterable { return fi; } + /** + * lookup the number of a field by name. + * + * @param fieldName field's name + * @return number of field, or -1 if it does not exist. + */ public int fieldNumber(String fieldName) { FieldInfo fi = fieldInfo(fieldName); return (fi != null) ? fi.number : -1; @@ -384,11 +390,17 @@ public final class FieldInfos implements Iterable { return byNumber.values().iterator(); } + /** + * @return number of fields + */ public int size() { assert byNumber.size() == byName.size(); return byNumber.size(); } + /** + * @return true if at least one field has any vectors + */ public boolean hasVectors() { if (isReadOnly()) { return hasVectors; @@ -402,9 +414,12 @@ public final class FieldInfos implements Iterable { return false; } + /** + * @return true if at least one field has any norms + */ public boolean hasNorms() { for (FieldInfo fi : this) { - if (fi.normsPresent()) { + if (fi.hasNorms()) { return true; } } @@ -441,7 +456,10 @@ public final class FieldInfos implements Iterable { return roFis; } - public boolean anyDocValuesFields() { + /** + * @return true if at least one field has docValues + */ + public boolean hasDocValues() { for (FieldInfo fi : this) { if (fi.hasDocValues()) { return true; diff --git a/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java b/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java index 7cda4d35947..e1a302f48e1 100644 --- a/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java +++ b/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java @@ -359,12 +359,6 @@ public class FilterAtomicReader extends AtomicReader { return in.hasDeletions(); } - @Override - public boolean hasNorms(String field) throws IOException { - ensureOpen(); - return in.hasNorms(field); - } - @Override protected void doClose() throws IOException { in.close(); diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java b/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java index 4b9e3f7c0e8..2f7ec499774 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java @@ -453,7 +453,7 @@ final class IndexFileDeleter { assert Thread.holdsLock(writer); if (infoStream.isEnabled("IFD")) { - infoStream.message("IFD", "now checkpoint \"" + writer.segString(segmentInfos) + "\" [" + segmentInfos.size() + " segments " + "; isCommit = " + isCommit + "]"); + infoStream.message("IFD", "now checkpoint \"" + writer.segString(writer.toLiveInfos(segmentInfos)) + "\" [" + segmentInfos.size() + " segments " + "; isCommit = " + isCommit + "]"); } // Try again now to delete any previously un-deletable diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java b/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java index a27557ea57c..8c33c1996fc 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexReaderContext.java @@ -41,25 +41,27 @@ public abstract class IndexReaderContext { this.isTopLevel = parent==null; } + /** Returns the {@link IndexReader}, this context represents. */ public abstract IndexReader reader(); /** * Returns the context's leaves if this context is a top-level context * otherwise null. For convenience, if this is an * {@link AtomicReaderContext} this returns itsself as the only leaf. - *

- * Note: this is convenience method since leaves can always be obtained by + *

Note: this is convenience method since leaves can always be obtained by * walking the context tree. + *

Warning: Don't modify the returned array! + * Doing so will corrupt the internal structure of this + * {@code IndexReaderContext}. */ public abstract AtomicReaderContext[] leaves(); /** * Returns the context's children iff this context is a composite context * otherwise null. - *

- * Note: this method is a convenience method to prevent - * instanceof checks and type-casts to - * {@link CompositeReaderContext}. + *

Warning: Don't modify the returned array! + * Doing so will corrupt the internal structure of this + * {@code IndexReaderContext}. */ public abstract IndexReaderContext[] children(); } \ No newline at end of file diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java index 4a6ec709bb5..01079bd0887 100644 --- a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java +++ b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java @@ -33,7 +33,6 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.codecs.Codec; -import org.apache.lucene.codecs.LiveDocsFormat; import org.apache.lucene.index.DocumentsWriterPerThread.FlushedSegment; import org.apache.lucene.index.FieldInfos.FieldNumberBiMap; import org.apache.lucene.index.IndexWriterConfig.OpenMode; @@ -392,260 +391,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { return r; } - // This class inherits all sync from IW: - class ReadersAndLiveDocs { - // Not final because we replace (clone) when we need to - // change it and it's been shared: - public final SegmentInfo info; - - // Set once (null, and then maybe set, and never set again): - private SegmentReader reader; - - // TODO: it's sometimes wasteful that we hold open two - // separate SRs (one for merging one for - // reading)... maybe just use a single SR? The gains of - // not loading the terms index (for merging in the - // non-NRT case) are far less now... and if the app has - // any deletes it'll open real readers anyway. - - // Set once (null, and then maybe set, and never set again): - private SegmentReader mergeReader; - - // Holds the current shared (readable and writable - // liveDocs). This is null when there are no deleted - // docs, and it's copy-on-write (cloned whenever we need - // to change it but it's been shared to an external NRT - // reader). - public Bits liveDocs; - - // How many further deletions we've done against - // liveDocs vs when we loaded it or last wrote it: - public int pendingDeleteCount; - - // True if the current liveDocs is referenced by an - // external NRT reader: - public boolean shared; - - public ReadersAndLiveDocs(SegmentInfo info) { - this.info = info; - shared = true; - } - - // Returns false if we are the only remaining refs of - // this reader: - public synchronized boolean anyOutsideRefs(SegmentReader sr) { - int myRefCounts = 0; - if (sr == reader) { - myRefCounts++; - } - if (sr == mergeReader) { - myRefCounts++; - } - final int rc = sr.getRefCount(); - assert rc >= myRefCounts; - return rc > myRefCounts; - } - - // Call only from assert! - public synchronized boolean verifyDocCounts() { - int count; - if (liveDocs != null) { - count = 0; - for(int docID=0;docID> it = readerMap.entrySet().iterator(); while(it.hasNext()) { final ReadersAndLiveDocs rld = it.next().getValue(); - //System.out.println("pool.dropAll: seg=" + rld.info); if (doSave && rld.writeLiveDocs(directory)) { + // Make sure we only write del docs for a live segment: assert infoIsLive(rld.info); // Must checkpoint w/ deleter, because we just // created created new _X_N.del file. @@ -735,13 +472,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { assert readerMap.size() == 0; } - public synchronized void drop(SegmentInfo info) throws IOException { - final ReadersAndLiveDocs rld = readerMap.remove(info); - if (rld != null) { - rld.dropReaders(); - } - } - /** * Commit live docs changes for the segment readers for * the provided infos. @@ -751,19 +481,23 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { public synchronized void commit(SegmentInfos infos) throws IOException { for (SegmentInfo info : infos) { final ReadersAndLiveDocs rld = readerMap.get(info); - if (rld != null && rld.writeLiveDocs(directory)) { - assert infoIsLive(info); - // Must checkpoint w/ deleter, because we just - // created created new _X_N.del file. - deleter.checkpoint(segmentInfos, false); + if (rld != null) { + assert rld.info == info; + if (rld.writeLiveDocs(directory)) { + // Make sure we only write del docs for a live segment: + assert infoIsLive(info); + // Must checkpoint w/ deleter, because we just + // created created new _X_N.del file. + deleter.checkpoint(segmentInfos, false); + } } } } /** * Obtain a ReadersAndLiveDocs instance from the - * readerPool. If getReader is true, you must later call - * {@link #release(SegmentReader)}. + * readerPool. If create is true, you must later call + * {@link #release(ReadersAndLiveDocs)}. * @throws IOException */ public synchronized ReadersAndLiveDocs get(SegmentInfo info, boolean create) { @@ -771,15 +505,22 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { assert info.dir == directory; ReadersAndLiveDocs rld = readerMap.get(info); - //System.out.println("rld.get seg=" + info + " poolReaders=" + poolReaders); if (rld == null) { - //System.out.println(" new rld"); if (!create) { return null; } - rld = new ReadersAndLiveDocs(info); + rld = new ReadersAndLiveDocs(IndexWriter.this, info); + // Steal initial reference: readerMap.put(info, rld); + } else { + assert rld.info == info: "rld.info=" + rld.info + " info=" + info + " isLive?=" + infoIsLive(rld.info) + " vs " + infoIsLive(info); } + + if (create) { + // Return ref to caller: + rld.incRef(); + } + return rld; } } @@ -795,7 +536,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { final ReadersAndLiveDocs rld = readerPool.get(info, false); if (rld != null) { - delCount += rld.pendingDeleteCount; + delCount += rld.getPendingDeleteCount(); } return delCount; } @@ -1116,7 +857,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { finishMerges(waitForMerges); stopMerges = true; } - mergeScheduler.close(); if (infoStream.isEnabled("IW")) { @@ -1160,8 +900,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { } } } - - /** Returns the Directory used by this index. */ public Directory getDirectory() { @@ -2020,6 +1758,9 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { notifyAll(); } + // Don't bother saving any changes in our segmentInfos + readerPool.dropAll(false); + // Keep the same segmentInfos instance but replace all // of its SegmentInfo instances. This is so the next // attempt to commit using this instance of IndexWriter @@ -2038,9 +1779,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { // them: deleter.checkpoint(segmentInfos, false); deleter.refresh(); - - // Don't bother saving any changes in our segmentInfos - readerPool.dropAll(false); } lastCommitChangeCount = changeCount; @@ -3023,16 +2761,18 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { final int docCount = info.docCount; final Bits prevLiveDocs = merge.readerLiveDocs.get(i); final Bits currentLiveDocs; - ReadersAndLiveDocs rld = readerPool.get(info, false); - // We enrolled in mergeInit: - assert rld != null; - currentLiveDocs = rld.liveDocs; + final ReadersAndLiveDocs rld = readerPool.get(info, false); + // We hold a ref so it should still be in the pool: + assert rld != null: "seg=" + info.name; + currentLiveDocs = rld.getLiveDocs(); if (prevLiveDocs != null) { // If we had deletions on starting the merge we must // still have deletions now: assert currentLiveDocs != null; + assert prevLiveDocs.length() == docCount; + assert currentLiveDocs.length() == docCount; // There were deletes on this segment when the merge // started. The merge has collapsed away those @@ -3066,9 +2806,10 @@ public class IndexWriter implements Closeable, TwoPhaseCommit { } } } else { - docUpto += info.docCount - info.getDelCount() - rld.pendingDeleteCount; + docUpto += info.docCount - info.getDelCount() - rld.getPendingDeleteCount(); } } else if (currentLiveDocs != null) { + assert currentLiveDocs.length() == docCount; // This segment had no deletes before but now it // does: for(int j=0; j 1; + } + + public void decRef() { + final int rc = refCount.decrementAndGet(); + assert rc >= 0; + } + + public int refCount() { + final int rc = refCount.get(); + assert rc >= 0; + return rc; + } + + public synchronized int getPendingDeleteCount() { + return pendingDeleteCount; + } + + // Call only from assert! + public synchronized boolean verifyDocCounts() { + int count; + if (liveDocs != null) { + count = 0; + for(int docID=0;docID 0 || writer.getKeepFullyDeletedSegments()) { - readers.add(reader); - infosUpto++; - } else { - reader.close(); - segmentInfos.remove(infosUpto); + final ReadersAndLiveDocs rld = writer.readerPool.get(info, true); + try { + final SegmentReader reader = rld.getReadOnlyClone(IOContext.READ); + if (reader.numDocs() > 0 || writer.getKeepFullyDeletedSegments()) { + // Steal the ref: + readers.add(reader); + infosUpto++; + } else { + reader.close(); + segmentInfos.remove(infosUpto); + } + } finally { + writer.readerPool.release(rld); } success = true; } catch(IOException ex) { prior = ex; } finally { - if (!success) + if (!success) { IOUtils.closeWhileHandlingException(prior, readers); + } } } return new StandardDirectoryReader(dir, readers.toArray(new SegmentReader[readers.size()]), @@ -219,12 +225,12 @@ final class StandardDirectoryReader extends DirectoryReader { } @Override - protected final DirectoryReader doOpenIfChanged() throws CorruptIndexException, IOException { + protected DirectoryReader doOpenIfChanged() throws CorruptIndexException, IOException { return doOpenIfChanged(null); } @Override - protected final DirectoryReader doOpenIfChanged(final IndexCommit commit) throws CorruptIndexException, IOException { + protected DirectoryReader doOpenIfChanged(final IndexCommit commit) throws CorruptIndexException, IOException { ensureOpen(); // If we were obtained by writer.getReader(), re-ask the @@ -237,7 +243,7 @@ final class StandardDirectoryReader extends DirectoryReader { } @Override - protected final DirectoryReader doOpenIfChanged(IndexWriter writer, boolean applyAllDeletes) throws CorruptIndexException, IOException { + protected DirectoryReader doOpenIfChanged(IndexWriter writer, boolean applyAllDeletes) throws CorruptIndexException, IOException { ensureOpen(); if (writer == this.writer && applyAllDeletes == this.applyAllDeletes) { return doOpenFromWriter(null); @@ -246,7 +252,7 @@ final class StandardDirectoryReader extends DirectoryReader { } } - private final DirectoryReader doOpenFromWriter(IndexCommit commit) throws CorruptIndexException, IOException { + private DirectoryReader doOpenFromWriter(IndexCommit commit) throws CorruptIndexException, IOException { if (commit != null) { throw new IllegalArgumentException("a reader obtained from IndexWriter.getReader() cannot currently accept a commit"); } diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java index a6860018a5b..5013f2f67d6 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java +++ b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java @@ -1640,7 +1640,7 @@ public abstract class FieldComparator { // This means segment has doc values, but they are // not able to provide a sorted source; consider // this a hard error: - throw new IllegalStateException("DocValues exist for field \"" + field + "\", but not as a sorted source: type=" + dv.getSource().type() + " reader=" + context.reader()); + throw new IllegalStateException("DocValues exist for field \"" + field + "\", but not as a sorted source: type=" + dv.getSource().getType() + " reader=" + context.reader()); } } diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java index fa19fad45ba..73d416adf8a 100644 --- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java +++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java @@ -584,8 +584,9 @@ public class IndexSearcher { Weight weight = query.createWeight(this); float v = weight.getValueForNormalization(); float norm = getSimilarity().queryNorm(v); - if (Float.isInfinite(norm) || Float.isNaN(norm)) + if (Float.isInfinite(norm) || Float.isNaN(norm)) { norm = 1.0f; + } weight.normalize(norm, 1.0f); return weight; } @@ -812,6 +813,8 @@ public class IndexSearcher { final int docCount; final long sumTotalTermFreq; final long sumDocFreq; + + assert field != null; Terms terms = MultiFields.getTerms(reader, field); if (terms == null) { diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java index d829bf32595..2dbc77e27cb 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java @@ -22,7 +22,6 @@ import java.util.*; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.DocsAndPositionsEnum; -import org.apache.lucene.index.DocsEnum; import org.apache.lucene.index.AtomicReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext; @@ -238,7 +237,7 @@ public class MultiPhraseQuery extends Query { docFreq = termsEnum.docFreq(); } - postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, docFreq, positions.get(pos).intValue(), terms[0]); + postingsFreqs[pos] = new PhraseQuery.PostingsAndFreq(postingsEnum, docFreq, positions.get(pos).intValue(), terms); } // sort by increasing docFreq order @@ -314,9 +313,21 @@ public class MultiPhraseQuery extends Query { } buffer.append("\""); + int k = 0; Iterator i = termArrays.iterator(); + int lastPos = -1; + boolean first = true; while (i.hasNext()) { Term[] terms = i.next(); + int position = positions.get(k); + if (first) { + first = false; + } else { + buffer.append(" "); + for (int j=1; j<(position-lastPos); j++) { + buffer.append("? "); + } + } if (terms.length > 1) { buffer.append("("); for (int j = 0; j < terms.length; j++) { @@ -328,8 +339,8 @@ public class MultiPhraseQuery extends Query { } else { buffer.append(terms[0].text()); } - if (i.hasNext()) - buffer.append(" "); + lastPos = position; + ++k; } buffer.append("\""); diff --git a/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java b/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java index 004d857c0f6..b2d4afe3814 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java +++ b/lucene/core/src/java/org/apache/lucene/search/PhrasePositions.java @@ -31,12 +31,15 @@ final class PhrasePositions { final int ord; // unique across all PhrasePositions instances final DocsAndPositionsEnum postings; // stream of docs & positions PhrasePositions next; // used to make lists - PhrasePositions nextRepeating; // link to next repeating pp: standing for same term in different query offsets + int rptGroup = -1; // >=0 indicates that this is a repeating PP + int rptInd; // index in the rptGroup + final Term[] terms; // for repetitions initialization - PhrasePositions(DocsAndPositionsEnum postings, int o, int ord) { + PhrasePositions(DocsAndPositionsEnum postings, int o, int ord, Term[] terms) { this.postings = postings; offset = o; this.ord = ord; + this.terms = terms; } final boolean next() throws IOException { // increments to next doc @@ -78,8 +81,8 @@ final class PhrasePositions { @Override public String toString() { String s = "d:"+doc+" o:"+offset+" p:"+position+" c:"+count; - if (nextRepeating!=null) { - s += " rpt[ "+nextRepeating+" ]"; + if (rptGroup >=0 ) { + s += " rpt:"+rptGroup+",i"+rptInd; } return s; } diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java index 30faaba84c3..2f2a45c9635 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java @@ -19,6 +19,7 @@ package org.apache.lucene.search; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Set; import org.apache.lucene.index.AtomicReaderContext; @@ -137,23 +138,46 @@ public class PhraseQuery extends Query { final DocsAndPositionsEnum postings; final int docFreq; final int position; - final Term term; + final Term[] terms; + final int nTerms; // for faster comparisons - public PostingsAndFreq(DocsAndPositionsEnum postings, int docFreq, int position, Term term) { + public PostingsAndFreq(DocsAndPositionsEnum postings, int docFreq, int position, Term... terms) { this.postings = postings; this.docFreq = docFreq; this.position = position; - this.term = term; + nTerms = terms==null ? 0 : terms.length; + if (nTerms>0) { + if (terms.length==1) { + this.terms = terms; + } else { + Term[] terms2 = new Term[terms.length]; + System.arraycopy(terms, 0, terms2, 0, terms.length); + Arrays.sort(terms2); + this.terms = terms2; + } + } else { + this.terms = null; + } } public int compareTo(PostingsAndFreq other) { - if (docFreq == other.docFreq) { - if (position == other.position) { - return term.compareTo(other.term); - } + if (docFreq != other.docFreq) { + return docFreq - other.docFreq; + } + if (position != other.position) { return position - other.position; } - return docFreq - other.docFreq; + if (nTerms != other.nTerms) { + return nTerms - other.nTerms; + } + if (nTerms == 0) { + return 0; + } + for (int i=0; i 0) { - min = new PhrasePositions(postings[0].postings, postings[0].position, 0); + min = new PhrasePositions(postings[0].postings, postings[0].position, 0, postings[0].terms); max = min; max.doc = -1; for (int i = 1; i < postings.length; i++) { - PhrasePositions pp = new PhrasePositions(postings[i].postings, postings[i].position, i); + PhrasePositions pp = new PhrasePositions(postings[i].postings, postings[i].position, i, postings[i].terms); max.next = pp; max = pp; max.doc = -1; diff --git a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java index dbd5ca84d41..2f2f9ed59a5 100644 --- a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java +++ b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java @@ -19,22 +19,38 @@ package org.apache.lucene.search; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import org.apache.lucene.index.Term; import org.apache.lucene.search.similarities.Similarity; +import org.apache.lucene.util.OpenBitSet; final class SloppyPhraseScorer extends PhraseScorer { - private int slop; - private boolean checkedRepeats; // flag to only check in first candidate doc in case there are no repeats - private boolean hasRepeats; // flag indicating that there are repeats (already checked in first candidate doc) - private PhraseQueue pq; // for advancing min position - private PhrasePositions[] nrPps; // non repeating pps ordered by their query offset + + private final int slop; + private final int numPostings; + private final PhraseQueue pq; // for advancing min position + + private int end; // current largest phrase position + + private boolean hasRpts; // flag indicating that there are repetitions (as checked in first candidate doc) + private boolean checkedRpts; // flag to only check for repetitions in first candidate doc + private boolean hasMultiTermRpts; // + private PhrasePositions[][] rptGroups; // in each group are PPs that repeats each other (i.e. same term), sorted by (query) offset + private PhrasePositions[] rptStack; // temporary stack for switching colliding repeating pps SloppyPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings, int slop, Similarity.SloppySimScorer docScorer) { super(weight, postings, docScorer); this.slop = slop; + this.numPostings = postings==null ? 0 : postings.length; + pq = new PhraseQueue(postings.length); } - + /** * Score a candidate doc for all slop-valid position-combinations (matches) * encountered while traversing/hopping the PhrasePositions. @@ -55,31 +71,27 @@ final class SloppyPhraseScorer extends PhraseScorer { */ @Override protected float phraseFreq() throws IOException { - int end = initPhrasePositions(); - //printPositions(System.err, "INIT DONE:"); - if (end==Integer.MIN_VALUE) { + if (!initPhrasePositions()) { return 0.0f; } - float freq = 0.0f; PhrasePositions pp = pq.pop(); int matchLength = end - pp.position; - int next = pq.size()>0 ? pq.top().position : pp.position; - //printQueue(System.err, pp, "Bef Loop: next="+next+" mlen="+end+"-"+pp.position+"="+matchLength); - while (pp.nextPosition() && (end=advanceRepeats(pp, end)) != Integer.MIN_VALUE) { - if (pp.position > next) { - //printQueue(System.err, pp, "A: >next="+next+" matchLength="+matchLength); + int next = pq.top().position; + while (advancePP(pp)) { + if (hasRpts && !advanceRpts(pp)) { + break; // pps exhausted + } + if (pp.position > next) { // done minimizing current match-length if (matchLength <= slop) { freq += docScorer.computeSlopFactor(matchLength); // score match } pq.add(pp); pp = pq.pop(); - next = pq.size()>0 ? pq.top().position : pp.position; + next = pq.top().position; matchLength = end - pp.position; - //printQueue(System.err, pp, "B: >next="+next+" matchLength="+matchLength); } else { int matchLength2 = end - pp.position; - //printQueue(System.err, pp, "C: mlen2 repeatsEnd) { - repeatsEnd = pp.position; + /** advance a PhrasePosition and update 'end', return false if exhausted */ + private boolean advancePP(PhrasePositions pp) throws IOException { + if (!pp.nextPosition()) { + return false; } - if (!hasRepeats) { - return repeatsEnd; + if (pp.position > end) { + end = pp.position; } + return true; + } + + /** pp was just advanced. If that caused a repeater collision, resolve by advancing the lesser + * of the two colliding pps. Note that there can only be one collision, as by the initialization + * there were no collisions before pp was advanced. */ + private boolean advanceRpts(PhrasePositions pp) throws IOException { + if (pp.rptGroup < 0) { + return true; // not a repeater + } + PhrasePositions[] rg = rptGroups[pp.rptGroup]; + OpenBitSet bits = new OpenBitSet(rg.length); // for re-queuing after collisions are resolved + int k0 = pp.rptInd; + int k; + while((k=collide(pp)) >= 0) { + pp = lesser(pp, rg[k]); // always advance the lesser of the (only) two colliding pps + if (!advancePP(pp)) { + return false; // exhausted + } + if (k != k0) { // careful: mark only those currently in the queue + bits.set(k); // mark that pp2 need to be re-queued + } + } + // collisions resolved, now re-queue + // empty (partially) the queue until seeing all pps advanced for resolving collisions + int n = 0; + while (bits.cardinality() > 0) { + PhrasePositions pp2 = pq.pop(); + rptStack[n++] = pp2; + if (pp2.rptGroup >= 0 && bits.get(pp2.rptInd)) { + bits.clear(pp2.rptInd); + } + } + // add back to queue + for (int i=n-1; i>=0; i--) { + pq.add(rptStack[i]); + } + return true; + } + + /** compare two pps, but only by position and offset */ + private PhrasePositions lesser(PhrasePositions pp, PhrasePositions pp2) { + if (pp.position < pp2.position || + (pp.position == pp2.position && pp.offset < pp2.offset)) { + return pp; + } + return pp2; + } + + /** index of a pp2 colliding with pp, or -1 if none */ + private int collide(PhrasePositions pp) { int tpPos = tpPos(pp); - for (PhrasePositions pp2=pp.nextRepeating; pp2!=null; pp2=pp2.nextRepeating) { - while (tpPos(pp2) <= tpPos) { - if (!pp2.nextPosition()) { - return Integer.MIN_VALUE; - } - } - tpPos = tpPos(pp2); - if (pp2.position > repeatsEnd) { - repeatsEnd = pp2.position; - } - // "dirty" trick: with holes, given a pp, its repeating pp2 might have smaller position. - // so in order to have the right "start" in matchLength computation we fake pp.position. - // this relies on pp.nextPosition() not using pp.position. - if (pp2.position < pp.position) { - pp.position = pp2.position; + PhrasePositions[] rg = rptGroups[pp.rptGroup]; + for (int i=0; i - *

  • Detect groups of repeating pps: those with same tpPos (tpPos==position in the doc) but different offsets in query. - *
  • For each such group: - *
      - *
    • form an inner linked list of the repeating ones. - *
    • propagate all group members but first so that they land on different tpPos(). - *
    - *
  • Mark whether there are repetitions at all, so that scoring queries with no repetitions has no overhead due to this computation. - *
  • Insert to pq only non repeating PPs, or PPs that are the first in a repeating group. + *
  • Check if there are repetitions + *
  • If there are, find groups of repetitions. * * Examples: *
      @@ -145,118 +186,305 @@ final class SloppyPhraseScorer extends PhraseScorer { *
    1. repetitions: "ho my my"~2 *
    2. repetitions: "my ho my"~2 *
    - * @return end (max position), or Integer.MIN_VALUE if any term ran out (i.e. done) + * @return false if PPs are exhausted (and so current doc will not be a match) */ - private int initPhrasePositions() throws IOException { - int end = Integer.MIN_VALUE; - - // no repeats at all (most common case is also the simplest one) - if (checkedRepeats && !hasRepeats) { - // build queue from list - pq.clear(); - for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max - pp.firstPosition(); - if (pp.position > end) { - end = pp.position; - } - pq.add(pp); // build pq from list - } - return end; + private boolean initPhrasePositions() throws IOException { + end = Integer.MIN_VALUE; + if (!checkedRpts) { + return initFirstTime(); } - - //printPositions(System.err, "Init: 1: Bef position"); - - // position the pp's - for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max - pp.firstPosition(); + if (!hasRpts) { + initSimple(); + return true; // PPs available } - - //printPositions(System.err, "Init: 2: Aft position"); - - // one time initialization for this scorer (done only for the first candidate doc) - if (!checkedRepeats) { - checkedRepeats = true; - ArrayList ppsA = new ArrayList(); - PhrasePositions dummyPP = new PhrasePositions(null, -1, -1); - // check for repeats - for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max - if (pp.nextRepeating != null) { - continue; // a repetition of an earlier pp - } - ppsA.add(pp); - int tpPos = tpPos(pp); - for (PhrasePositions prevB=pp, pp2=pp.next; pp2!= min; pp2=pp2.next) { - if ( - pp2.nextRepeating != null // already detected as a repetition of an earlier pp - || pp.offset == pp2.offset // not a repetition: the two PPs are originally in same offset in the query! - || tpPos(pp2) != tpPos) { // not a repetition - continue; - } - // a repetition - hasRepeats = true; - prevB.nextRepeating = pp2; // add pp2 to the repeats linked list - pp2.nextRepeating = dummyPP; // allows not to handle the last pp in a sub-list - prevB = pp2; - } - } - if (hasRepeats) { - // clean dummy markers - for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max - if (pp.nextRepeating == dummyPP) { - pp.nextRepeating = null; - } - } - } - nrPps = ppsA.toArray(new PhrasePositions[0]); - pq = new PhraseQueue(nrPps.length); - } - - //printPositions(System.err, "Init: 3: Aft check-repeats"); - - // with repeats must advance some repeating pp's so they all start with differing tp's - if (hasRepeats) { - for (PhrasePositions pp: nrPps) { - if ((end=advanceRepeats(pp, end)) == Integer.MIN_VALUE) { - return Integer.MIN_VALUE; // ran out of a term -- done (no valid matches in current doc) - } - } - } - - //printPositions(System.err, "Init: 4: Aft advance-repeats"); - - // build queue from non repeating pps + return initComplex(); + } + + /** no repeats: simplest case, and most common. It is important to keep this piece of the code simple and efficient */ + private void initSimple() throws IOException { + //System.err.println("initSimple: doc: "+min.doc); pq.clear(); - for (PhrasePositions pp: nrPps) { + // position pps and build queue from list + for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max + pp.firstPosition(); if (pp.position > end) { end = pp.position; } pq.add(pp); } - - return end; } + /** with repeats: not so simple. */ + private boolean initComplex() throws IOException { + //System.err.println("initComplex: doc: "+min.doc); + placeFirstPositions(); + if (!advanceRepeatGroups()) { + return false; // PPs exhausted + } + fillQueue(); + return true; // PPs available + } + + /** move all PPs to their first position */ + private void placeFirstPositions() throws IOException { + for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max + pp.firstPosition(); + } + } + + /** Fill the queue (all pps are already placed */ + private void fillQueue() { + pq.clear(); + for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max + if (pp.position > end) { + end = pp.position; + } + pq.add(pp); + } + } + + /** At initialization (each doc), each repetition group is sorted by (query) offset. + * This provides the start condition: no collisions. + *

    Case 1: no multi-term repeats
    + * It is sufficient to advance each pp in the group by one less than its group index. + * So lesser pp is not advanced, 2nd one advance once, 3rd one advanced twice, etc. + *

    Case 2: multi-term repeats
    + * + * @return false if PPs are exhausted. + */ + private boolean advanceRepeatGroups() throws IOException { + for (PhrasePositions[] rg: rptGroups) { + if (hasMultiTermRpts) { + // more involved, some may not collide + int incr; + for (int i=0; i= 0) { + PhrasePositions pp2 = lesser(pp, rg[k]); + if (!advancePP(pp2)) { // at initialization always advance pp with higher offset + return false; // exhausted + } + if (pp2.rptInd < i) { // should not happen? + incr = 0; + break; + } + } + } + } else { + // simpler, we know exactly how much to advance + for (int j=1; j + * If there are repetitions, check if multi-term postings (MTP) are involved.

    + * Without MTP, once PPs are placed in the first candidate doc, repeats (and groups) are visible.
    + * With MTP, a more complex check is needed, up-front, as there may be "hidden collisions".
    + * For example P1 has {A,B}, P1 has {B,C}, and the first doc is: "A C B". At start, P1 would point + * to "A", p2 to "C", and it will not be identified that P1 and P2 are repetitions of each other.

    + * The more complex initialization has two parts:
    + * (1) identification of repetition groups.
    + * (2) advancing repeat groups at the start of the doc.
    + * For (1), a possible solution is to just create a single repetition group, + * made of all repeating pps. But this would slow down the check for collisions, + * as all pps would need to be checked. Instead, we compute "connected regions" + * on the bipartite graph of postings and terms. + */ + private boolean initFirstTime() throws IOException { + //System.err.println("initFirstTime: doc: "+min.doc); + checkedRpts = true; + placeFirstPositions(); + + LinkedHashMap rptTerms = repeatingTerms(); + hasRpts = !rptTerms.isEmpty(); + + if (hasRpts) { + rptStack = new PhrasePositions[numPostings]; // needed with repetitions + ArrayList> rgs = gatherRptGroups(rptTerms); + sortRptGroups(rgs); + if (!advanceRepeatGroups()) { + return false; // PPs exhausted + } + } + + fillQueue(); + return true; // PPs available + } + + /** sort each repetition group by (query) offset. + * Done only once (at first doc) and allows to initialize faster for each doc. */ + private void sortRptGroups(ArrayList> rgs) { + rptGroups = new PhrasePositions[rgs.size()][]; + Comparator cmprtr = new Comparator() { + public int compare(PhrasePositions pp1, PhrasePositions pp2) { + return pp1.offset - pp2.offset; + } + }; + for (int i=0; i> gatherRptGroups(LinkedHashMap rptTerms) throws IOException { + PhrasePositions[] rpp = repeatingPPs(rptTerms); + ArrayList> res = new ArrayList>(); + if (!hasMultiTermRpts) { + // simpler - no multi-terms - can base on positions in first doc + for (int i=0; i=0) continue; // already marked as a repetition + int tpPos = tpPos(pp); + for (int j=i+1; j=0 // already marked as a repetition + || pp2.offset == pp.offset // not a repetition: two PPs are originally in same offset in the query! + || tpPos(pp2) != tpPos) { // not a repetition + continue; + } + // a repetition + int g = pp.rptGroup; + if (g < 0) { + g = res.size(); + pp.rptGroup = g; + ArrayList rl = new ArrayList(2); + rl.add(pp); + res.add(rl); + } + pp2.rptGroup = g; + res.get(g).add(pp2); + } + } + } else { + // more involved - has multi-terms + ArrayList> tmp = new ArrayList>(); + ArrayList bb = ppTermsBitSets(rpp, rptTerms); + unionTermGroups(bb); + HashMap tg = termGroups(rptTerms, bb); + HashSet distinctGroupIDs = new HashSet(tg.values()); + for (int i=0; i()); + } + for (PhrasePositions pp : rpp) { + for (Term t: pp.terms) { + if (rptTerms.containsKey(t)) { + int g = tg.get(t); + tmp.get(g).add(pp); + assert pp.rptGroup==-1 || pp.rptGroup==g; + pp.rptGroup = g; + } + } + } + for (HashSet hs : tmp) { + res.add(new ArrayList(hs)); + } + } + return res; + } + /** Actual position in doc of a PhrasePosition, relies on that position = tpPos - offset) */ private final int tpPos(PhrasePositions pp) { return pp.position + pp.offset; } - -// private void printPositions(PrintStream ps, String title) { -// ps.println(); -// ps.println("---- "+title); -// int k = 0; -// if (nrPps!=null) { -// for (PhrasePositions pp: nrPps) { -// ps.println(" " + k++ + " " + pp); -// } -// } else { -// for (PhrasePositions pp=min; 0==k || pp!=min; pp = pp.next) { -// ps.println(" " + k++ + " " + pp); -// } -// } -// } + /** find repeating terms and assign them ordinal values */ + private LinkedHashMap repeatingTerms() { + LinkedHashMap tord = new LinkedHashMap(); + HashMap tcnt = new HashMap(); + for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max + for (Term t : pp.terms) { + Integer cnt0 = tcnt.get(t); + Integer cnt = cnt0==null ? new Integer(1) : new Integer(1+cnt0.intValue()); + tcnt.put(t, cnt); + if (cnt==2) { + tord.put(t,tord.size()); + } + } + } + return tord; + } + + /** find repeating pps, and for each, if has multi-terms, update this.hasMultiTermRpts */ + private PhrasePositions[] repeatingPPs(HashMap rptTerms) { + ArrayList rp = new ArrayList(); + for (PhrasePositions pp=min,prev=null; prev!=max; pp=(prev=pp).next) { // iterate cyclic list: done once handled max + for (Term t : pp.terms) { + if (rptTerms.containsKey(t)) { + rp.add(pp); + hasMultiTermRpts |= (pp.terms.length > 1); + break; + } + } + } + return rp.toArray(new PhrasePositions[0]); + } + + /** bit-sets - for each repeating pp, for each of its repeating terms, the term ordinal values is set */ + private ArrayList ppTermsBitSets(PhrasePositions[] rpp, HashMap tord) { + ArrayList bb = new ArrayList(rpp.length); + for (PhrasePositions pp : rpp) { + OpenBitSet b = new OpenBitSet(tord.size()); + Integer ord; + for (Term t: pp.terms) { + if ((ord=tord.get(t))!=null) { + b.set(ord); + } + } + bb.add(b); + } + return bb; + } + + /** union (term group) bit-sets until they are disjoint (O(n^^2)), and each group have different terms */ + private void unionTermGroups(ArrayList bb) { + int incr; + for (int i=0; i termGroups(LinkedHashMap tord, ArrayList bb) throws IOException { + HashMap tg = new HashMap(); + Term[] t = tord.keySet().toArray(new Term[0]); + for (int i=0; idot language description * for visualization. Example of use: * - *

    -   * PrintStream ps = new PrintStream("out.dot");
    -   * fst.toDot(ps);
    -   * ps.close();
    +   * 
    +   * PrintWriter pw = new PrintWriter("out.dot");
    +   * Util.toDot(fst, pw, true, true);
    +   * pw.close();
        * 
    * * and then, from command line: diff --git a/lucene/core/src/test/org/apache/lucene/codecs/lucene40/values/TestDocValues.java b/lucene/core/src/test/org/apache/lucene/codecs/lucene40/values/TestDocValues.java index 354a70e83fa..e6cff5ba2f8 100644 --- a/lucene/core/src/test/org/apache/lucene/codecs/lucene40/values/TestDocValues.java +++ b/lucene/core/src/test/org/apache/lucene/codecs/lucene40/values/TestDocValues.java @@ -188,7 +188,7 @@ public class TestDocValues extends LuceneTestCase { DocValues r = Ints.getValues(dir, "test", 2, Type.VAR_INTS, newIOContext(random)); Source source = getSource(r); assertEquals(i + " with min: " + minMax[i][0] + " max: " + minMax[i][1], - expectedTypes[i], source.type()); + expectedTypes[i], source.getType()); assertEquals(minMax[i][0], source.getInt(0)); assertEquals(minMax[i][1], source.getInt(1)); @@ -368,7 +368,7 @@ public class TestDocValues extends LuceneTestCase { DocValues r = Ints.getValues(dir, "test", NUM_VALUES + additionalDocs, type, newIOContext(random)); for (int iter = 0; iter < 2; iter++) { Source s = getSource(r); - assertEquals(type, s.type()); + assertEquals(type, s.getType()); for (int i = 0; i < NUM_VALUES; i++) { final long v = s.getInt(i); assertEquals("index " + i, values[i], v); diff --git a/lucene/core/src/test/org/apache/lucene/index/TestCompoundFile.java b/lucene/core/src/test/org/apache/lucene/index/TestCompoundFile.java index 7dca18e31ac..56f03875b18 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestCompoundFile.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestCompoundFile.java @@ -682,7 +682,7 @@ public class TestCompoundFile extends LuceneTestCase try { newDir.copy(csw, "d1", "d1", newIOContext(random)); fail("file does already exist"); - } catch (IOException e) { + } catch (IllegalArgumentException e) { // } out.close(); diff --git a/lucene/core/src/test/org/apache/lucene/index/TestCustomNorms.java b/lucene/core/src/test/org/apache/lucene/index/TestCustomNorms.java index 936bdf05498..a8a4fc3239a 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestCustomNorms.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestCustomNorms.java @@ -83,7 +83,7 @@ public class TestCustomNorms extends LuceneTestCase { assertNotNull(normValues); Source source = normValues.getSource(); assertTrue(source.hasArray()); - assertEquals(Type.FLOAT_32, normValues.type()); + assertEquals(Type.FLOAT_32, normValues.getType()); float[] norms = (float[]) source.getArray(); for (int i = 0; i < open.maxDoc(); i++) { Document document = open.document(i); diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDocValuesIndexing.java b/lucene/core/src/test/org/apache/lucene/index/TestDocValuesIndexing.java index a7d854492a0..2677190a42e 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestDocValuesIndexing.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestDocValuesIndexing.java @@ -148,8 +148,8 @@ public class TestDocValuesIndexing extends LuceneTestCase { Directory target = newDirectory(); IndexWriter w = new IndexWriter(target, writerConfig(random.nextBoolean())); - IndexReader r_1 = IndexReader.open(w_1, true); - IndexReader r_2 = IndexReader.open(w_2, true); + DirectoryReader r_1 = DirectoryReader.open(w_1, true); + DirectoryReader r_2 = DirectoryReader.open(w_2, true); if (random.nextBoolean()) { w.addIndexes(d_1, d_2); } else { @@ -163,7 +163,7 @@ public class TestDocValuesIndexing extends LuceneTestCase { // check values - IndexReader merged = IndexReader.open(w, true); + DirectoryReader merged = DirectoryReader.open(w, true); Source source_1 = getSource(getDocValues(r_1, first.name())); Source source_2 = getSource(getDocValues(r_2, second.name())); Source source_1_merged = getSource(getDocValues(merged, first.name())); @@ -260,7 +260,7 @@ public class TestDocValuesIndexing extends LuceneTestCase { FixedBitSet deleted = indexValues(w, numValues, val, numVariantList, withDeletions, 7); List closeables = new ArrayList(); - IndexReader r = IndexReader.open(w, true); + DirectoryReader r = DirectoryReader.open(w, true); final int numRemainingValues = numValues - deleted.cardinality(); final int base = r.numDocs() - numRemainingValues; // for FIXED_INTS_8 we use value mod 128 - to enable testing in @@ -338,7 +338,7 @@ public class TestDocValuesIndexing extends LuceneTestCase { final int bytesSize = 1 + atLeast(50); FixedBitSet deleted = indexValues(w, numValues, byteIndexValue, byteVariantList, withDeletions, bytesSize); - final IndexReader r = IndexReader.open(w, withDeletions); + final DirectoryReader r = DirectoryReader.open(w, withDeletions); assertEquals(0, r.numDeletedDocs()); final int numRemainingValues = numValues - deleted.cardinality(); final int base = r.numDocs() - numRemainingValues; @@ -422,13 +422,17 @@ public class TestDocValuesIndexing extends LuceneTestCase { for (Type val : numVariantList) { indexValues(w, numValues, val, numVariantList, false, 7); - IndexReader r = IndexReader.open(w, true); + DirectoryReader r = DirectoryReader.open(w, true); + if (val == Type.VAR_INTS) { + DocValues docValues = getDocValues(r, val.name()); + } DocValues docValues = getDocValues(r, val.name()); assertNotNull(docValues); // make sure we don't get a direct source since they don't support getArray() + if (val == Type.VAR_INTS) { + } Source source = docValues.getSource(); - - switch (source.type()) { + switch (source.getType()) { case FIXED_INTS_8: { assertTrue(source.hasArray()); @@ -465,7 +469,8 @@ public class TestDocValuesIndexing extends LuceneTestCase { } } break; - case VAR_INTS: + case VAR_INTS: + System.out.println(source.hasArray()); assertFalse(source.hasArray()); break; case FLOAT_32: @@ -487,7 +492,7 @@ public class TestDocValuesIndexing extends LuceneTestCase { } break; default: - fail("unexpected value " + source.type()); + fail("unexpected value " + source.getType()); } r.close(); } @@ -503,27 +508,28 @@ public class TestDocValuesIndexing extends LuceneTestCase { final int numValues = 50 + atLeast(10); // only single byte fixed straight supports getArray() indexValues(w, numValues, Type.BYTES_FIXED_STRAIGHT, null, false, 1); - IndexReader r = IndexReader.open(w, true); + DirectoryReader r = DirectoryReader.open(w, true); DocValues docValues = getDocValues(r, Type.BYTES_FIXED_STRAIGHT.name()); assertNotNull(docValues); // make sure we don't get a direct source since they don't support // getArray() Source source = docValues.getSource(); - switch (source.type()) { + switch (source.getType()) { case BYTES_FIXED_STRAIGHT: { BytesRef ref = new BytesRef(); - assertTrue(source.hasArray()); - byte[] values = (byte[]) source.getArray(); - for (int i = 0; i < numValues; i++) { - source.getBytes(i, ref); - assertEquals(1, ref.length); - assertEquals(values[i], ref.bytes[ref.offset]); + if (source.hasArray()) { + byte[] values = (byte[]) source.getArray(); + for (int i = 0; i < numValues; i++) { + source.getBytes(i, ref); + assertEquals(1, ref.length); + assertEquals(values[i], ref.bytes[ref.offset]); + } } } break; default: - fail("unexpected value " + source.type()); + fail("unexpected value " + source.getType()); } r.close(); w.close(); @@ -543,7 +549,7 @@ public class TestDocValuesIndexing extends LuceneTestCase { case 2: return values.getDirectSource(); case 1: - if(values.type() == Type.BYTES_VAR_SORTED || values.type() == Type.BYTES_FIXED_SORTED) { + if(values.getType() == Type.BYTES_VAR_SORTED || values.getType() == Type.BYTES_FIXED_SORTED) { return values.getSource().asSortedSource(); } default: @@ -925,4 +931,4 @@ public class TestDocValuesIndexing extends LuceneTestCase { r.close(); dir.close(); } -} +} \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java b/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java index 5a1e2b39b10..77032dab11c 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestDocumentWriter.java @@ -97,7 +97,7 @@ public class TestDocumentWriter extends LuceneTestCase { // omitNorms is true for (FieldInfo fi : reader.getFieldInfos()) { if (fi.isIndexed) { - assertTrue(fi.omitNorms == !reader.hasNorms(fi.name)); + assertTrue(fi.omitNorms == (reader.normValues(fi.name) == null)); } } reader.close(); @@ -330,10 +330,10 @@ public class TestDocumentWriter extends LuceneTestCase { SegmentReader reader = getOnlySegmentReader(IndexReader.open(dir)); FieldInfos fi = reader.getFieldInfos(); // f1 - assertFalse("f1 should have no norms", reader.hasNorms("f1")); + assertFalse("f1 should have no norms", fi.fieldInfo("f1").hasNorms()); assertEquals("omitTermFreqAndPositions field bit should not be set for f1", IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, fi.fieldInfo("f1").indexOptions); // f2 - assertTrue("f2 should have norms", reader.hasNorms("f2")); + assertTrue("f2 should have norms", fi.fieldInfo("f2").hasNorms()); assertEquals("omitTermFreqAndPositions field bit should be set for f2", IndexOptions.DOCS_ONLY, fi.fieldInfo("f2").indexOptions); reader.close(); } diff --git a/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java b/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java index 2c297278c4a..7c416ca2f0d 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestDuelingCodecs.java @@ -562,7 +562,7 @@ public class TestDuelingCodecs extends LuceneTestCase { public void assertDocValues(DocValues leftDocValues, DocValues rightDocValues) throws Exception { assertNotNull(info, leftDocValues); assertNotNull(info, rightDocValues); - assertEquals(info, leftDocValues.type(), rightDocValues.type()); + assertEquals(info, leftDocValues.getType(), rightDocValues.getType()); assertEquals(info, leftDocValues.getValueSize(), rightDocValues.getValueSize()); assertDocValuesSource(leftDocValues.getDirectSource(), rightDocValues.getDirectSource()); assertDocValuesSource(leftDocValues.getSource(), rightDocValues.getSource()); @@ -572,8 +572,8 @@ public class TestDuelingCodecs extends LuceneTestCase { * checks source API */ public void assertDocValuesSource(DocValues.Source left, DocValues.Source right) throws Exception { - DocValues.Type leftType = left.type(); - assertEquals(info, leftType, right.type()); + DocValues.Type leftType = left.getType(); + assertEquals(info, leftType, right.getType()); switch(leftType) { case VAR_INTS: case FIXED_INTS_8: diff --git a/lucene/core/src/test/org/apache/lucene/index/TestNorms.java b/lucene/core/src/test/org/apache/lucene/index/TestNorms.java index 811cf569fa1..eff3061085a 100755 --- a/lucene/core/src/test/org/apache/lucene/index/TestNorms.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestNorms.java @@ -96,7 +96,7 @@ public class TestNorms extends LuceneTestCase { assertNotNull(normValues); Source source = normValues.getSource(); assertTrue(source.hasArray()); - assertEquals(Type.FIXED_INTS_8, normValues.type()); + assertEquals(Type.FIXED_INTS_8, normValues.getType()); byte[] norms = (byte[]) source.getArray(); for (int i = 0; i < open.maxDoc(); i++) { Document document = open.document(i); @@ -128,9 +128,9 @@ public class TestNorms extends LuceneTestCase { assertFalse(fieldInfo.omitNorms); assertTrue(fieldInfo.isIndexed); if (secondWriteNorm) { - assertTrue(fieldInfo.normsPresent()); + assertTrue(fieldInfo.hasNorms()); } else { - assertFalse(fieldInfo.normsPresent()); + assertFalse(fieldInfo.hasNorms()); } IndexWriterConfig config = newIndexWriterConfig(TEST_VERSION_CURRENT, @@ -144,18 +144,18 @@ public class TestNorms extends LuceneTestCase { FieldInfo fi = mergedReader.getFieldInfos().fieldInfo(byteTestField); assertFalse(fi.omitNorms); assertTrue(fi.isIndexed); - assertFalse(fi.normsPresent()); + assertFalse(fi.hasNorms()); } else { FieldInfo fi = mergedReader.getFieldInfos().fieldInfo(byteTestField); assertFalse(fi.omitNorms); assertTrue(fi.isIndexed); - assertTrue(fi.normsPresent()); + assertTrue(fi.hasNorms()); DocValues normValues = mergedReader.normValues(byteTestField); assertNotNull(normValues); Source source = normValues.getSource(); assertTrue(source.hasArray()); - assertEquals(Type.FIXED_INTS_8, normValues.type()); + assertEquals(Type.FIXED_INTS_8, normValues.getType()); byte[] norms = (byte[]) source.getArray(); for (int i = 0; i < mergedReader.maxDoc(); i++) { Document document = mergedReader.document(i); diff --git a/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java b/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java index 965b02737a5..e6afa2d7e27 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestPostingsOffsets.java @@ -29,6 +29,7 @@ import org.apache.lucene.analysis.MockPayloadAnalyzer; import org.apache.lucene.analysis.Token; import org.apache.lucene.codecs.Codec; import org.apache.lucene.codecs.lucene40.Lucene40PostingsFormat; +import org.apache.lucene.codecs.memory.MemoryPostingsFormat; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; @@ -43,6 +44,8 @@ import org.apache.lucene.util.English; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util._TestUtil; +// TODO: we really need to test indexingoffsets, but then getting only docs / docs + freqs. +// not all codecs store prx separate... public class TestPostingsOffsets extends LuceneTestCase { IndexWriterConfig iwc; @@ -54,7 +57,11 @@ public class TestPostingsOffsets extends LuceneTestCase { if (Codec.getDefault().getName().equals("Lucene40")) { // pulsing etc are not implemented - iwc.setCodec(_TestUtil.alwaysPostingsFormat(new Lucene40PostingsFormat())); + if (random.nextBoolean()) { + iwc.setCodec(_TestUtil.alwaysPostingsFormat(new Lucene40PostingsFormat())); + } else { + iwc.setCodec(_TestUtil.alwaysPostingsFormat(new MemoryPostingsFormat())); + } } } @@ -126,7 +133,11 @@ public class TestPostingsOffsets extends LuceneTestCase { iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, analyzer); if (Codec.getDefault().getName().equals("Lucene40")) { // pulsing etc are not implemented - iwc.setCodec(_TestUtil.alwaysPostingsFormat(new Lucene40PostingsFormat())); + if (random.nextBoolean()) { + iwc.setCodec(_TestUtil.alwaysPostingsFormat(new Lucene40PostingsFormat())); + } else { + iwc.setCodec(_TestUtil.alwaysPostingsFormat(new MemoryPostingsFormat())); + } } iwc.setMergePolicy(newLogMergePolicy()); // will rely on docids a bit for skipping RandomIndexWriter w = new RandomIndexWriter(random, dir, iwc); diff --git a/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java b/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java index 9e5b58b76d8..c84382b9a23 100644 --- a/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java +++ b/lucene/core/src/test/org/apache/lucene/index/TestSegmentReader.java @@ -179,9 +179,9 @@ public class TestSegmentReader extends LuceneTestCase { for (int i=0; i foobars = new ArrayList(); + for (Failure f : runClasses.getFailures()) { + Matcher m = Pattern.compile("foobar[0-9]+").matcher(f.getTrace()); + while (m.find()) { + foobars.add(m.group()); + } + } + + Collections.sort(foobars); + Assert.assertEquals("[foobar1, foobar2, foobar3]", + Arrays.toString(foobars.toArray())); + } + + @Test + public void testExceptionWithinBefore() { + Result runClasses = JUnitCore.runClasses(Nested3.class); + Assert.assertEquals(1, runClasses.getFailureCount()); + Assert.assertEquals(1, runClasses.getRunCount()); + Assert.assertTrue(runClasses.getFailures().get(0).getTrace().contains("foobar")); + } + +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/lucene3x/PreFlexRWNormsConsumer.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/lucene3x/PreFlexRWNormsConsumer.java index 2bb54828a6c..fe0817300da 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/codecs/lucene3x/PreFlexRWNormsConsumer.java +++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/lucene3x/PreFlexRWNormsConsumer.java @@ -81,7 +81,7 @@ class PreFlexRWNormsConsumer extends PerDocConsumer { @Override protected boolean canMerge(FieldInfo info) { - return info.normsPresent(); + return info.hasNorms(); } @Override @@ -163,6 +163,11 @@ class PreFlexRWNormsConsumer extends PerDocConsumer { docIDs[upto] = docID; upto++; } + + @Override + protected Type getType() { + return Type.FIXED_INTS_8; + } } @@ -228,7 +233,7 @@ class PreFlexRWNormsConsumer extends PerDocConsumer { public void merge(MergeState mergeState) throws IOException { int numMergedDocs = 0; for (FieldInfo fi : mergeState.fieldInfos) { - if (fi.normsPresent()) { + if (fi.hasNorms()) { startField(fi); int numMergedDocsForField = 0; for (MergeState.IndexReaderAndLiveDocs reader : mergeState.readers) { diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java index bc374a9936e..6c11e41c87a 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java +++ b/lucene/test-framework/src/java/org/apache/lucene/index/FieldFilterAtomicReader.java @@ -100,11 +100,6 @@ public final class FieldFilterAtomicReader extends FilterAtomicReader { }); } - @Override - public boolean hasNorms(String field) throws IOException { - return hasField(field) ? super.hasNorms(field) : false; - } - @Override public Fields fields() throws IOException { final Fields f = super.fields(); diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java index 23d9077c374..042afa7a04f 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/index/ThreadedIndexingAndSearchingTestCase.java @@ -352,27 +352,21 @@ public abstract class ThreadedIndexingAndSearchingTestCase extends LuceneTestCas int seenTermCount = 0; int shift; int trigger; - if (totTermCount.get() < 10) { + if (totTermCount.get() < 30) { shift = 0; trigger = 1; } else { trigger = totTermCount.get()/30; shift = random.nextInt(trigger); } - while (true) { + while (System.currentTimeMillis() < stopTimeMS) { BytesRef term = termsEnum.next(); if (term == null) { - if (seenTermCount == 0) { - break; - } totTermCount.set(seenTermCount); break; } seenTermCount++; // search 30 terms - if (trigger == 0) { - trigger = 1; - } if ((seenTermCount + shift) % trigger == 0) { //if (VERBOSE) { //System.out.println(Thread.currentThread().getName() + " now search body:" + term.utf8ToString()); diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java index 7c10d88b758..fdb73b73d5b 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java @@ -104,6 +104,7 @@ import org.junit.internal.AssumptionViolatedException; import org.junit.rules.*; import org.junit.runner.*; import org.junit.runner.notification.RunListener; +import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; /** @@ -221,8 +222,6 @@ public abstract class LuceneTestCase extends Assert { private int savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount(); - private volatile Thread.UncaughtExceptionHandler savedUncaughtExceptionHandler = null; - /** * Some tests expect the directory to contain a single segment, and want to do tests on that segment's reader. * This is an utility method to help them. @@ -235,17 +234,6 @@ public abstract class LuceneTestCase extends Assert { return (SegmentReader) subReaders[0]; } - private static class UncaughtExceptionEntry { - public final Thread thread; - public final Throwable exception; - - public UncaughtExceptionEntry(Thread thread, Throwable exception) { - this.thread = thread; - this.exception = exception; - } - } - private List uncaughtExceptions = Collections.synchronizedList(new ArrayList()); - // default codec private static Codec savedCodec; @@ -265,9 +253,7 @@ public abstract class LuceneTestCase extends Assert { protected static Map stores; - /** @deprecated (4.0) until we fix no-fork problems in solr tests */ - @Deprecated - static List testClassesRun = new ArrayList(); + private static List testClassesRun = new ArrayList(); private static void initRandom() { assert !random.initialized; @@ -279,11 +265,46 @@ public abstract class LuceneTestCase extends Assert { @Deprecated private static boolean icuTested = false; + /** + * Stores the currently class under test. + */ + private static final StoreClassNameRule classNameRule = new StoreClassNameRule(); + + /** + * Catch any uncaught exceptions on threads within the suite scope and fail the test/ + * suite if they happen. + */ + private static final UncaughtExceptionsRule uncaughtExceptionsRule = new UncaughtExceptionsRule(); + + /** + * This controls how suite-level rules are nested. It is important that _all_ rules declared + * in {@link LuceneTestCase} are executed in proper order if they depend on each + * other. + */ @ClassRule - public static TestRule classRules = RuleChain.outerRule(new SystemPropertiesInvariantRule()); + public static TestRule classRules = RuleChain + .outerRule(new SystemPropertiesInvariantRule()) + .around(classNameRule) + .around(uncaughtExceptionsRule); + + /** + * This controls how individual test rules are nested. It is important that _all_ rules declared + * in {@link LuceneTestCase} are executed in proper order if they depend on each + * other. + */ + @Rule + public final TestRule ruleChain = RuleChain + .outerRule(new RememberThreadRule()) + .around(new UncaughtExceptionsRule()) + .around(new TestResultInterceptorRule()) + .around(new SystemPropertiesInvariantRule()) + .around(new InternalSetupTeardownRule()) + .around(new SubclassSetupTeardownRule()); @BeforeClass public static void beforeClassLuceneTestCaseJ4() { + testClassesRun.add(getTestClass().getSimpleName()); + initRandom(); tempDirs.clear(); stores = Collections.synchronizedMap(new IdentityHashMap()); @@ -436,6 +457,10 @@ public abstract class LuceneTestCase extends Assert { if (problem != null) { reportPartialFailureInfo(); } + + if (uncaughtExceptionsRule.hasUncaughtExceptions()) { + testsFailed = true; + } // if verbose or tests failed, report some information back if (VERBOSE || testsFailed || problem != null) { @@ -600,19 +625,6 @@ public abstract class LuceneTestCase extends Assert { } } - /** - * This controls how rules are nested. It is important that _all_ rules declared - * in {@link LuceneTestCase} are executed in proper order if they depend on each - * other. - */ - @Rule - public final TestRule ruleChain = RuleChain - .outerRule(new RememberThreadRule()) - .around(new TestResultInterceptorRule()) - .around(new SystemPropertiesInvariantRule()) - .around(new InternalSetupTeardownRule()) - .around(new SubclassSetupTeardownRule()); - /** * Internal {@link LuceneTestCase} setup before/after each test. */ @@ -622,15 +634,26 @@ public abstract class LuceneTestCase extends Assert { return new Statement() { @Override public void evaluate() throws Throwable { - setUpInternal(); // We simulate the previous behavior of @Before in that // if any statement below us fails, we just propagate the original // exception and do not call tearDownInternal. + setUpInternal(); + final ArrayList errors = new ArrayList(); + try { + // But we will collect errors from statements below and wrap them + // into a multiple so that tearDownInternal is called. + base.evaluate(); + } catch (Throwable t) { + errors.add(t); + } + + try { + tearDownInternal(); + } catch (Throwable t) { + errors.add(t); + } - // TODO: [DW] should this really be this way? We could use - // JUnit's MultipleFailureException and propagate both? - base.evaluate(); - tearDownInternal(); + MultipleFailureException.assertEmpty(errors); } }; } @@ -646,36 +669,6 @@ public abstract class LuceneTestCase extends Assert { Thread.currentThread().setName("LTC-main#seed=" + new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed)); - savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); - Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { - public void uncaughtException(Thread t, Throwable e) { - // org.junit.internal.AssumptionViolatedException in older releases - // org.junit.Assume.AssumptionViolatedException in recent ones - if (e.getClass().getName().endsWith("AssumptionViolatedException")) { - String where = ""; - for (StackTraceElement elem : e.getStackTrace()) { - if ( ! elem.getClassName().startsWith("org.junit")) { - where = elem.toString(); - break; - } - } - System.err.print("NOTE: Assume failed at " + where + " (ignored):"); - if (VERBOSE) { - System.err.println(); - e.printStackTrace(System.err); - } else { - System.err.print(" "); - System.err.println(e.getMessage()); - } - } else { - testsFailed = true; - uncaughtExceptions.add(new UncaughtExceptionEntry(t, e)); - if (savedUncaughtExceptionHandler != null) - savedUncaughtExceptionHandler.uncaughtException(t, e); - } - } - }); - savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount(); if (useNoMemoryExpensiveCodec) { @@ -774,15 +767,7 @@ public abstract class LuceneTestCase extends Assert { // this won't throw any exceptions or fail the test // if we change this, then change this logic checkRogueThreadsAfter(); - // restore the default uncaught exception handler - Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler); - - try { - checkUncaughtExceptionsAfter(); - } catch (Throwable t) { - if (problem == null) problem = t; - } - + try { // calling assertSaneFieldCaches here isn't as useful as having test // classes call it directly from the scope where the index readers @@ -809,7 +794,7 @@ public abstract class LuceneTestCase extends Assert { throw new RuntimeException(problem); } } - + /** check if the test still has threads running, we don't want them to * fail in a subsequent test and pass the blame to the wrong test */ private void checkRogueThreadsAfter() { @@ -818,26 +803,10 @@ public abstract class LuceneTestCase extends Assert { if (!testsFailed && rogueThreads > 0) { System.err.println("RESOURCE LEAK: test method: '" + getName() + "' left " + rogueThreads + " thread(s) running"); - // TODO: fail, but print seed for now - if (uncaughtExceptions.isEmpty()) { - reportAdditionalFailureInfo(); - } } } } - /** see if any other threads threw uncaught exceptions, and fail the test if so */ - private void checkUncaughtExceptionsAfter() { - if (!uncaughtExceptions.isEmpty()) { - System.err.println("The following exceptions were thrown by threads:"); - for (UncaughtExceptionEntry entry : uncaughtExceptions) { - System.err.println("*** Thread: " + entry.thread.getName() + " ***"); - entry.exception.printStackTrace(System.err); - } - fail("Some threads threw uncaught exceptions!"); - } - } - private final static int THREAD_STOP_GRACE_MSEC = 10; // jvm-wide list of 'rogue threads' we found, so they only get reported once. private final static IdentityHashMap rogueThreads = new IdentityHashMap(); @@ -1564,6 +1533,13 @@ public abstract class LuceneTestCase extends Assert { } return context; } + + /** + * Return the current class being tested. + */ + public static Class getTestClass() { + return classNameRule.getTestClass(); + } // initialized by the TestRunner static boolean useNoMemoryExpensiveCodec; diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCaseRunner.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCaseRunner.java index 322acd20daa..a0663b216e9 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCaseRunner.java +++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCaseRunner.java @@ -68,7 +68,6 @@ public class LuceneTestCaseRunner extends BlockJUnit4ClassRunner { Random r = new Random(runnerSeed); - LuceneTestCase.testClassesRun.add(getTestClass().getJavaClass().getSimpleName()); testMethods = new ArrayList(); for (Method m : getTestClass().getJavaClass().getMethods()) { // check if the current test's class has methods annotated with @Ignore diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/StoreClassNameRule.java b/lucene/test-framework/src/java/org/apache/lucene/util/StoreClassNameRule.java new file mode 100644 index 00000000000..c7d0d74fb53 --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/util/StoreClassNameRule.java @@ -0,0 +1,39 @@ +package org.apache.lucene.util; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class StoreClassNameRule implements TestRule { + private volatile Description description; + + @Override + public Statement apply(final Statement s, final Description d) { + if (!d.isSuite()) { + throw new IllegalArgumentException("This is a @ClassRule (applies to suites only)."); + } + + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + description = d; + s.evaluate(); + } finally { + description = null; + } + } + }; + } + + /** + * Returns the test class currently executing in this rule. + */ + public Class getTestClass() { + Description localDescription = description; + if (localDescription == null) { + throw new RuntimeException("The rule is not currently executing."); + } + return localDescription.getTestClass(); + } +} diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/UncaughtExceptionsRule.java b/lucene/test-framework/src/java/org/apache/lucene/util/UncaughtExceptionsRule.java new file mode 100644 index 00000000000..82e506558e8 --- /dev/null +++ b/lucene/test-framework/src/java/org/apache/lucene/util/UncaughtExceptionsRule.java @@ -0,0 +1,115 @@ +package org.apache.lucene.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.ArrayList; +import java.util.List; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.MultipleFailureException; +import org.junit.runners.model.Statement; + +/** + * Subscribes to + * {@link Thread#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)} + * and causes test/ suite failures if uncaught exceptions are detected. + */ +public class UncaughtExceptionsRule implements TestRule { + // This was originally volatile, but I don't think it needs to be. It's the same + // thread accessing it, always. + private UncaughtExceptionHandler savedUncaughtExceptionHandler; + + public static class UncaughtExceptionEntry { + public final Thread thread; + public final Throwable exception; + + public UncaughtExceptionEntry(Thread thread, Throwable exception) { + this.thread = thread; + this.exception = exception; + } + } + + @SuppressWarnings("serial") + private static class UncaughtExceptionsInBackgroundThread extends RuntimeException { + public UncaughtExceptionsInBackgroundThread(UncaughtExceptionEntry e) { + super("Uncaught exception by thread: " + e.thread, e.exception); + } + } + + // Lock on uncaughtExceptions to access. + private final List uncaughtExceptions = new ArrayList(); + + @Override + public Statement apply(final Statement s, final Description d) { + return new Statement() { + public void evaluate() throws Throwable { + final ArrayList errors = new ArrayList(); + try { + setupHandler(); + s.evaluate(); + } catch (Throwable t) { + errors.add(t); + } finally { + restoreHandler(); + } + + synchronized (uncaughtExceptions) { + for (UncaughtExceptionEntry e : uncaughtExceptions) { + errors.add(new UncaughtExceptionsInBackgroundThread(e)); + } + uncaughtExceptions.clear(); + } + + MultipleFailureException.assertEmpty(errors); + } + }; + } + + /** + * Just a check if anything's been caught. + */ + public boolean hasUncaughtExceptions() { + synchronized (uncaughtExceptions) { + return !uncaughtExceptions.isEmpty(); + } + } + + private void restoreHandler() { + Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler); + } + + private void setupHandler() { + savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread t, Throwable e) { + // org.junit.internal.AssumptionViolatedException in older releases + // org.junit.Assume.AssumptionViolatedException in recent ones + if (e.getClass().getName().endsWith("AssumptionViolatedException")) { + String where = ""; + for (StackTraceElement elem : e.getStackTrace()) { + if (!elem.getClassName().startsWith("org.junit")) { + where = elem.toString(); + break; + } + } + System.err.print("NOTE: Uncaught exception handler caught a failed assumption at " + + where + " (ignored):"); + } else { + synchronized (uncaughtExceptions) { + uncaughtExceptions.add(new UncaughtExceptionEntry(t, e)); + } + + StringWriter sw = new StringWriter(); + sw.write("\n===>\nUncaught exception by thread: " + t + "\n"); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + sw.write("<===\n"); + System.err.println(sw.toString()); + } + } + }); + } +} diff --git a/lucene/tools/custom-tasks.xml b/lucene/tools/custom-tasks.xml index 407f0075951..a1239b0fef5 100644 --- a/lucene/tools/custom-tasks.xml +++ b/lucene/tools/custom-tasks.xml @@ -45,9 +45,6 @@ - - - diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractFirstPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractFirstPassGroupingCollector.java index bc8d9908647..a5d8164ef1c 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractFirstPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractFirstPassGroupingCollector.java @@ -60,6 +60,7 @@ abstract public class AbstractFirstPassGroupingCollector exten * @param topNGroups How many top groups to keep. * @throws IOException If I/O related errors occur */ + @SuppressWarnings({"unchecked","rawtypes"}) public AbstractFirstPassGroupingCollector(Sort groupSort, int topNGroups) throws IOException { if (topNGroups < 1) { throw new IllegalArgumentException("topNGroups must be >= 1 (got " + topNGroups + ")"); @@ -284,7 +285,7 @@ abstract public class AbstractFirstPassGroupingCollector exten if (orderedGroups != null) { orderedGroups.add(group); assert orderedGroups.size() == topNGroups; - final CollectedSearchGroup newLast = orderedGroups.last(); + final CollectedSearchGroup newLast = orderedGroups.last(); // If we changed the value of the last group, or changed which group was last, then update bottom: if (group == newLast || prevLast != newLast) { for (FieldComparator fc : comparators) { @@ -295,8 +296,8 @@ abstract public class AbstractFirstPassGroupingCollector exten } private void buildSortedSet() { - final Comparator comparator = new Comparator() { - public int compare(CollectedSearchGroup o1, CollectedSearchGroup o2) { + final Comparator> comparator = new Comparator>() { + public int compare(CollectedSearchGroup o1, CollectedSearchGroup o2) { for (int compIDX = 0;; compIDX++) { FieldComparator fc = comparators[compIDX]; final int c = reversed[compIDX] * fc.compare(o1.comparatorSlot, o2.comparatorSlot); diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractGroupFacetCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractGroupFacetCollector.java new file mode 100644 index 00000000000..23e855a5fdc --- /dev/null +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractGroupFacetCollector.java @@ -0,0 +1,224 @@ +package org.apache.lucene.search.grouping; + +/* + * 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 org.apache.lucene.search.Collector; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.util.BytesRef; + +import java.io.IOException; +import java.util.*; + +/** + * Base class for computing grouped facets. + * + * @lucene.experimental + */ +public abstract class AbstractGroupFacetCollector extends Collector { + + protected final String groupField; + protected final String facetField; + protected final BytesRef facetPrefix; + + protected AbstractGroupFacetCollector(String groupField, String facetField, BytesRef facetPrefix) { + this.groupField = groupField; + this.facetField = facetField; + this.facetPrefix = facetPrefix; + } + + /** + * Returns grouped facet results that were computed over zero or more segments. + * Grouped facet counts are merged from zero or more segment results. + * + * @param size The total number of facets to include. This is typically offset + limit + * @param minCount The minimum count a facet entry should have to be included in the grouped facet result + * @param orderByCount Whether to sort the facet entries by facet entry count. If false then the facets + * are sorted lexicographically in ascending order. + * @return grouped facet results + * @throws IOException If I/O related errors occur during merging segment grouped facet counts. + */ + public abstract GroupedFacetResult mergeSegmentResults(int size, int minCount, boolean orderByCount) throws IOException; + + public void setScorer(Scorer scorer) throws IOException { + } + + public boolean acceptsDocsOutOfOrder() { + return true; + } + + /** + * The grouped facet result. Containing grouped facet entries, total count and total missing count. + */ + public static class GroupedFacetResult { + + private final static Comparator orderByCountAndValue = new Comparator() { + + public int compare(FacetEntry a, FacetEntry b) { + int cmp = b.count - a.count; // Highest count first! + if (cmp != 0) { + return cmp; + } + return a.value.compareTo(b.value); + } + + }; + + private final static Comparator orderByValue = new Comparator() { + + public int compare(FacetEntry a, FacetEntry b) { + return a.value.compareTo(b.value); + } + + }; + + private final int maxSize; + private final NavigableSet facetEntries; + private final int totalMissingCount; + private final int totalCount; + + private int currentMin; + + public GroupedFacetResult(int size, int minCount, boolean orderByCount, int totalCount, int totalMissingCount) { + this.facetEntries = new TreeSet(orderByCount ? orderByCountAndValue : orderByValue); + this.totalMissingCount = totalMissingCount; + this.totalCount = totalCount; + maxSize = size; + currentMin = minCount; + } + + public void addFacetCount(BytesRef facetValue, int count) { + if (count < currentMin) { + return; + } + + FacetEntry facetEntry = new FacetEntry(facetValue, count); + if (facetEntries.size() == maxSize) { + if (facetEntries.higher(facetEntry) == null) { + return; + } + facetEntries.pollLast(); + } + facetEntries.add(facetEntry); + + if (facetEntries.size() == maxSize) { + currentMin = facetEntries.last().count; + } + } + + /** + * Returns a list of facet entries to be rendered based on the specified offset and limit. + * The facet entries are retrieved from the facet entries collected during merging. + * + * @param offset The offset in the collected facet entries during merging + * @param limit The number of facets to return starting from the offset. + * @return a list of facet entries to be rendered based on the specified offset and limit + */ + public List getFacetEntries(int offset, int limit) { + List entries = new LinkedList(); + limit += offset; + + int i = 0; + for (FacetEntry facetEntry : facetEntries) { + if (i < offset) { + i++; + continue; + } + if (i++ >= limit) { + break; + } + entries.add(facetEntry); + } + return entries; + } + + /** + * Returns the sum of all facet entries counts. + * + * @return the sum of all facet entries counts + */ + public int getTotalCount() { + return totalCount; + } + + /** + * Returns the number of groups that didn't have a facet value. + * + * @return the number of groups that didn't have a facet value + */ + public int getTotalMissingCount() { + return totalMissingCount; + } + } + + /** + * Represents a facet entry with a value and a count. + */ + public static class FacetEntry { + + private final BytesRef value; + private final int count; + + public FacetEntry(BytesRef value, int count) { + this.value = value; + this.count = count; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FacetEntry that = (FacetEntry) o; + + if (count != that.count) return false; + if (!value.equals(that.value)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = value.hashCode(); + result = 31 * result + count; + return result; + } + + @Override + public String toString() { + return "FacetEntry{" + + "value=" + value.utf8ToString() + + ", count=" + count + + '}'; + } + + /** + * @return The value of this facet entry + */ + public BytesRef getValue() { + return value; + } + + /** + * @return The count (number of groups) of this facet entry. + */ + public int getCount() { + return count; + } + } + +} diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractSecondPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractSecondPassGroupingCollector.java index c4d40420ee8..6dbfcd40270 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractSecondPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/AbstractSecondPassGroupingCollector.java @@ -66,7 +66,7 @@ public abstract class AbstractSecondPassGroupingCollector exte for (SearchGroup group : groups) { //System.out.println(" prep group=" + (group.groupValue == null ? "null" : group.groupValue.utf8ToString())); - final TopDocsCollector collector; + final TopDocsCollector collector; if (withinGroupSort == null) { // Sort by score collector = TopScoreDocCollector.create(maxDocsPerGroup, true); @@ -120,11 +120,11 @@ public abstract class AbstractSecondPassGroupingCollector exte } public TopGroups getTopGroups(int withinGroupOffset) { - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) final GroupDocs[] groupDocsResult = (GroupDocs[]) new GroupDocs[groups.size()]; int groupIDX = 0; - for(SearchGroup group : groups) { + for(SearchGroup group : groups) { final SearchGroupDocs groupDocs = groupMap.get(group.groupValue); final TopDocs topDocs = groupDocs.collector.topDocs(withinGroupOffset, maxDocsPerGroup); groupDocsResult[groupIDX++] = new GroupDocs(topDocs.getMaxScore(), @@ -146,9 +146,9 @@ public abstract class AbstractSecondPassGroupingCollector exte public class SearchGroupDocs { public final GROUP_VALUE_TYPE groupValue; - public final TopDocsCollector collector; + public final TopDocsCollector collector; - public SearchGroupDocs(GROUP_VALUE_TYPE groupValue, TopDocsCollector collector) { + public SearchGroupDocs(GROUP_VALUE_TYPE groupValue, TopDocsCollector collector) { this.groupValue = groupValue; this.collector = collector; } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java index b75441ba56a..60739c0b239 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java @@ -18,25 +18,14 @@ package org.apache.lucene.search.grouping; */ -import java.io.IOException; - import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.index.IndexWriter; // javadocs -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.Filter; -import org.apache.lucene.search.Scorer; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopFieldCollector; -import org.apache.lucene.search.TopScoreDocCollector; -import org.apache.lucene.search.Weight; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.*; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.PriorityQueue; +import java.io.IOException; + /** BlockGroupingCollector performs grouping with a * single pass collector, as long as you are grouping by a * doc block field, ie all documents sharing a given group @@ -265,7 +254,7 @@ public class BlockGroupingCollector extends Collector { this.topNGroups = topNGroups; final SortField[] sortFields = groupSort.getSort(); - comparators = new FieldComparator[sortFields.length]; + comparators = new FieldComparator[sortFields.length]; compIDXEnd = comparators.length - 1; reversed = new int[sortFields.length]; for (int i = 0; i < sortFields.length; i++) { @@ -301,7 +290,7 @@ public class BlockGroupingCollector extends Collector { * @param fillSortFields If true then the Comparable * values for the sort fields will be set */ - public TopGroups getTopGroups(Sort withinGroupSort, int groupOffset, int withinGroupOffset, int maxDocsPerGroup, boolean fillSortFields) throws IOException { + public TopGroups getTopGroups(Sort withinGroupSort, int groupOffset, int withinGroupOffset, int maxDocsPerGroup, boolean fillSortFields) throws IOException { //if (queueFull) { //System.out.println("getTopGroups groupOffset=" + groupOffset + " topNGroups=" + topNGroups); @@ -316,7 +305,7 @@ public class BlockGroupingCollector extends Collector { final FakeScorer fakeScorer = new FakeScorer(); - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) final GroupDocs[] groups = new GroupDocs[groupQueue.size() - groupOffset]; for(int downTo=groupQueue.size()-groupOffset-1;downTo>=0;downTo--) { final OneGroup og = groupQueue.pop(); @@ -350,7 +339,7 @@ public class BlockGroupingCollector extends Collector { final Object[] groupSortValues; if (fillSortFields) { - groupSortValues = new Comparable[comparators.length]; + groupSortValues = new Comparable[comparators.length]; for(int sortFieldIDX=0;sortFieldIDX { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - SearchGroup that = (SearchGroup) o; + SearchGroup that = (SearchGroup) o; if (groupValue == null) { if (that.groupValue != null) { @@ -113,7 +113,7 @@ public class SearchGroup { // Only for assert private boolean neverEquals(Object _other) { if (_other instanceof MergedGroup) { - MergedGroup other = (MergedGroup) _other; + MergedGroup other = (MergedGroup) _other; if (groupValue == null) { assert other.groupValue != null; } else { @@ -130,7 +130,7 @@ public class SearchGroup { assert neverEquals(_other); if (_other instanceof MergedGroup) { - MergedGroup other = (MergedGroup) _other; + MergedGroup other = (MergedGroup) _other; if (groupValue == null) { return other == null; } else { @@ -158,7 +158,7 @@ public class SearchGroup { public GroupComparator(Sort groupSort) throws IOException { final SortField[] sortFields = groupSort.getSort(); - comparators = new FieldComparator[sortFields.length]; + comparators = new FieldComparator[sortFields.length]; reversed = new int[sortFields.length]; for (int compIDX = 0; compIDX < sortFields.length; compIDX++) { final SortField sortField = sortFields[compIDX]; @@ -167,7 +167,7 @@ public class SearchGroup { } } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) public int compare(MergedGroup group, MergedGroup other) { if (group == other) { return 0; @@ -202,7 +202,7 @@ public class SearchGroup { groupsSeen = new HashMap>(); } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) private void updateNextGroup(int topN, ShardIter shard) { while(shard.iter.hasNext()) { final SearchGroup group = shard.next(); diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java index 4d23c8d2d95..5739d76b2da 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java @@ -111,7 +111,7 @@ public class TopGroups { } } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) final GroupDocs[] mergedGroupDocs = new GroupDocs[numGroups]; final TopDocs[] shardTopDocs = new TopDocs[shardGroups.length]; @@ -124,7 +124,7 @@ public class TopGroups { for(int shardIDX=0;shardIDX extends AbstractAllGroupHeadsCollector { +public abstract class DVAllGroupHeadsCollector> extends AbstractAllGroupHeadsCollector { final String groupField; final boolean diskResident; @@ -65,40 +67,42 @@ public abstract class DVAllGroupHeadsCollectorAbstractAllGroupHeadsCollector instance based on the supplied arguments * @throws IOException If I/O related errors occur */ - public static AbstractAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup, DocValues.Type type, boolean diskResident) throws IOException { + @SuppressWarnings("unchecked") + public static > DVAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup, DocValues.Type type, boolean diskResident) throws IOException { switch (type) { case VAR_INTS: case FIXED_INTS_8: case FIXED_INTS_16: case FIXED_INTS_32: case FIXED_INTS_64: - return new GeneralAllGroupHeadsCollector.Lng(groupField, type, sortWithinGroup, diskResident); + // Type erasure b/c otherwise we have inconvertible types... + return (DVAllGroupHeadsCollector) new GeneralAllGroupHeadsCollector.Lng(groupField, type, sortWithinGroup, diskResident); case FLOAT_32: case FLOAT_64: - return new GeneralAllGroupHeadsCollector.Dbl(groupField, type, sortWithinGroup, diskResident); + return (DVAllGroupHeadsCollector) new GeneralAllGroupHeadsCollector.Dbl(groupField, type, sortWithinGroup, diskResident); case BYTES_FIXED_STRAIGHT: case BYTES_FIXED_DEREF: case BYTES_VAR_STRAIGHT: case BYTES_VAR_DEREF: - return new GeneralAllGroupHeadsCollector.BR(groupField, type, sortWithinGroup, diskResident); + return (DVAllGroupHeadsCollector) new GeneralAllGroupHeadsCollector.BR(groupField, type, sortWithinGroup, diskResident); case BYTES_VAR_SORTED: case BYTES_FIXED_SORTED: - return new GeneralAllGroupHeadsCollector.SortedBR(groupField, type, sortWithinGroup, diskResident); + return (DVAllGroupHeadsCollector) new GeneralAllGroupHeadsCollector.SortedBR(groupField, type, sortWithinGroup, diskResident); default: throw new IllegalArgumentException(String.format("ValueType %s not supported", type)); } } - static class GroupHead extends AbstractAllGroupHeadsCollector.GroupHead { + static class GroupHead extends AbstractAllGroupHeadsCollector.GroupHead> { - final FieldComparator[] comparators; + final FieldComparator[] comparators; AtomicReaderContext readerContext; Scorer scorer; - GroupHead(Comparable groupValue, Sort sort, int doc, AtomicReaderContext readerContext, Scorer scorer) throws IOException { + GroupHead(Comparable groupValue, Sort sort, int doc, AtomicReaderContext readerContext, Scorer scorer) throws IOException { super(groupValue, doc + readerContext.docBase); final SortField[] sortFields = sort.getSort(); - comparators = new FieldComparator[sortFields.length]; + comparators = new FieldComparator[sortFields.length]; for (int i = 0; i < sortFields.length; i++) { comparators[i] = sortFields[i].getComparator(1, i).setNextReader(readerContext); comparators[i].setScorer(scorer); @@ -115,7 +119,7 @@ public abstract class DVAllGroupHeadsCollector comparator : comparators) { comparator.copy(0, doc); comparator.setBottom(0); } @@ -156,12 +160,12 @@ public abstract class DVAllGroupHeadsCollector { private final Sort sortWithinGroup; - private final Map groups; + private final Map, GroupHead> groups; GeneralAllGroupHeadsCollector(String groupField, DocValues.Type valueType, Sort sortWithinGroup, boolean diskResident) throws IOException { super(groupField, valueType, sortWithinGroup.getSort().length, diskResident); this.sortWithinGroup = sortWithinGroup; - groups = new HashMap(); + groups = new HashMap, GroupHead>(); final SortField[] sortFields = sortWithinGroup.getSort(); for (int i = 0; i < sortFields.length; i++) { @@ -170,7 +174,7 @@ public abstract class DVAllGroupHeadsCollector groupValue = getGroupValue(doc); GroupHead groupHead = groups.get(groupValue); if (groupHead == null) { groupHead = new GroupHead(groupValue, sortWithinGroup, doc, readerContext, scorer); @@ -182,9 +186,9 @@ public abstract class DVAllGroupHeadsCollector getGroupValue(int doc); - protected abstract Comparable duplicate(Comparable value); + protected abstract Comparable duplicate(Comparable value); protected Collection getCollectedGroupHeads() { return groups.values(); @@ -204,7 +208,7 @@ public abstract class DVAllGroupHeadsCollector comparator : groupHead.comparators) { comparator.setScorer(scorer); } } @@ -218,11 +222,11 @@ public abstract class DVAllGroupHeadsCollector getGroupValue(int doc) { return source.getBytes(doc, scratchBytesRef); } - protected Comparable duplicate(Comparable value) { + protected Comparable duplicate(Comparable value) { return BytesRef.deepCopyOf((BytesRef) value); } @@ -244,11 +248,11 @@ public abstract class DVAllGroupHeadsCollector getGroupValue(int doc) { return source.getBytes(doc, scratchBytesRef); } - protected Comparable duplicate(Comparable value) { + protected Comparable duplicate(Comparable value) { return BytesRef.deepCopyOf((BytesRef) value); } @@ -266,11 +270,11 @@ public abstract class DVAllGroupHeadsCollector getGroupValue(int doc) { return source.getInt(doc); } - protected Comparable duplicate(Comparable value) { + protected Comparable duplicate(Comparable value) { return value; } @@ -287,11 +291,11 @@ public abstract class DVAllGroupHeadsCollector getGroupValue(int doc) { return source.getFloat(doc); } - protected Comparable duplicate(Comparable value) { + protected Comparable duplicate(Comparable value) { return value; } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVAllGroupsCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVAllGroupsCollector.java index 28cf37e3901..893239a3e38 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVAllGroupsCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVAllGroupsCollector.java @@ -52,25 +52,30 @@ public abstract class DVAllGroupsCollector extends AbstractAll * heap usage is 4 bytes * initialSize. Not all concrete implementions use this! * @return the most optimal all groups collector implementation for grouping by {@link DocValues} */ - public static DVAllGroupsCollector create(String groupField, DocValues.Type type, boolean diskResident, int initialSize) { + @SuppressWarnings("unchecked") + public static DVAllGroupsCollector create(String groupField, DocValues.Type type, boolean diskResident, int initialSize) { switch (type) { case VAR_INTS: case FIXED_INTS_8: case FIXED_INTS_16: case FIXED_INTS_32: case FIXED_INTS_64: - return new Lng(groupField, type, diskResident); + // Type erasure b/c otherwise we have inconvertible types... + return (DVAllGroupsCollector) new Lng(groupField, type, diskResident); case FLOAT_32: case FLOAT_64: - return new Dbl(groupField, type, diskResident); + // Type erasure b/c otherwise we have inconvertible types... + return (DVAllGroupsCollector) new Dbl(groupField, type, diskResident); case BYTES_FIXED_STRAIGHT: case BYTES_FIXED_DEREF: case BYTES_VAR_STRAIGHT: case BYTES_VAR_DEREF: - return new BR(groupField, type, diskResident); + // Type erasure b/c otherwise we have inconvertible types... + return (DVAllGroupsCollector) new BR(groupField, type, diskResident); case BYTES_VAR_SORTED: case BYTES_FIXED_SORTED: - return new SortedBR(groupField, type, diskResident, initialSize); + // Type erasure b/c otherwise we have inconvertible types... + return (DVAllGroupsCollector) new SortedBR(groupField, type, diskResident, initialSize); default: throw new IllegalArgumentException(String.format("ValueType %s not supported", type)); } @@ -87,7 +92,7 @@ public abstract class DVAllGroupsCollector extends AbstractAll * @param diskResident Wether the values to group by should be disk resident * @return the most optimal all groups collector implementation for grouping by {@link DocValues} */ - public static DVAllGroupsCollector create(String groupField, DocValues.Type type, boolean diskResident) { + public static DVAllGroupsCollector create(String groupField, DocValues.Type type, boolean diskResident) { return create(groupField, type, diskResident, DEFAULT_INITIAL_SIZE); } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVFirstPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVFirstPassGroupingCollector.java index 2f78401b67d..2176f0ea8b8 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVFirstPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVFirstPassGroupingCollector.java @@ -20,7 +20,6 @@ package org.apache.lucene.search.grouping.dv; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.DocValues; import org.apache.lucene.index.DocValues.Type; // javadocs -import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Sort; import org.apache.lucene.search.grouping.AbstractFirstPassGroupingCollector; import org.apache.lucene.util.BytesRef; @@ -38,25 +37,42 @@ public abstract class DVFirstPassGroupingCollector extends Abs final boolean diskResident; final DocValues.Type valueType; - public static DVFirstPassGroupingCollector create(Sort groupSort, int topNGroups, String groupField, DocValues.Type type, boolean diskResident) throws IOException { + /** + * Constructs a {@link DVFirstPassGroupingCollector}. + * Selects and constructs the most optimal first pass collector implementation for grouping by {@link DocValues}. + * + * @param groupField The field to group by + * @param topNGroups The maximum top number of groups to return. Typically this equals to offset + rows. + * @param diskResident Whether the values to group by should be disk resident + * @param type The {@link Type} which is used to select a concrete implementation. + * @param groupSort The sort used for the groups + * @return the most optimal first pass collector implementation for grouping by {@link DocValues} + * @throws IOException If I/O related errors occur + */ + @SuppressWarnings("unchecked") + public static DVFirstPassGroupingCollector create(Sort groupSort, int topNGroups, String groupField, DocValues.Type type, boolean diskResident) throws IOException { switch (type) { case VAR_INTS: case FIXED_INTS_8: case FIXED_INTS_16: case FIXED_INTS_32: case FIXED_INTS_64: - return new Lng(groupSort, topNGroups, groupField, diskResident, type); + // Type erasure b/c otherwise we have inconvertible types... + return (DVFirstPassGroupingCollector) new Lng(groupSort, topNGroups, groupField, diskResident, type); case FLOAT_32: case FLOAT_64: - return new Dbl(groupSort, topNGroups, groupField, diskResident, type); + // Type erasure b/c otherwise we have inconvertible types... + return (DVFirstPassGroupingCollector) new Dbl(groupSort, topNGroups, groupField, diskResident, type); case BYTES_FIXED_STRAIGHT: case BYTES_FIXED_DEREF: case BYTES_VAR_STRAIGHT: case BYTES_VAR_DEREF: - return new BR(groupSort, topNGroups, groupField, diskResident, type); + // Type erasure b/c otherwise we have inconvertible types... + return (DVFirstPassGroupingCollector) new BR(groupSort, topNGroups, groupField, diskResident, type); case BYTES_VAR_SORTED: case BYTES_FIXED_SORTED: - return new SortedBR(groupSort, topNGroups, groupField, diskResident, type); + // Type erasure b/c otherwise we have inconvertible types... + return (DVFirstPassGroupingCollector) new SortedBR(groupSort, topNGroups, groupField, diskResident, type); default: throw new IllegalArgumentException(String.format("ValueType %s not supported", type)); } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVSecondPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVSecondPassGroupingCollector.java index c3460f7980b..a1cfd6bb093 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVSecondPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/dv/DVSecondPassGroupingCollector.java @@ -19,12 +19,12 @@ package org.apache.lucene.search.grouping.dv; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.DocValues; -import org.apache.lucene.index.DocValues.Type; // javadocs +import org.apache.lucene.index.DocValues.Type; import org.apache.lucene.search.Sort; import org.apache.lucene.search.grouping.AbstractSecondPassGroupingCollector; import org.apache.lucene.search.grouping.SearchGroup; -import org.apache.lucene.util.SentinelIntSet; import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.SentinelIntSet; import java.io.IOException; import java.util.Collection; @@ -54,10 +54,10 @@ public abstract class DVSecondPassGroupingCollector extends Abstrac * @throws IOException If I/O related errors occur */ @SuppressWarnings("unchecked") - public static DVSecondPassGroupingCollector create(String groupField, + public static DVSecondPassGroupingCollector create(String groupField, boolean diskResident, DocValues.Type type, - Collection searchGroups, + Collection> searchGroups, Sort groupSort, Sort withinGroupSort, int maxDocsPerGroup, @@ -71,21 +71,21 @@ public abstract class DVSecondPassGroupingCollector extends Abstrac case FIXED_INTS_32: case FIXED_INTS_64: // Type erasure b/c otherwise we have inconvertible types... - return new Lng(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); + return (DVSecondPassGroupingCollector) new Lng(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); case FLOAT_32: case FLOAT_64: // Type erasure b/c otherwise we have inconvertible types... - return new Dbl(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); + return (DVSecondPassGroupingCollector) new Dbl(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); case BYTES_FIXED_STRAIGHT: case BYTES_FIXED_DEREF: case BYTES_VAR_STRAIGHT: case BYTES_VAR_DEREF: // Type erasure b/c otherwise we have inconvertible types... - return new BR(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); + return (DVSecondPassGroupingCollector) new BR(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); case BYTES_VAR_SORTED: case BYTES_FIXED_SORTED: // Type erasure b/c otherwise we have inconvertible types... - return new SortedBR(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); + return (DVSecondPassGroupingCollector) new SortedBR(groupField, type, diskResident, (Collection) searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); default: throw new IllegalArgumentException(String.format("ValueType %s not supported", type)); } @@ -192,7 +192,7 @@ public abstract class DVSecondPassGroupingCollector extends Abstrac private final BytesRef spare = new BytesRef(); private final SentinelIntSet ordSet; - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) SortedBR(String groupField, DocValues.Type valueType, boolean diskResident, Collection> searchGroups, Sort groupSort, Sort withinGroupSort, int maxDocsPerGroup, boolean getScores, boolean getMaxScores, boolean fillSortFields) throws IOException { super(groupField, valueType, diskResident, searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); ordSet = new SentinelIntSet(groupMap.size(), -1); diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupHeadsCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupHeadsCollector.java index d07ec6bd421..3755ad64454 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupHeadsCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupHeadsCollector.java @@ -41,7 +41,7 @@ import java.util.Map; public class FunctionAllGroupHeadsCollector extends AbstractAllGroupHeadsCollector { private final ValueSource groupBy; - private final Map vsContext; + private final Map vsContext; private final Map groups; private final Sort sortWithinGroup; @@ -57,7 +57,7 @@ public class FunctionAllGroupHeadsCollector extends AbstractAllGroupHeadsCollect * @param vsContext The ValueSource context * @param sortWithinGroup The sort within a group */ - public FunctionAllGroupHeadsCollector(ValueSource groupBy, Map vsContext, Sort sortWithinGroup) { + public FunctionAllGroupHeadsCollector(ValueSource groupBy, Map vsContext, Sort sortWithinGroup) { super(sortWithinGroup.getSort().length); groups = new HashMap(); this.sortWithinGroup = sortWithinGroup; @@ -120,6 +120,7 @@ public class FunctionAllGroupHeadsCollector extends AbstractAllGroupHeadsCollect final FieldComparator[] comparators; + @SuppressWarnings({"unchecked","rawtypes"}) private GroupHead(MutableValue groupValue, Sort sort, int doc) throws IOException { super(groupValue, doc + readerContext.docBase); final SortField[] sortFields = sort.getSort(); diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupsCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupsCollector.java index b947b23a9e7..cbfda8e9aac 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupsCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionAllGroupsCollector.java @@ -43,7 +43,7 @@ import java.util.TreeSet; */ public class FunctionAllGroupsCollector extends AbstractAllGroupsCollector { - private final Map vsContext; + private final Map vsContext; private final ValueSource groupBy; private final SortedSet groups = new TreeSet(); @@ -56,7 +56,7 @@ public class FunctionAllGroupsCollector extends AbstractAllGroupsCollector vsContext) { this.vsContext = vsContext; this.groupBy = groupBy; } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionFirstPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionFirstPassGroupingCollector.java index 6c078f9c38a..f80c6ea7ed3 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionFirstPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionFirstPassGroupingCollector.java @@ -36,7 +36,7 @@ import java.util.Map; public class FunctionFirstPassGroupingCollector extends AbstractFirstPassGroupingCollector { private final ValueSource groupByVS; - private final Map vsContext; + private final Map vsContext; private FunctionValues docValues; private FunctionValues.ValueFiller filler; @@ -56,7 +56,7 @@ public class FunctionFirstPassGroupingCollector extends AbstractFirstPassGroupin * @param topNGroups How many top groups to keep. * @throws IOException When I/O related errors occur */ - public FunctionFirstPassGroupingCollector(ValueSource groupByVS, Map vsContext, Sort groupSort, int topNGroups) throws IOException { + public FunctionFirstPassGroupingCollector(ValueSource groupByVS, Map vsContext, Sort groupSort, int topNGroups) throws IOException { super(groupSort, topNGroups); this.groupByVS = groupByVS; this.vsContext = vsContext; diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionSecondPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionSecondPassGroupingCollector.java index 0dd466167a4..5c11df1b213 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionSecondPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/function/FunctionSecondPassGroupingCollector.java @@ -39,7 +39,7 @@ import java.util.Map; public class FunctionSecondPassGroupingCollector extends AbstractSecondPassGroupingCollector { private final ValueSource groupByVS; - private final Map vsContext; + private final Map vsContext; private FunctionValues.ValueFiller filler; private MutableValue mval; @@ -58,7 +58,7 @@ public class FunctionSecondPassGroupingCollector extends AbstractSecondPassGroup * @param vsContext The value source context * @throws IOException IOException When I/O related errors occur */ - public FunctionSecondPassGroupingCollector(Collection> searchGroups, Sort groupSort, Sort withinGroupSort, int maxDocsPerGroup, boolean getScores, boolean getMaxScores, boolean fillSortFields, ValueSource groupByVS, Map vsContext) throws IOException { + public FunctionSecondPassGroupingCollector(Collection> searchGroups, Sort groupSort, Sort withinGroupSort, int maxDocsPerGroup, boolean getScores, boolean getMaxScores, boolean fillSortFields, ValueSource groupByVS, Map vsContext) throws IOException { super(searchGroups, groupSort, withinGroupSort, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); this.groupByVS = groupByVS; this.vsContext = vsContext; diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermAllGroupHeadsCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermAllGroupHeadsCollector.java index 15c632fe5cf..8e5f6cd4ed3 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermAllGroupHeadsCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermAllGroupHeadsCollector.java @@ -18,11 +18,10 @@ package org.apache.lucene.search.grouping.term; */ import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.*; import org.apache.lucene.search.grouping.AbstractAllGroupHeadsCollector; -import org.apache.lucene.util.SentinelIntSet; import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.SentinelIntSet; import java.io.IOException; import java.util.*; @@ -34,7 +33,7 @@ import java.util.*; * * @lucene.experimental */ -public abstract class TermAllGroupHeadsCollector extends AbstractAllGroupHeadsCollector { +public abstract class TermAllGroupHeadsCollector> extends AbstractAllGroupHeadsCollector { private static final int DEFAULT_INITIAL_SIZE = 128; @@ -60,7 +59,7 @@ public abstract class TermAllGroupHeadsCollectorAbstractAllGroupHeadsCollector instance based on the supplied arguments * @throws IOException If I/O related errors occur */ - public static AbstractAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup) throws IOException { + public static AbstractAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup) throws IOException { return create(groupField, sortWithinGroup, DEFAULT_INITIAL_SIZE); } @@ -76,7 +75,7 @@ public abstract class TermAllGroupHeadsCollectorAbstractAllGroupHeadsCollector instance based on the supplied arguments * @throws IOException If I/O related errors occur */ - public static AbstractAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup, int initialSize) throws IOException { + public static AbstractAllGroupHeadsCollector create(String groupField, Sort sortWithinGroup, int initialSize) throws IOException { boolean sortAllScore = true; boolean sortAllFieldValue = true; @@ -157,7 +156,7 @@ public abstract class TermAllGroupHeadsCollector comparator : groupHead.comparators) { comparator.setScorer(scorer); } } @@ -165,8 +164,9 @@ public abstract class TermAllGroupHeadsCollector { - final FieldComparator[] comparators; + final FieldComparator[] comparators; + @SuppressWarnings({"unchecked","rawtypes"}) private GroupHead(BytesRef groupValue, Sort sort, int doc) throws IOException { super(groupValue, doc + readerContext.docBase); final SortField[] sortFields = sort.getSort(); @@ -184,7 +184,7 @@ public abstract class TermAllGroupHeadsCollector comparator : comparators) { comparator.copy(0, doc); comparator.setBottom(0); } diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermGroupFacetCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermGroupFacetCollector.java new file mode 100644 index 00000000000..4a9326e38e9 --- /dev/null +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermGroupFacetCollector.java @@ -0,0 +1,391 @@ +package org.apache.lucene.search.grouping.term; + +/* + * 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 org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.DocTermOrds; +import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.search.FieldCache; +import org.apache.lucene.search.grouping.AbstractGroupFacetCollector; +import org.apache.lucene.util.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * An implementation of {@link AbstractGroupFacetCollector} that computes grouped facets based on the indexed terms + * from the {@link FieldCache}. + * + * @lucene.experimental + */ +public abstract class TermGroupFacetCollector extends AbstractGroupFacetCollector { + + final List groupedFacetHits; + final SentinelIntSet segmentGroupedFacetHits; + final List segmentResults; + final BytesRef spare = new BytesRef(); + + FieldCache.DocTermsIndex groupFieldTermsIndex; + int[] segmentFacetCounts; + int segmentTotalCount; + int startFacetOrd; + int endFacetOrd; + + /** + * Factory method for creating the right implementation based on the fact whether the facet field contains + * multiple tokens per documents. + * + * @param groupField The group field + * @param facetField The facet field + * @param facetFieldMultivalued Whether the facet field has multiple tokens per document + * @param facetPrefix The facet prefix a facet entry should start with to be included. + * @param initialSize The initial allocation size of the internal int set and group facet list which should roughly + * match the total number of expected unique groups. Be aware that the heap usage is + * 4 bytes * initialSize. + * @return TermGroupFacetCollector implementation + */ + public static TermGroupFacetCollector createTermGroupFacetCollector(String groupField, + String facetField, + boolean facetFieldMultivalued, + BytesRef facetPrefix, + int initialSize) { + if (facetFieldMultivalued) { + return new MV(groupField, facetField, facetPrefix, initialSize); + } else { + return new SV(groupField, facetField, facetPrefix, initialSize); + } + } + + TermGroupFacetCollector(String groupField, String facetField, BytesRef facetPrefix, int initialSize) { + super(groupField, facetField, facetPrefix); + groupedFacetHits = new ArrayList(initialSize); + segmentGroupedFacetHits = new SentinelIntSet(initialSize, -1); + segmentResults = new ArrayList(); + } + + /** + * {@inheritDoc} + */ + public GroupedFacetResult mergeSegmentResults(int size, int minCount, boolean orderByCount) throws IOException { + if (segmentFacetCounts != null) { + segmentResults.add(createSegmentResult()); + segmentFacetCounts = null; // reset + } + + int totalCount = 0; + int missingCount = 0; + SegmentResultPriorityQueue segments = new SegmentResultPriorityQueue(segmentResults.size()); + for (SegmentResult segmentResult : segmentResults) { + missingCount += segmentResult.missing; + if (segmentResult.mergePos >= segmentResult.maxTermPos) { + continue; + } + totalCount += segmentResult.total; + segmentResult.initializeForMerge(); + segments.add(segmentResult); + } + + GroupedFacetResult facetResult = new GroupedFacetResult(size, minCount, orderByCount, totalCount, missingCount); + while (segments.size() > 0) { + SegmentResult segmentResult = segments.top(); + BytesRef currentFacetValue = BytesRef.deepCopyOf(segmentResult.mergeTerm); + int count = 0; + + do { + count += segmentResult.counts[segmentResult.mergePos++]; + if (segmentResult.mergePos < segmentResult.maxTermPos) { + segmentResult.nextTerm(); + segmentResult = segments.updateTop(); + } else { + segments.pop(); + segmentResult = segments.top(); + if (segmentResult == null) { + break; + } + } + } while (currentFacetValue.equals(segmentResult.mergeTerm)); + facetResult.addFacetCount(currentFacetValue, count); + } + return facetResult; + } + + protected abstract SegmentResult createSegmentResult(); + + // Implementation for single valued facet fields. + static class SV extends TermGroupFacetCollector { + + private FieldCache.DocTermsIndex facetFieldTermsIndex; + + SV(String groupField, String facetField, BytesRef facetPrefix, int initialSize) { + super(groupField, facetField, facetPrefix, initialSize); + } + + public void collect(int doc) throws IOException { + int facetOrd = facetFieldTermsIndex.getOrd(doc); + if (facetOrd < startFacetOrd || facetOrd >= endFacetOrd) { + return; + } + + int groupOrd = groupFieldTermsIndex.getOrd(doc); + int segmentGroupedFacetsIndex = (groupOrd * facetFieldTermsIndex.numOrd()) + facetOrd; + if (segmentGroupedFacetHits.exists(segmentGroupedFacetsIndex)) { + return; + } + + segmentTotalCount++; + segmentFacetCounts[facetOrd]++; + + segmentGroupedFacetHits.put(segmentGroupedFacetsIndex); + groupedFacetHits.add( + new GroupedFacetHit( + groupOrd == 0 ? null : groupFieldTermsIndex.lookup(groupOrd, new BytesRef()), + facetOrd == 0 ? null : facetFieldTermsIndex.lookup(facetOrd, new BytesRef()) + ) + ); + } + + public void setNextReader(AtomicReaderContext context) throws IOException { + if (segmentFacetCounts != null) { + segmentResults.add(createSegmentResult()); + } + + groupFieldTermsIndex = FieldCache.DEFAULT.getTermsIndex(context.reader(), groupField); + facetFieldTermsIndex = FieldCache.DEFAULT.getTermsIndex(context.reader(), facetField); + segmentFacetCounts = new int[facetFieldTermsIndex.numOrd()]; + segmentTotalCount = 0; + + segmentGroupedFacetHits.clear(); + for (GroupedFacetHit groupedFacetHit : groupedFacetHits) { + int facetOrd = facetFieldTermsIndex.binarySearchLookup(groupedFacetHit.facetValue, spare); + if (facetOrd < 0) { + continue; + } + + int groupOrd = groupFieldTermsIndex.binarySearchLookup(groupedFacetHit.groupValue, spare); + if (groupOrd < 0) { + continue; + } + + int segmentGroupedFacetsIndex = (groupOrd * facetFieldTermsIndex.numOrd()) + facetOrd; + segmentGroupedFacetHits.put(segmentGroupedFacetsIndex); + } + + if (facetPrefix != null) { + startFacetOrd = facetFieldTermsIndex.binarySearchLookup(facetPrefix, spare); + if (startFacetOrd < 0) { + // Points to the ord one higher than facetPrefix + startFacetOrd = -startFacetOrd - 1; + } + BytesRef facetEndPrefix = BytesRef.deepCopyOf(facetPrefix); + facetEndPrefix.append(UnicodeUtil.BIG_TERM); + endFacetOrd = facetFieldTermsIndex.binarySearchLookup(facetEndPrefix, spare); + endFacetOrd = -endFacetOrd - 1; // Points to the ord one higher than facetEndPrefix + } else { + startFacetOrd = 0; + endFacetOrd = facetFieldTermsIndex.numOrd(); + } + } + + protected SegmentResult createSegmentResult() { + return new SegmentResult(segmentFacetCounts, segmentTotalCount, facetFieldTermsIndex.getTermsEnum(), startFacetOrd, endFacetOrd); + } + } + + // Implementation for multi valued facet fields. + static class MV extends TermGroupFacetCollector { + + private DocTermOrds facetFieldDocTermOrds; + private TermsEnum facetOrdTermsEnum; + private DocTermOrds.TermOrdsIterator reuse; + + MV(String groupField, String facetField, BytesRef facetPrefix, int initialSize) { + super(groupField, facetField, facetPrefix, initialSize); + } + + public void collect(int doc) throws IOException { + int groupOrd = groupFieldTermsIndex.getOrd(doc); + reuse = facetFieldDocTermOrds.lookup(doc, reuse); + int chunk; + boolean first = true; + int[] buffer = new int[5]; + do { + chunk = reuse.read(buffer); + if (first && chunk == 0) { + chunk = 1; + buffer[0] = facetFieldDocTermOrds.numTerms(); // this facet ord is reserved for docs not containing facet field. + } + first = false; + + for (int pos = 0; pos < chunk; pos++) { + int facetOrd = buffer[pos]; + if (facetOrd < startFacetOrd || facetOrd >= endFacetOrd) { + continue; + } + + int segmentGroupedFacetsIndex = (groupOrd * (facetFieldDocTermOrds.numTerms() + 1)) + facetOrd; + if (segmentGroupedFacetHits.exists(segmentGroupedFacetsIndex)) { + continue; + } + + segmentTotalCount++; + segmentFacetCounts[facetOrd]++; + + segmentGroupedFacetHits.put(segmentGroupedFacetsIndex); + groupedFacetHits.add( + new GroupedFacetHit( + groupOrd == 0 ? null : groupFieldTermsIndex.lookup(groupOrd, new BytesRef()), + facetOrd == facetFieldDocTermOrds.numTerms() ? null : BytesRef.deepCopyOf(facetFieldDocTermOrds.lookupTerm(facetOrdTermsEnum, facetOrd)) + ) + ); + } + } while (chunk >= buffer.length); + } + + public void setNextReader(AtomicReaderContext context) throws IOException { + if (segmentFacetCounts != null) { + segmentResults.add(createSegmentResult()); + } + + reuse = null; + groupFieldTermsIndex = FieldCache.DEFAULT.getTermsIndex(context.reader(), groupField); + facetFieldDocTermOrds = FieldCache.DEFAULT.getDocTermOrds(context.reader(), facetField); + facetOrdTermsEnum = facetFieldDocTermOrds.getOrdTermsEnum(context.reader()); + // [facetFieldDocTermOrds.numTerms() + 1] for all possible facet values and docs not containing facet field + segmentFacetCounts = new int[facetFieldDocTermOrds.numTerms() + 1]; + segmentTotalCount = 0; + + segmentGroupedFacetHits.clear(); + for (GroupedFacetHit groupedFacetHit : groupedFacetHits) { + int groupOrd = groupFieldTermsIndex.binarySearchLookup(groupedFacetHit.groupValue, spare); + if (groupOrd < 0) { + continue; + } + + int facetOrd; + if (groupedFacetHit.facetValue != null) { + if (!facetOrdTermsEnum.seekExact(groupedFacetHit.facetValue, true)) { + continue; + } + facetOrd = (int) facetOrdTermsEnum.ord(); + } else { + facetOrd = facetFieldDocTermOrds.numTerms(); + } + + // (facetFieldDocTermOrds.numTerms() + 1) for all possible facet values and docs not containing facet field + int segmentGroupedFacetsIndex = (groupOrd * (facetFieldDocTermOrds.numTerms() + 1)) + facetOrd; + segmentGroupedFacetHits.put(segmentGroupedFacetsIndex); + } + + if (facetPrefix != null) { + TermsEnum.SeekStatus seekStatus = facetOrdTermsEnum.seekCeil(facetPrefix, true); + if (seekStatus != TermsEnum.SeekStatus.END) { + startFacetOrd = (int) facetOrdTermsEnum.ord(); + } else { + startFacetOrd = 0; + endFacetOrd = 0; + return; + } + + BytesRef facetEndPrefix = BytesRef.deepCopyOf(facetPrefix); + facetEndPrefix.append(UnicodeUtil.BIG_TERM); + seekStatus = facetOrdTermsEnum.seekCeil(facetEndPrefix, true); + if (seekStatus != TermsEnum.SeekStatus.END) { + endFacetOrd = (int) facetOrdTermsEnum.ord(); + } else { + endFacetOrd = facetFieldDocTermOrds.numTerms(); // Don't include null... + } + } else { + startFacetOrd = 0; + endFacetOrd = facetFieldDocTermOrds.numTerms() + 1; + } + } + + protected SegmentResult createSegmentResult() { + return new SegmentResult(segmentFacetCounts, segmentTotalCount, facetFieldDocTermOrds.numTerms(), facetOrdTermsEnum, startFacetOrd, endFacetOrd); + } + } + +} + +class SegmentResult { + + final int[] counts; + final int total; + final int missing; + + // Used for merging the segment results + BytesRef mergeTerm; + int mergePos; + final int maxTermPos; + final TermsEnum tenum; + + SegmentResult(int[] counts, int total, TermsEnum tenum, int startFacetOrd, int endFacetOrd) { + this.counts = counts; + this.missing = counts[0]; + this.total = total - missing; + this.tenum = tenum; + this.mergePos = startFacetOrd == 0 ? 1 : startFacetOrd; + this.maxTermPos = endFacetOrd; + } + + SegmentResult(int[] counts, int total, int missingCountIndex, TermsEnum tenum, int startFacetOrd, int endFacetOrd) { + this.counts = counts; + this.missing = counts[missingCountIndex]; + this.total = total - missing; + this.tenum = tenum; + this.mergePos = startFacetOrd; + if (endFacetOrd == missingCountIndex + 1) { + this.maxTermPos = missingCountIndex; + } else { + this.maxTermPos = endFacetOrd; + } + } + + void initializeForMerge() throws IOException { + tenum.seekExact(mergePos); + mergeTerm = tenum.term(); + } + + void nextTerm() throws IOException { + mergeTerm = tenum.next(); + } + +} + +class GroupedFacetHit { + + final BytesRef groupValue; + final BytesRef facetValue; + + GroupedFacetHit(BytesRef groupValue, BytesRef facetValue) { + this.groupValue = groupValue; + this.facetValue = facetValue; + } +} + +class SegmentResultPriorityQueue extends PriorityQueue { + + SegmentResultPriorityQueue(int maxSize) { + super(maxSize); + } + + protected boolean lessThan(SegmentResult a, SegmentResult b) { + return a.mergeTerm.compareTo(b.mergeTerm) < 0; + } +} diff --git a/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermSecondPassGroupingCollector.java b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermSecondPassGroupingCollector.java index 3530b8ebd9c..4d364030fea 100644 --- a/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermSecondPassGroupingCollector.java +++ b/modules/grouping/src/java/org/apache/lucene/search/grouping/term/TermSecondPassGroupingCollector.java @@ -42,7 +42,7 @@ public class TermSecondPassGroupingCollector extends AbstractSecondPassGroupingC private final BytesRef spareBytesRef = new BytesRef(); private final String groupField; - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) public TermSecondPassGroupingCollector(String groupField, Collection> groups, Sort groupSort, Sort withinGroupSort, int maxDocsPerGroup, boolean getScores, boolean getMaxScores, boolean fillSortFields) throws IOException { diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/AbstractGroupingTestCase.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/AbstractGroupingTestCase.java new file mode 100644 index 00000000000..08883ac5337 --- /dev/null +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/AbstractGroupingTestCase.java @@ -0,0 +1,37 @@ +package org.apache.lucene.search.grouping; + +/* + * 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 org.apache.lucene.util.LuceneTestCase; +import org.apache.lucene.util._TestUtil; + +/** + * Base class for grouping related tests. + */ +// TODO (MvG) : The grouping tests contain a lot of code duplication. Try to move the common code to this class.. +public abstract class AbstractGroupingTestCase extends LuceneTestCase { + protected String generateRandomNonEmptyString() { + String randomValue; + do { + // B/c of DV based impl we can't see the difference between an empty string and a null value. + // For that reason we don't generate empty string groups. + randomValue = _TestUtil.randomRealisticUnicodeString(random); + } while ("".equals(randomValue)); + return randomValue; + } +} diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupHeadsCollectorTest.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupHeadsCollectorTest.java index 4e1f822b9e0..c19236c3ffd 100644 --- a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupHeadsCollectorTest.java +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupHeadsCollectorTest.java @@ -122,34 +122,34 @@ public class AllGroupHeadsCollectorTest extends LuceneTestCase { int maxDoc = reader.maxDoc(); Sort sortWithinGroup = new Sort(new SortField("id", SortField.Type.INT, true)); - AbstractAllGroupHeadsCollector c1 = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); - indexSearcher.search(new TermQuery(new Term("content", "random")), c1); - assertTrue(arrayContains(new int[]{2, 3, 5, 7}, c1.retrieveGroupHeads())); - assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, c1.retrieveGroupHeads(maxDoc), maxDoc)); + AbstractAllGroupHeadsCollector allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); + indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector); + assertTrue(arrayContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads())); + assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc)); - AbstractAllGroupHeadsCollector c2 = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); - indexSearcher.search(new TermQuery(new Term("content", "some")), c2); - assertTrue(arrayContains(new int[]{2, 3, 4}, c2.retrieveGroupHeads())); - assertTrue(openBitSetContains(new int[]{2, 3, 4}, c2.retrieveGroupHeads(maxDoc), maxDoc)); + allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); + indexSearcher.search(new TermQuery(new Term("content", "some")), allGroupHeadsCollector); + assertTrue(arrayContains(new int[]{2, 3, 4}, allGroupHeadsCollector.retrieveGroupHeads())); + assertTrue(openBitSetContains(new int[]{2, 3, 4}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc)); - AbstractAllGroupHeadsCollector c3 = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); - indexSearcher.search(new TermQuery(new Term("content", "blob")), c3); - assertTrue(arrayContains(new int[]{1, 5}, c3.retrieveGroupHeads())); - assertTrue(openBitSetContains(new int[]{1, 5}, c3.retrieveGroupHeads(maxDoc), maxDoc)); + allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup, canUseIDV, valueType); + indexSearcher.search(new TermQuery(new Term("content", "blob")), allGroupHeadsCollector); + assertTrue(arrayContains(new int[]{1, 5}, allGroupHeadsCollector.retrieveGroupHeads())); + assertTrue(openBitSetContains(new int[]{1, 5}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc)); // STRING sort type triggers different implementation Sort sortWithinGroup2 = new Sort(new SortField("id", SortField.Type.STRING, true)); - AbstractAllGroupHeadsCollector c4 = createRandomCollector(groupField, sortWithinGroup2, canUseIDV, valueType); - indexSearcher.search(new TermQuery(new Term("content", "random")), c4); - assertTrue(arrayContains(new int[]{2, 3, 5, 7}, c4.retrieveGroupHeads())); - assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, c4.retrieveGroupHeads(maxDoc), maxDoc)); + allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup2, canUseIDV, valueType); + indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector); + assertTrue(arrayContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads())); + assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc)); Sort sortWithinGroup3 = new Sort(new SortField("id", SortField.Type.STRING, false)); - AbstractAllGroupHeadsCollector c5 = createRandomCollector(groupField, sortWithinGroup3, canUseIDV, valueType); - indexSearcher.search(new TermQuery(new Term("content", "random")), c5); + allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup3, canUseIDV, valueType); + indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector); // 7 b/c higher doc id wins, even if order of field is in not in reverse. - assertTrue(arrayContains(new int[]{0, 3, 4, 6}, c5.retrieveGroupHeads())); - assertTrue(openBitSetContains(new int[]{0, 3, 4, 6}, c5.retrieveGroupHeads(maxDoc), maxDoc)); + assertTrue(arrayContains(new int[]{0, 3, 4, 6}, allGroupHeadsCollector.retrieveGroupHeads())); + assertTrue(openBitSetContains(new int[]{0, 3, 4, 6}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc)); indexSearcher.getIndexReader().close(); dir.close(); @@ -316,7 +316,7 @@ public class AllGroupHeadsCollectorTest extends LuceneTestCase { final String searchTerm = "real" + random.nextInt(3); boolean sortByScoreOnly = random.nextBoolean(); Sort sortWithinGroup = getRandomSort(sortByScoreOnly); - AbstractAllGroupHeadsCollector allGroupHeadsCollector = createRandomCollector("group", sortWithinGroup, canUseIDV, valueType); + AbstractAllGroupHeadsCollector allGroupHeadsCollector = createRandomCollector("group", sortWithinGroup, canUseIDV, valueType); s.search(new TermQuery(new Term("content", searchTerm)), allGroupHeadsCollector); int[] expectedGroupHeads = createExpectedGroupHeads(searchTerm, groupDocs, sortWithinGroup, sortByScoreOnly, fieldIdToDocID); int[] actualGroupHeads = allGroupHeadsCollector.retrieveGroupHeads(); @@ -506,11 +506,11 @@ public class AllGroupHeadsCollectorTest extends LuceneTestCase { }; } - private AbstractAllGroupHeadsCollector createRandomCollector(String groupField, Sort sortWithinGroup, boolean canUseIDV, Type valueType) throws IOException { - AbstractAllGroupHeadsCollector collector; + private AbstractAllGroupHeadsCollector createRandomCollector(String groupField, Sort sortWithinGroup, boolean canUseIDV, Type valueType) throws IOException { + AbstractAllGroupHeadsCollector collector; if (random.nextBoolean()) { ValueSource vs = new BytesRefFieldSource(groupField); - collector = new FunctionAllGroupHeadsCollector(vs, new HashMap(), sortWithinGroup); + collector = new FunctionAllGroupHeadsCollector(vs, new HashMap(), sortWithinGroup); } else if (canUseIDV && random.nextBoolean()) { boolean diskResident = random.nextBoolean(); collector = DVAllGroupHeadsCollector.create(groupField, sortWithinGroup, valueType, diskResident); diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java index 719fd66220e..a723cf62374 100644 --- a/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/AllGroupsCollectorTest.java @@ -104,17 +104,17 @@ public class AllGroupsCollectorTest extends LuceneTestCase { IndexSearcher indexSearcher = new IndexSearcher(w.getReader()); w.close(); - AbstractAllGroupsCollector c1 = createRandomCollector(groupField, canUseIDV); - indexSearcher.search(new TermQuery(new Term("content", "random")), c1); - assertEquals(4, c1.getGroupCount()); + AbstractAllGroupsCollector allGroupsCollector = createRandomCollector(groupField, canUseIDV); + indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupsCollector); + assertEquals(4, allGroupsCollector.getGroupCount()); - AbstractAllGroupsCollector c2 = createRandomCollector(groupField, canUseIDV); - indexSearcher.search(new TermQuery(new Term("content", "some")), c2); - assertEquals(3, c2.getGroupCount()); + allGroupsCollector = createRandomCollector(groupField, canUseIDV); + indexSearcher.search(new TermQuery(new Term("content", "some")), allGroupsCollector); + assertEquals(3, allGroupsCollector.getGroupCount()); - AbstractAllGroupsCollector c3 = createRandomCollector(groupField, canUseIDV); - indexSearcher.search(new TermQuery(new Term("content", "blob")), c3); - assertEquals(2, c3.getGroupCount()); + allGroupsCollector = createRandomCollector(groupField, canUseIDV); + indexSearcher.search(new TermQuery(new Term("content", "blob")), allGroupsCollector); + assertEquals(2, allGroupsCollector.getGroupCount()); indexSearcher.getIndexReader().close(); dir.close(); @@ -127,8 +127,8 @@ public class AllGroupsCollectorTest extends LuceneTestCase { } } - private AbstractAllGroupsCollector createRandomCollector(String groupField, boolean canUseIDV) throws IOException { - AbstractAllGroupsCollector selected; + private AbstractAllGroupsCollector createRandomCollector(String groupField, boolean canUseIDV) throws IOException { + AbstractAllGroupsCollector selected; if (random.nextBoolean() && canUseIDV) { boolean diskResident = random.nextBoolean(); selected = DVAllGroupsCollector.create(groupField, Type.BYTES_VAR_SORTED, diskResident); @@ -136,7 +136,7 @@ public class AllGroupsCollectorTest extends LuceneTestCase { selected = new TermAllGroupsCollector(groupField); } else { ValueSource vs = new BytesRefFieldSource(groupField); - selected = new FunctionAllGroupsCollector(vs, new HashMap()); + selected = new FunctionAllGroupsCollector(vs, new HashMap()); } if (VERBOSE) { diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/TermGroupFacetCollectorTest.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermGroupFacetCollectorTest.java new file mode 100644 index 00000000000..4dfea352a5f --- /dev/null +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TermGroupFacetCollectorTest.java @@ -0,0 +1,600 @@ +package org.apache.lucene.search.grouping; + +/* + * 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 org.apache.lucene.analysis.MockAnalyzer; +import org.apache.lucene.document.*; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.grouping.term.TermGroupFacetCollector; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util._TestUtil; + +import java.io.IOException; +import java.util.*; + +public class TermGroupFacetCollectorTest extends AbstractGroupingTestCase { + + public void testSimple() throws Exception { + final String groupField = "hotel"; + FieldType customType = new FieldType(); + customType.setStored(true); + + Directory dir = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter( + random, + dir, + newIndexWriterConfig(TEST_VERSION_CURRENT, + new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy())); + boolean canUseIDV = false;// Enable later... !"Lucene3x".equals(w.w.getConfig().getCodec().getName()); + + // 0 + Document doc = new Document(); + addGroupField(doc, groupField, "a", canUseIDV); + doc.add(new Field("airport", "ams", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "5", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 1 + doc = new Document(); + addGroupField(doc, groupField, "a", canUseIDV); + doc.add(new Field("airport", "dus", TextField.TYPE_STORED)); + doc.add(new Field("duration", "10", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 2 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("airport", "ams", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "10", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + w.commit(); // To ensure a second segment + + // 3 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("airport", "ams", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "5", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 4 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("airport", "ams", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "5", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + IndexSearcher indexSearcher = new IndexSearcher(w.getReader()); + TermGroupFacetCollector groupedAirportFacetCollector = + TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "airport", false, null, 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector); + TermGroupFacetCollector.GroupedFacetResult airportResult = groupedAirportFacetCollector.mergeSegmentResults(10, 0, false); + assertEquals(3, airportResult.getTotalCount()); + assertEquals(0, airportResult.getTotalMissingCount()); + + List entries = airportResult.getFacetEntries(0, 10); + assertEquals(2, entries.size()); + assertEquals("ams", entries.get(0).getValue().utf8ToString()); + assertEquals(2, entries.get(0).getCount()); + assertEquals("dus", entries.get(1).getValue().utf8ToString()); + assertEquals(1, entries.get(1).getCount()); + + + TermGroupFacetCollector groupedDurationFacetCollector = + TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "duration", false, null, 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector); + TermGroupFacetCollector.GroupedFacetResult durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 0, false); + assertEquals(4, durationResult.getTotalCount()); + assertEquals(0, durationResult.getTotalMissingCount()); + + entries = durationResult.getFacetEntries(0, 10); + assertEquals(2, entries.size()); + assertEquals("10", entries.get(0).getValue().utf8ToString()); + assertEquals(2, entries.get(0).getCount()); + assertEquals("5", entries.get(1).getValue().utf8ToString()); + assertEquals(2, entries.get(1).getCount()); + + // 5 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("duration", "5", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 6 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("airport", "bru", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "10", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 7 + doc = new Document(); + addGroupField(doc, groupField, "b", canUseIDV); + doc.add(new Field("airport", "bru", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "15", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 8 + doc = new Document(); + addGroupField(doc, groupField, "a", canUseIDV); + doc.add(new Field("airport", "bru", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "10", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + indexSearcher.getIndexReader().close(); + indexSearcher = new IndexSearcher(w.getReader()); + groupedAirportFacetCollector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "airport", true, null, 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector); + airportResult = groupedAirportFacetCollector.mergeSegmentResults(3, 0, true); + assertEquals(5, airportResult.getTotalCount()); + assertEquals(1, airportResult.getTotalMissingCount()); + + entries = airportResult.getFacetEntries(1, 2); + assertEquals(2, entries.size()); + assertEquals("bru", entries.get(0).getValue().utf8ToString()); + assertEquals(2, entries.get(0).getCount()); + assertEquals("dus", entries.get(1).getValue().utf8ToString()); + assertEquals(1, entries.get(1).getCount()); + + groupedDurationFacetCollector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "duration", false, null, 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector); + durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 2, true); + assertEquals(5, durationResult.getTotalCount()); + assertEquals(0, durationResult.getTotalMissingCount()); + + entries = durationResult.getFacetEntries(1, 1); + assertEquals(1, entries.size()); + assertEquals("5", entries.get(0).getValue().utf8ToString()); + assertEquals(2, entries.get(0).getCount()); + + // 9 + doc = new Document(); + addGroupField(doc, groupField, "c", canUseIDV); + doc.add(new Field("airport", "bru", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "15", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + // 10 + doc = new Document(); + addGroupField(doc, groupField, "c", canUseIDV); + doc.add(new Field("airport", "dus", TextField.TYPE_UNSTORED)); + doc.add(new Field("duration", "10", TextField.TYPE_UNSTORED)); + w.addDocument(doc); + + indexSearcher.getIndexReader().close(); + indexSearcher = new IndexSearcher(w.getReader()); + groupedAirportFacetCollector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "airport", false, null, 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector); + airportResult = groupedAirportFacetCollector.mergeSegmentResults(10, 0, false); + assertEquals(7, airportResult.getTotalCount()); + assertEquals(1, airportResult.getTotalMissingCount()); + + entries = airportResult.getFacetEntries(0, 10); + assertEquals(3, entries.size()); + assertEquals("ams", entries.get(0).getValue().utf8ToString()); + assertEquals(2, entries.get(0).getCount()); + assertEquals("bru", entries.get(1).getValue().utf8ToString()); + assertEquals(3, entries.get(1).getCount()); + assertEquals("dus", entries.get(2).getValue().utf8ToString()); + assertEquals(2, entries.get(2).getCount()); + + groupedDurationFacetCollector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, "duration", false, new BytesRef("1"), 128); + indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector); + durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 0, true); + assertEquals(5, durationResult.getTotalCount()); + assertEquals(0, durationResult.getTotalMissingCount()); + + entries = durationResult.getFacetEntries(0, 10); + assertEquals(2, entries.size()); + assertEquals("10", entries.get(0).getValue().utf8ToString()); + assertEquals(3, entries.get(0).getCount()); + assertEquals("15", entries.get(1).getValue().utf8ToString()); + assertEquals(2, entries.get(1).getCount()); + + w.close(); + indexSearcher.getIndexReader().close(); + dir.close(); + } + + private void addGroupField(Document doc, String groupField, String value, boolean canUseIDV) { + doc.add(new Field(groupField, value, TextField.TYPE_UNSTORED)); + if (canUseIDV) { + doc.add(new DocValuesField(groupField, new BytesRef(value), DocValues.Type.BYTES_VAR_SORTED)); + } + } + + public void testRandom() throws Exception { + int numberOfRuns = _TestUtil.nextInt(random, 3, 6); + for (int indexIter = 0; indexIter < numberOfRuns; indexIter++) { + boolean multipleFacetsPerDocument = random.nextBoolean(); + IndexContext context = createIndexContext(multipleFacetsPerDocument); + final IndexSearcher searcher = newSearcher(context.indexReader); + + for (int searchIter = 0; searchIter < 100; searchIter++) { + String searchTerm = context.contentStrings[random.nextInt(context.contentStrings.length)]; + int limit = random.nextInt(context.facetValues.size()); + int offset = random.nextInt(context.facetValues.size() - limit); + int size = offset + limit; + int minCount = random.nextBoolean() ? 0 : random.nextInt(1 + context.facetWithMostGroups / 10); + boolean orderByCount = random.nextBoolean(); + String randomStr = getFromSet(context.facetValues, random.nextInt(context.facetValues.size())); + final String facetPrefix; + if (randomStr == null) { + facetPrefix = null; + } else { + int codePointLen = randomStr.codePointCount(0, randomStr.length()); + int randomLen = random.nextInt(codePointLen); + if (codePointLen == randomLen - 1) { + facetPrefix = null; + } else { + int end = randomStr.offsetByCodePoints(0, randomLen); + facetPrefix = random.nextBoolean() ? null : randomStr.substring(end); + } + } + + GroupedFacetResult expectedFacetResult = createExpectedFacetResult(searchTerm, context, offset, limit, minCount, orderByCount, facetPrefix); + TermGroupFacetCollector groupFacetCollector = createRandomCollector("group", "facet", facetPrefix, multipleFacetsPerDocument); + searcher.search(new TermQuery(new Term("content", searchTerm)), groupFacetCollector); + TermGroupFacetCollector.GroupedFacetResult actualFacetResult = groupFacetCollector.mergeSegmentResults(size, minCount, orderByCount); + + List expectedFacetEntries = expectedFacetResult.getFacetEntries(); + List actualFacetEntries = actualFacetResult.getFacetEntries(offset, limit); + + if (VERBOSE) { + System.out.println("Collector: " + groupFacetCollector.getClass().getSimpleName()); + System.out.println("Num group: " + context.numGroups); + System.out.println("Num doc: " + context.numDocs); + System.out.println("Index iter: " + indexIter); + System.out.println("multipleFacetsPerDocument: " + multipleFacetsPerDocument); + System.out.println("Search iter: " + searchIter); + + System.out.println("Search term: " + searchTerm); + System.out.println("Min count: " + minCount); + System.out.println("Facet offset: " + offset); + System.out.println("Facet limit: " + limit); + System.out.println("Facet prefix: " + facetPrefix); + System.out.println("Order by count: " + orderByCount); + + System.out.println("\n=== Expected: \n"); + System.out.println("Total count " + expectedFacetResult.getTotalCount()); + System.out.println("Total missing count " + expectedFacetResult.getTotalMissingCount()); + int counter = 1; + for (TermGroupFacetCollector.FacetEntry expectedFacetEntry : expectedFacetEntries) { + System.out.println( + String.format( + "%d. Expected facet value %s with count %d", + counter++, expectedFacetEntry.getValue().utf8ToString(), expectedFacetEntry.getCount() + ) + ); + } + + System.out.println("\n=== Actual: \n"); + System.out.println("Total count " + actualFacetResult.getTotalCount()); + System.out.println("Total missing count " + actualFacetResult.getTotalMissingCount()); + counter = 1; + for (TermGroupFacetCollector.FacetEntry actualFacetEntry : actualFacetEntries) { + System.out.println( + String.format( + "%d. Actual facet value %s with count %d", + counter++, actualFacetEntry.getValue().utf8ToString(), actualFacetEntry.getCount() + ) + ); + } + System.out.println("\n==================================================================================="); + } + + assertEquals(expectedFacetResult.getTotalCount(), actualFacetResult.getTotalCount()); + assertEquals(expectedFacetResult.getTotalMissingCount(), actualFacetResult.getTotalMissingCount()); + assertEquals(expectedFacetEntries.size(), actualFacetEntries.size()); + for (int i = 0; i < expectedFacetEntries.size(); i++) { + TermGroupFacetCollector.FacetEntry expectedFacetEntry = expectedFacetEntries.get(i); + TermGroupFacetCollector.FacetEntry actualFacetEntry = actualFacetEntries.get(i); + assertEquals(expectedFacetEntry.getValue().utf8ToString() + " != " + actualFacetEntry.getValue().utf8ToString(), expectedFacetEntry.getValue(), actualFacetEntry.getValue()); + assertEquals(expectedFacetEntry.getCount() + " != " + actualFacetEntry.getCount(), expectedFacetEntry.getCount(), actualFacetEntry.getCount()); + } + } + + context.indexReader.close(); + context.dir.close(); + } + } + + private IndexContext createIndexContext(boolean multipleFacetValuesPerDocument) throws IOException { + final int numDocs = _TestUtil.nextInt(random, 138, 1145) * RANDOM_MULTIPLIER; + final int numGroups = _TestUtil.nextInt(random, 1, numDocs / 4); + final int numFacets = _TestUtil.nextInt(random, 1, numDocs / 6); + + if (VERBOSE) { + System.out.println("TEST: numDocs=" + numDocs + " numGroups=" + numGroups); + } + + final List groups = new ArrayList(); + for (int i = 0; i < numGroups; i++) { + groups.add(generateRandomNonEmptyString()); + } + final List facetValues = new ArrayList(); + for (int i = 0; i < numFacets; i++) { + facetValues.add(generateRandomNonEmptyString()); + } + final String[] contentBrs = new String[_TestUtil.nextInt(random, 2, 20)]; + if (VERBOSE) { + System.out.println("TEST: create fake content"); + } + for (int contentIDX = 0; contentIDX < contentBrs.length; contentIDX++) { + contentBrs[contentIDX] = generateRandomNonEmptyString(); + if (VERBOSE) { + System.out.println(" content=" + contentBrs[contentIDX]); + } + } + + Directory dir = newDirectory(); + RandomIndexWriter writer = new RandomIndexWriter( + random, + dir, + newIndexWriterConfig( + TEST_VERSION_CURRENT, + new MockAnalyzer(random) + ) + ); + + Document doc = new Document(); + Document docNoGroup = new Document(); + Document docNoFacet = new Document(); + Document docNoGroupNoFacet = new Document(); + Field group = newField("group", "", StringField.TYPE_UNSTORED); + doc.add(group); + docNoFacet.add(group); + Field[] facetFields = multipleFacetValuesPerDocument? new Field[2 + random.nextInt(6)] : new Field[1]; + for (int i = 0; i < facetFields.length; i++) { + facetFields[i] = newField("facet", "", StringField.TYPE_UNSTORED); + doc.add(facetFields[i]); + docNoGroup.add(facetFields[i]); + } + Field content = newField("content", "", StringField.TYPE_UNSTORED); + doc.add(content); + docNoGroup.add(content); + docNoFacet.add(content); + docNoGroupNoFacet.add(content); + + NavigableSet uniqueFacetValues = new TreeSet(new Comparator() { + + public int compare(String a, String b) { + if (a == b) { + return 0; + } else if (a == null) { + return -1; + } else if (b == null) { + return 1; + } else { + return a.compareTo(b); + } + } + + }); + Map>> searchTermToFacetToGroups = new HashMap>>(); + int facetWithMostGroups = 0; + for (int i = 0; i < numDocs; i++) { + final String groupValue; + if (random.nextInt(24) == 17) { + // So we test the "doc doesn't have the group'd + // field" case: + groupValue = null; + } else { + groupValue = groups.get(random.nextInt(groups.size())); + } + + String contentStr = contentBrs[random.nextInt(contentBrs.length)]; + if (!searchTermToFacetToGroups.containsKey(contentStr)) { + searchTermToFacetToGroups.put(contentStr, new HashMap>()); + } + Map> facetToGroups = searchTermToFacetToGroups.get(contentStr); + + List facetVals = new ArrayList(); + if (random.nextInt(24) != 18) { + for (Field facetField : facetFields) { + String facetValue = facetValues.get(random.nextInt(facetValues.size())); + uniqueFacetValues.add(facetValue); + if (!facetToGroups.containsKey(facetValue)) { + facetToGroups.put(facetValue, new HashSet()); + } + Set groupsInFacet = facetToGroups.get(facetValue); + groupsInFacet.add(groupValue); + if (groupsInFacet.size() > facetWithMostGroups) { + facetWithMostGroups = groupsInFacet.size(); + } + facetField.setStringValue(facetValue); + facetVals.add(facetValue); + } + } else { + uniqueFacetValues.add(null); + if (!facetToGroups.containsKey(null)) { + facetToGroups.put(null, new HashSet()); + } + Set groupsInFacet = facetToGroups.get(null); + groupsInFacet.add(groupValue); + if (groupsInFacet.size() > facetWithMostGroups) { + facetWithMostGroups = groupsInFacet.size(); + } + } + + if (VERBOSE) { + System.out.println(" doc content=" + contentStr + " group=" + (groupValue == null ? "null" : groupValue) + " facetVals=" + facetVals); + } + + if (groupValue != null) { + group.setStringValue(groupValue); + } + content.setStringValue(contentStr); + if (groupValue == null && facetVals.isEmpty()) { + writer.addDocument(docNoGroupNoFacet); + } else if (facetVals.isEmpty()) { + writer.addDocument(docNoFacet); + } else if (groupValue == null) { + writer.addDocument(docNoGroup); + } else { + writer.addDocument(doc); + } + } + + DirectoryReader reader = writer.getReader(); + writer.close(); + + return new IndexContext(searchTermToFacetToGroups, reader, numDocs, dir, facetWithMostGroups, numGroups, contentBrs, uniqueFacetValues); + } + + private GroupedFacetResult createExpectedFacetResult(String searchTerm, IndexContext context, int offset, int limit, int minCount, final boolean orderByCount, String facetPrefix) { + Map> facetGroups = context.searchTermToFacetGroups.get(searchTerm); + if (facetGroups == null) { + facetGroups = new HashMap>(); + } + + int totalCount = 0; + int totalMissCount = 0; + Set facetValues; + if (facetPrefix != null) { + facetValues = new HashSet(); + for (String facetValue : context.facetValues) { + if (facetValue != null && facetValue.startsWith(facetPrefix)) { + facetValues.add(facetValue); + } + } + } else { + facetValues = context.facetValues; + } + + List entries = new ArrayList(facetGroups.size()); + // also includes facets with count 0 + for (String facetValue : facetValues) { + if (facetValue == null) { + continue; + } + + Set groups = facetGroups.get(facetValue); + int count = groups != null ? groups.size() : 0; + if (count >= minCount) { + entries.add(new TermGroupFacetCollector.FacetEntry(new BytesRef(facetValue), count)); + } + totalCount += count; + } + + // Only include null count when no facet prefix is specified + if (facetPrefix == null) { + Set groups = facetGroups.get(null); + if (groups != null) { + totalMissCount = groups.size(); + } + } + + Collections.sort(entries, new Comparator() { + + public int compare(TermGroupFacetCollector.FacetEntry a, TermGroupFacetCollector.FacetEntry b) { + if (orderByCount) { + int cmp = b.getCount() - a.getCount(); + if (cmp != 0) { + return cmp; + } + } + return a.getValue().compareTo(b.getValue()); + } + + }); + + int endOffset = offset + limit; + List entriesResult; + if (offset >= entries.size()) { + entriesResult = Collections.emptyList(); + } else if (endOffset >= entries.size()) { + entriesResult = entries.subList(offset, entries.size()); + } else { + entriesResult = entries.subList(offset, endOffset); + } + return new GroupedFacetResult(totalCount, totalMissCount, entriesResult); + } + + private TermGroupFacetCollector createRandomCollector(String groupField, String facetField, String facetPrefix, boolean multipleFacetsPerDocument) { + BytesRef facetPrefixBR = facetPrefix == null ? null : new BytesRef(facetPrefix); + return TermGroupFacetCollector.createTermGroupFacetCollector(groupField, facetField, multipleFacetsPerDocument, facetPrefixBR, random.nextInt(1024)); + } + + private String getFromSet(Set set, int index) { + int currentIndex = 0; + for (String bytesRef : set) { + if (currentIndex++ == index) { + return bytesRef; + } + } + + return null; + } + + private class IndexContext { + + final int numDocs; + final DirectoryReader indexReader; + final Map>> searchTermToFacetGroups; + final NavigableSet facetValues; + final Directory dir; + final int facetWithMostGroups; + final int numGroups; + final String[] contentStrings; + + public IndexContext(Map>> searchTermToFacetGroups, DirectoryReader r, + int numDocs, Directory dir, int facetWithMostGroups, int numGroups, String[] contentStrings, NavigableSet facetValues) { + this.searchTermToFacetGroups = searchTermToFacetGroups; + this.indexReader = r; + this.numDocs = numDocs; + this.dir = dir; + this.facetWithMostGroups = facetWithMostGroups; + this.numGroups = numGroups; + this.contentStrings = contentStrings; + this.facetValues = facetValues; + } + } + + private class GroupedFacetResult { + + final int totalCount; + final int totalMissingCount; + final List facetEntries; + + private GroupedFacetResult(int totalCount, int totalMissingCount, List facetEntries) { + this.totalCount = totalCount; + this.totalMissingCount = totalMissingCount; + this.facetEntries = facetEntries; + } + + public int getTotalCount() { + return totalCount; + } + + public int getTotalMissingCount() { + return totalMissingCount; + } + + public List getFacetEntries() { + return facetEntries; + } + } + +} diff --git a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java index d5a77ee5f38..a72247a75af 100644 --- a/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java +++ b/modules/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java @@ -126,13 +126,13 @@ public class TestGrouping extends LuceneTestCase { w.close(); final Sort groupSort = Sort.RELEVANCE; - final AbstractFirstPassGroupingCollector c1 = createRandomFirstPassCollector(groupField, groupSort, 10, canUseIDV); + final AbstractFirstPassGroupingCollector c1 = createRandomFirstPassCollector(groupField, groupSort, 10, canUseIDV); indexSearcher.search(new TermQuery(new Term("content", "random")), c1); - final AbstractSecondPassGroupingCollector c2 = createSecondPassCollector(c1, groupField, groupSort, null, 0, 5, true, false, true); + final AbstractSecondPassGroupingCollector c2 = createSecondPassCollector(c1, groupField, groupSort, null, 0, 5, true, false, true); indexSearcher.search(new TermQuery(new Term("content", "random")), c2); - final TopGroups groups = c2.getTopGroups(0); + final TopGroups groups = c2.getTopGroups(0); assertEquals(7, groups.totalHitCount); assertEquals(7, groups.totalGroupedHitCount); @@ -142,7 +142,7 @@ public class TestGrouping extends LuceneTestCase { // the later a document is added the higher this docId // value - GroupDocs group = groups.groups[0]; + GroupDocs group = groups.groups[0]; compareGroupValue("author3", group); assertEquals(2, group.scoreDocs.length); assertEquals(5, group.scoreDocs[0].doc); @@ -179,14 +179,14 @@ public class TestGrouping extends LuceneTestCase { } } - private AbstractFirstPassGroupingCollector createRandomFirstPassCollector(String groupField, Sort groupSort, int topDocs, boolean canUseIDV) throws IOException { - AbstractFirstPassGroupingCollector selected; + private AbstractFirstPassGroupingCollector createRandomFirstPassCollector(String groupField, Sort groupSort, int topDocs, boolean canUseIDV) throws IOException { + AbstractFirstPassGroupingCollector selected; if (canUseIDV && random.nextBoolean()) { boolean diskResident = random.nextBoolean(); selected = DVFirstPassGroupingCollector.create(groupSort, topDocs, groupField, Type.BYTES_VAR_SORTED, diskResident); } else if (random.nextBoolean()) { ValueSource vs = new BytesRefFieldSource(groupField); - selected = new FunctionFirstPassGroupingCollector(vs, new HashMap(), groupSort, topDocs); + selected = new FunctionFirstPassGroupingCollector(vs, new HashMap(), groupSort, topDocs); } else { selected = new TermFirstPassGroupingCollector(groupField, groupSort, topDocs); } @@ -196,20 +196,20 @@ public class TestGrouping extends LuceneTestCase { return selected; } - private AbstractFirstPassGroupingCollector createFirstPassCollector(String groupField, Sort groupSort, int topDocs, AbstractFirstPassGroupingCollector firstPassGroupingCollector) throws IOException { + private AbstractFirstPassGroupingCollector createFirstPassCollector(String groupField, Sort groupSort, int topDocs, AbstractFirstPassGroupingCollector firstPassGroupingCollector) throws IOException { if (DVFirstPassGroupingCollector.class.isAssignableFrom(firstPassGroupingCollector.getClass())) { boolean diskResident = random.nextBoolean(); return DVFirstPassGroupingCollector.create(groupSort, topDocs, groupField, Type.BYTES_VAR_SORTED, diskResident); } else if (TermFirstPassGroupingCollector.class.isAssignableFrom(firstPassGroupingCollector.getClass())) { ValueSource vs = new BytesRefFieldSource(groupField); - return new FunctionFirstPassGroupingCollector(vs, new HashMap(), groupSort, topDocs); + return new FunctionFirstPassGroupingCollector(vs, new HashMap(), groupSort, topDocs); } else { return new TermFirstPassGroupingCollector(groupField, groupSort, topDocs); } } - @SuppressWarnings("unchecked") - private AbstractSecondPassGroupingCollector createSecondPassCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, + @SuppressWarnings({"unchecked","rawtypes"}) + private AbstractSecondPassGroupingCollector createSecondPassCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, String groupField, Sort groupSort, Sort sortWithinGroup, @@ -221,21 +221,21 @@ public class TestGrouping extends LuceneTestCase { if (DVFirstPassGroupingCollector.class.isAssignableFrom(firstPassGroupingCollector.getClass())) { boolean diskResident = random.nextBoolean(); - Collection searchGroups = firstPassGroupingCollector.getTopGroups(groupOffset, fillSortFields); + Collection> searchGroups = firstPassGroupingCollector.getTopGroups(groupOffset, fillSortFields); return DVSecondPassGroupingCollector.create(groupField, diskResident, Type.BYTES_VAR_SORTED, searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getScores, getMaxScores, fillSortFields); } else if (TermFirstPassGroupingCollector.class.isAssignableFrom(firstPassGroupingCollector.getClass())) { Collection> searchGroups = firstPassGroupingCollector.getTopGroups(groupOffset, fillSortFields); - return new TermSecondPassGroupingCollector(groupField, searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup , getScores, getMaxScores, fillSortFields); + return (AbstractSecondPassGroupingCollector) new TermSecondPassGroupingCollector(groupField, searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup , getScores, getMaxScores, fillSortFields); } else { ValueSource vs = new BytesRefFieldSource(groupField); Collection> searchGroups = firstPassGroupingCollector.getTopGroups(groupOffset, fillSortFields); - return new FunctionSecondPassGroupingCollector(searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getScores, getMaxScores, fillSortFields, vs, new HashMap()); + return (AbstractSecondPassGroupingCollector) new FunctionSecondPassGroupingCollector(searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getScores, getMaxScores, fillSortFields, vs, new HashMap()); } } // Basically converts searchGroups from MutableValue to BytesRef if grouping by ValueSource @SuppressWarnings("unchecked") - private AbstractSecondPassGroupingCollector createSecondPassCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, + private AbstractSecondPassGroupingCollector createSecondPassCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, String groupField, Collection> searchGroups, Sort groupSort, @@ -266,11 +266,11 @@ public class TestGrouping extends LuceneTestCase { mvalSearchGroups.add(sg); } - return new FunctionSecondPassGroupingCollector(mvalSearchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getScores, getMaxScores, fillSortFields, vs, new HashMap()); + return new FunctionSecondPassGroupingCollector(mvalSearchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getScores, getMaxScores, fillSortFields, vs, new HashMap()); } } - private AbstractAllGroupsCollector createAllGroupsCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, + private AbstractAllGroupsCollector createAllGroupsCollector(AbstractFirstPassGroupingCollector firstPassGroupingCollector, String groupField) { if (firstPassGroupingCollector.getClass().isAssignableFrom(TermFirstPassGroupingCollector.class)) { return new TermAllGroupsCollector(groupField); @@ -279,11 +279,11 @@ public class TestGrouping extends LuceneTestCase { return DVAllGroupsCollector.create(groupField, Type.BYTES_VAR_SORTED, diskResident); } else { ValueSource vs = new BytesRefFieldSource(groupField); - return new FunctionAllGroupsCollector(vs, new HashMap()); + return new FunctionAllGroupsCollector(vs, new HashMap()); } } - private void compareGroupValue(String expected, GroupDocs group) { + private void compareGroupValue(String expected, GroupDocs group) { if (expected == null) { if (group.groupValue == null) { return; @@ -306,7 +306,7 @@ public class TestGrouping extends LuceneTestCase { } } - private Collection> getSearchGroups(AbstractFirstPassGroupingCollector c, int groupOffset, boolean fillFields) { + private Collection> getSearchGroups(AbstractFirstPassGroupingCollector c, int groupOffset, boolean fillFields) { if (TermFirstPassGroupingCollector.class.isAssignableFrom(c.getClass())) { return ((TermFirstPassGroupingCollector) c).getTopGroups(groupOffset, fillFields); } else if (FunctionFirstPassGroupingCollector.class.isAssignableFrom(c.getClass())) { @@ -332,7 +332,7 @@ public class TestGrouping extends LuceneTestCase { return null; } - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "rawtypes"}) private TopGroups getTopGroups(AbstractSecondPassGroupingCollector c, int withinGroupOffset) { if (c.getClass().isAssignableFrom(TermSecondPassGroupingCollector.class)) { return ((TermSecondPassGroupingCollector) c).getTopGroups(withinGroupOffset); @@ -425,6 +425,7 @@ public class TestGrouping extends LuceneTestCase { }; } + @SuppressWarnings({"unchecked","rawtypes"}) private Comparable[] fillFields(GroupDoc d, Sort sort) { final SortField[] sortFields = sort.getSort(); final Comparable[] fields = new Comparable[sortFields.length]; @@ -514,7 +515,7 @@ public class TestGrouping extends LuceneTestCase { final int limit = Math.min(groupOffset + topNGroups, groups.size()); final Comparator docSortComp = getComparator(docSort); - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked","rawtypes"}) final GroupDocs[] result = new GroupDocs[limit-groupOffset]; int totalGroupedHitCount = 0; for(int idx=groupOffset;idx < limit;idx++) { @@ -874,11 +875,11 @@ public class TestGrouping extends LuceneTestCase { System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " dF=" + r.docFreq("content", new BytesRef(searchTerm)) +" dFBlock=" + rBlocks.docFreq("content", new BytesRef(searchTerm)) + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups + " getScores=" + getScores + " getMaxScores=" + getMaxScores); } - final AbstractFirstPassGroupingCollector c1 = createRandomFirstPassCollector("group", groupSort, groupOffset+topNGroups, canUseIDV); + final AbstractFirstPassGroupingCollector c1 = createRandomFirstPassCollector("group", groupSort, groupOffset+topNGroups, canUseIDV); final CachingCollector cCache; final Collector c; - final AbstractAllGroupsCollector allGroupsCollector; + final AbstractAllGroupsCollector allGroupsCollector; if (doAllGroups) { allGroupsCollector = createAllGroupsCollector(c1, "group"); } else { @@ -953,7 +954,7 @@ public class TestGrouping extends LuceneTestCase { ValueHolder idvBasedImplsUsedSharded = new ValueHolder(false); final TopGroups topGroupsShards = searchShards(s, shards.subSearchers, query, groupSort, docSort, groupOffset, topNGroups, docOffset, docsPerGroup, getScores, getMaxScores, canUseIDV, preFlex, idvBasedImplsUsedSharded); - final AbstractSecondPassGroupingCollector c2; + final AbstractSecondPassGroupingCollector c2; if (topGroups != null) { if (VERBOSE) { @@ -1062,8 +1063,8 @@ public class TestGrouping extends LuceneTestCase { } // Get block grouping result: sBlocks.search(query, c4); - @SuppressWarnings("unchecked") - final TopGroups tempTopGroupsBlocks = c3.getTopGroups(docSort, groupOffset, docOffset, docOffset+docsPerGroup, fillFields); + @SuppressWarnings({"unchecked","rawtypes"}) + final TopGroups tempTopGroupsBlocks = (TopGroups) c3.getTopGroups(docSort, groupOffset, docOffset, docOffset+docsPerGroup, fillFields); final TopGroups groupsResultBlocks; if (doAllGroups && tempTopGroupsBlocks != null) { assertEquals((int) tempTopGroupsBlocks.totalGroupCount, allGroupsCollector2.getGroupCount()); @@ -1097,7 +1098,7 @@ public class TestGrouping extends LuceneTestCase { if (expectedGroups != null) { // Fixup scores for reader2 - for (GroupDocs groupDocsHits : expectedGroups.groups) { + for (GroupDocs groupDocsHits : expectedGroups.groups) { for(ScoreDoc hit : groupDocsHits.scoreDocs) { final GroupDoc gd = groupDocsByID[hit.doc]; assertEquals(gd.id, hit.doc); @@ -1110,7 +1111,7 @@ public class TestGrouping extends LuceneTestCase { final Map termScoreMap = scoreMap.get(searchTerm); for(int groupSortIDX=0;groupSortIDX groupDocsHits : expectedGroups.groups) { if (groupDocsHits.groupSortValues != null) { //System.out.println("remap " + groupDocsHits.groupSortValues[groupSortIDX] + " to " + termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX])); groupDocsHits.groupSortValues[groupSortIDX] = termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX]); @@ -1123,7 +1124,7 @@ public class TestGrouping extends LuceneTestCase { final SortField[] docSortFields = docSort.getSort(); for(int docSortIDX=0;docSortIDX groupDocsHits : expectedGroups.groups) { for(ScoreDoc _hit : groupDocsHits.scoreDocs) { FieldDoc hit = (FieldDoc) _hit; if (hit.fields != null) { @@ -1155,7 +1156,7 @@ public class TestGrouping extends LuceneTestCase { } private void verifyShards(int[] docStarts, TopGroups topGroups) { - for(GroupDocs group : topGroups.groups) { + for(GroupDocs group : topGroups.groups) { for(int hitIDX=0;hitIDX>> shardGroups = new ArrayList>>(); - List firstPassGroupingCollectors = new ArrayList(); - AbstractFirstPassGroupingCollector firstPassCollector = null; + List> firstPassGroupingCollectors = new ArrayList>(); + AbstractFirstPassGroupingCollector firstPassCollector = null; for(int shardIDX=0;shardIDX[] shardTopGroups = new TopGroups[subSearchers.length]; for(int shardIDX=0;shardIDX secondPassCollector = createSecondPassCollector(firstPassGroupingCollectors.get(shardIDX), "group", mergedTopGroups, groupSort, docSort, docOffset + topNDocs, getScores, getMaxScores, true); subSearchers[shardIDX].search(w, secondPassCollector); shardTopGroups[shardIDX] = getTopGroups(secondPassCollector, 0); diff --git a/modules/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java b/modules/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java index 0bc310d0f12..63d4af322b7 100644 --- a/modules/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java +++ b/modules/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java @@ -17,33 +17,17 @@ package org.apache.lucene.search.join; * limitations under the License. */ -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; - import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.IndexWriter; // javadocs -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.FieldComparator; -import org.apache.lucene.search.FieldValueHitQueue; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.ScoreCachingWrappingScorer; -import org.apache.lucene.search.Scorer; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.*; import org.apache.lucene.search.Scorer.ChildScorer; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopFieldCollector; -import org.apache.lucene.search.TopScoreDocCollector; -import org.apache.lucene.search.Weight; import org.apache.lucene.search.grouping.GroupDocs; import org.apache.lucene.search.grouping.TopGroups; import org.apache.lucene.util.ArrayUtil; +import java.io.IOException; +import java.util.*; + /** Collects parent document hits for a Query containing one more more * BlockJoinQuery clauses, sorted by the @@ -399,6 +383,7 @@ public class ToParentBlockJoinCollector extends Collector { final FakeScorer fakeScorer = new FakeScorer(); + @SuppressWarnings({"unchecked","rawtypes"}) final GroupDocs[] groups = new GroupDocs[sortedGroups.length - offset]; for(int groupIDX=offset;groupIDX, weights are encoded as costs: (Integer.MAX_VALUE-weight) */ @@ -127,21 +118,13 @@ public class WFSTCompletionLookup extends Lookup { fst = builder.finish(); } - @Override - public boolean store(File storeDir) throws IOException { - fst.save(new File(storeDir, FILENAME)); - return true; - } - - @Override - public boolean load(File storeDir) throws IOException { - this.fst = FST.read(new File(storeDir, FILENAME), PositiveIntOutputs.getSingleton(true)); - return true; - } @Override public boolean store(OutputStream output) throws IOException { try { + if (fst == null) { + return false; + } fst.save(new OutputStreamDataOutput(output)); } finally { IOUtils.close(output); diff --git a/modules/suggest/src/java/org/apache/lucene/search/suggest/jaspell/JaspellLookup.java b/modules/suggest/src/java/org/apache/lucene/search/suggest/jaspell/JaspellLookup.java index b7bb15e8a46..9e570671e17 100644 --- a/modules/suggest/src/java/org/apache/lucene/search/suggest/jaspell/JaspellLookup.java +++ b/modules/suggest/src/java/org/apache/lucene/search/suggest/jaspell/JaspellLookup.java @@ -19,9 +19,6 @@ package org.apache.lucene.search.suggest.jaspell; import java.io.DataInputStream; import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -109,22 +106,11 @@ public class JaspellLookup extends Lookup { return res; } - public static final String FILENAME = "jaspell.dat"; private static final byte LO_KID = 0x01; private static final byte EQ_KID = 0x02; private static final byte HI_KID = 0x04; private static final byte HAS_VALUE = 0x08; - - @Override - public boolean load(File storeDir) throws IOException { - File data = new File(storeDir, FILENAME); - if (!data.exists() || !data.canRead()) { - return false; - } - return load(new FileInputStream(data)); - } - private void readRecursively(DataInputStream in, TSTNode node) throws IOException { node.splitchar = in.readChar(); byte mask = in.readByte(); @@ -148,15 +134,6 @@ public class JaspellLookup extends Lookup { } } - @Override - public boolean store(File storeDir) throws IOException { - if (!storeDir.exists() || !storeDir.isDirectory() || !storeDir.canWrite()) { - return false; - } - File data = new File(storeDir, FILENAME); - return store(new FileOutputStream(data)); - } - private void writeRecursively(DataOutputStream out, TSTNode node) throws IOException { if (node == null) { return; diff --git a/modules/suggest/src/java/org/apache/lucene/search/suggest/tst/TSTLookup.java b/modules/suggest/src/java/org/apache/lucene/search/suggest/tst/TSTLookup.java index 99e4e6a8c46..86a10cde713 100644 --- a/modules/suggest/src/java/org/apache/lucene/search/suggest/tst/TSTLookup.java +++ b/modules/suggest/src/java/org/apache/lucene/search/suggest/tst/TSTLookup.java @@ -119,23 +119,12 @@ public class TSTLookup extends Lookup { return res; } - public static final String FILENAME = "tst.dat"; - private static final byte LO_KID = 0x01; private static final byte EQ_KID = 0x02; private static final byte HI_KID = 0x04; private static final byte HAS_TOKEN = 0x08; private static final byte HAS_VALUE = 0x10; - @Override - public synchronized boolean load(File storeDir) throws IOException { - File data = new File(storeDir, FILENAME); - if (!data.exists() || !data.canRead()) { - return false; - } - return load(new FileInputStream(data)); - } - // pre-order traversal private void readRecursively(DataInputStream in, TernaryTreeNode node) throws IOException { node.splitchar = in.readChar(); @@ -160,15 +149,6 @@ public class TSTLookup extends Lookup { } } - @Override - public synchronized boolean store(File storeDir) throws IOException { - if (!storeDir.exists() || !storeDir.isDirectory() || !storeDir.canWrite()) { - return false; - } - File data = new File(storeDir, FILENAME); - return store(new FileOutputStream(data)); - } - // pre-order traversal private void writeRecursively(DataOutputStream out, TernaryTreeNode node) throws IOException { // write out the current node diff --git a/modules/suggest/src/test/org/apache/lucene/search/suggest/PersistenceTest.java b/modules/suggest/src/test/org/apache/lucene/search/suggest/PersistenceTest.java index 73f5ae82dad..34bf6b1ed7a 100644 --- a/modules/suggest/src/test/org/apache/lucene/search/suggest/PersistenceTest.java +++ b/modules/suggest/src/test/org/apache/lucene/search/suggest/PersistenceTest.java @@ -17,6 +17,8 @@ package org.apache.lucene.search.suggest; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.util.List; import org.apache.lucene.search.suggest.Lookup; @@ -69,11 +71,11 @@ public class PersistenceTest extends LuceneTestCase { // Store the suggester. File storeDir = TEMP_DIR; - lookup.store(storeDir); + lookup.store(new FileOutputStream(new File(storeDir, "lookup.dat"))); // Re-read it from disk. lookup = lookupClass.newInstance(); - lookup.load(storeDir); + lookup.load(new FileInputStream(new File(storeDir, "lookup.dat"))); // Assert validity. long previous = Long.MIN_VALUE; diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 7317820622d..95405b4547f 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -232,6 +232,7 @@ New Features * SOLR-3134: Include shard info in distributed response when shards.info=true (Russell Black, ryan) +* SOLR-2898: Support grouped faceting. (Martijn van Groningen) Optimizations ---------------------- @@ -440,6 +441,8 @@ Other Changes * SOLR-3202: Dropping Support for JSP. New Admin UI is all client side (ryan) +* SOLR-3159: Upgrade example and tests to run with Jetty 8 (ryan) + Documentation ---------------------- @@ -530,6 +533,14 @@ New Features * SOLR-3033: ReplicationHandler's backup command now supports a 'maxNumberOfBackups' init param that can be used to delete all but the most recent N backups. (Torsten Krah, James Dyer) +* SOLR-2202: Currency FieldType, whith support for currencies and exchange rates + (Greg Fodor & Andrew Morrison via janhoy, rmuir, Uwe Schindler) + +* SOLR-3026: eDismax: Locking down which fields can be explicitly queried (user fields aka uf) + (janhoy, hossmann, TomĂ¡s FernĂ¡ndez Löbbe) + +* SOLR-2826: URLClassify Update Processor (janhoy) + Optimizations ---------------------- * SOLR-1931: Speedup for LukeRequestHandler and admin/schema browser. New parameter @@ -640,6 +651,12 @@ Bug Fixes * SOLR-3195: timeAllowed is ignored for grouping queries (Russell Black via Martijn van Groningen) +* SOLR-3204: The packaged pre-release artifact of Commons CSV used the original + package name (org.apache.commons.csv). This created a compatibility issue as + the Apache Commons team works toward an official release of Commons CSV. + JARJAR (http://code.google.com/p/jarjar/) was used to change the package name + to org.apache.solr.internal.csv. (Uwe Schindler, Emmanuel Bourg) + Other Changes ---------------------- * SOLR-2922: Upgrade commons-io and commons-lang to 2.1 and 2.6, respectively. (koji) @@ -662,6 +679,13 @@ Other Changes * SOLR-3097, SOLR-3105: Add analysis configurations for different languages to the example. (Christian Moen, Robert Muir) +* SOLR-3140: Upgrade schema version to 1.5, where omitNorms defaults to "true" for all + primitive (non-analyzed) field types such as int, float, date, bool, string.. (janhoy) + +* SOLR-3077: Better error messages when attempting to use "blank" field names + (Antony Stubbs via hossman) + + Build ---------------------- * SOLR-2487: Add build target to package war without slf4j jars (janhoy) diff --git a/solr/README.txt b/solr/README.txt index 7a4bea33ec8..4ae6405e2f9 100644 --- a/solr/README.txt +++ b/solr/README.txt @@ -30,10 +30,10 @@ Getting Started See the "example" directory for an example Solr setup. A tutorial using the example setup can be found at -http://lucene.apache.org/solr/tutorial.html -or in in "docs/tutorial.html" in a binary distribution. + http://lucene.apache.org/solr/tutorial.html +or linked from "docs/api/index.html" in a binary distribution. Also, there are Solr clients for many programming languages, see -http://wiki.apache.org/solr/IntegratingSolr + http://wiki.apache.org/solr/IntegratingSolr Files included in an Apache Solr binary distribution @@ -54,12 +54,8 @@ dist/apache-solr-XX.jar Apache Solr Plugins (see http://wiki.apache.org/solr/SolrPlugins for more information). -docs/index.html - The contents of the Apache Solr website. - docs/api/index.html - The Apache Solr Javadoc API documentation. - + The Apache Solr Javadoc API documentation and Tutorial Instructions for Building Apache Solr from Source diff --git a/solr/build.xml b/solr/build.xml index a7c8f113d9f..0923029c02e 100644 --- a/solr/build.xml +++ b/solr/build.xml @@ -180,8 +180,10 @@ - - + + + + @@ -359,7 +361,7 @@ excludes="build ${package.dir}/** ${dist}/** example/webapps/*.war example/exampledocs/post.jar lib/README.committers.txt **/data/ **/logs/* - **/*.sh **/bin/ scripts/ site-src/build/ + **/*.sh **/bin/ scripts/ .idea/ **/*.iml **/pom.xml" /> @@ -451,9 +453,7 @@ - - - + - - - - - - - - - diff --git a/solr/contrib/langid/src/test-files/langid/solr/conf/schema.xml b/solr/contrib/langid/src/test-files/langid/solr/conf/schema.xml index ddb7c2481d3..4c3d2e80e48 100644 --- a/solr/contrib/langid/src/test-files/langid/solr/conf/schema.xml +++ b/solr/contrib/langid/src/test-files/langid/solr/conf/schema.xml @@ -28,7 +28,7 @@ $Name: $ --> - + - + - + diff --git a/solr/core/build.xml b/solr/core/build.xml index 895c8abedff..bcee88f3b71 100644 --- a/solr/core/build.xml +++ b/solr/core/build.xml @@ -23,8 +23,8 @@ - + diff --git a/solr/site/tutorial.html b/solr/core/src/java/doc-files/tutorial.html similarity index 72% rename from solr/site/tutorial.html rename to solr/core/src/java/doc-files/tutorial.html index 94d391b46d3..a6aab5f6ee1 100755 --- a/solr/site/tutorial.html +++ b/solr/core/src/java/doc-files/tutorial.html @@ -1,281 +1,20 @@ - - - - -Solr tutorial - - - - - - + +Solr Tutorial + + - - -
    - - - -
    - - - - - - - - - - - - -
    -
    -
    -
    - -
    - - -
    - -
    + -   -
    - - - - -
    - -

    Solr tutorial

    - - +

    Solr Tutorial

    Overview

    @@ -299,7 +38,7 @@ To follow along with this tutorial, you will need... Oracle, Open JDK, IBM, or -
    +
    Running java -version at the command line should indicate a version number starting with 1.6. Gnu's GCJ is not supported and does not work with Solr. @@ -328,7 +67,6 @@ user:~solr$ ls solr-nightly.zip user:~solr$ unzip -q solr-nightly.zip user:~solr$ cd solr-nightly/example/ -

    Solr can run in any Java Servlet Container of your choice, but to simplify @@ -346,7 +84,6 @@ user:~/solr/example$ java -jar start.jar Oct 23, 2009 4:41:56 PM org.apache.solr.core.SolrCore registerSearcher INFO: [] Registered new searcher Searcher@7c3885 main -

    This will start up the Jetty application server on port 8983, and use your terminal to display the logging information from Solr. @@ -401,7 +138,7 @@ Clicking the "Search" button should take you to the following URL... You can index all of the sample data, using the following command (assuming your command line shell supports the *.xml notation):

    -  user:~/solr/example/exampledocs$ java -jar post.jar *.xml
    +user:~/solr/example/exampledocs$ java -jar post.jar *.xml
     SimplePostTool: version 1.2
     SimplePostTool: WARNING: Make sure your XML documents are encoded in UTF-8, other encodings are not currently supported
     SimplePostTool: POSTing files to http://localhost:8983/solr/update..
    @@ -815,7 +552,7 @@ in subsequent searches.
     

      -
    • Subscribe to the Solr mailing lists!
    • +
    • Subscribe to the Solr mailing lists!
    • Make a copy of the Solr example directory as a template for your project.
    • @@ -831,49 +568,22 @@ in subsequent searches. and search results clustering. Explore the Solr Wiki to find - more details about Solr's many features. + more details about Solr's many features.

      Have Fun, and we'll see you on the Solr mailing lists!

    +
    - -
     
    - + - diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java index c66c58f00fc..0ba5b363838 100644 --- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java +++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java @@ -18,23 +18,24 @@ package org.apache.solr.client.solrj.embedded; import java.io.IOException; +import java.util.EnumSet; import java.util.Random; +import javax.servlet.DispatcherType; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.solr.servlet.SolrDispatchFilter; -import org.mortbay.component.LifeCycle; -import org.mortbay.jetty.Connector; -import org.mortbay.jetty.Handler; -import org.mortbay.jetty.Server; -import org.mortbay.jetty.bio.SocketConnector; -import org.mortbay.jetty.servlet.Context; -import org.mortbay.jetty.servlet.FilterHolder; -import org.mortbay.jetty.servlet.HashSessionIdManager; -import org.mortbay.log.Logger; -import org.mortbay.thread.QueuedThreadPool; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.bio.SocketConnector; +import org.eclipse.jetty.server.session.HashSessionIdManager; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.thread.QueuedThreadPool; /** * Run solr using jetty @@ -122,7 +123,7 @@ public class JettySolrRunner { } // Initialize the servlets - final Context root = new Context(server, context, Context.SESSIONS); + final ServletContextHandler root = new ServletContextHandler(server,context,ServletContextHandler.SESSIONS); server.addLifeCycleListener(new LifeCycle.Listener() { public void lifeCycleStopping(LifeCycle arg0) { @@ -147,12 +148,10 @@ public class JettySolrRunner { schemaFilename); // SolrDispatchFilter filter = new SolrDispatchFilter(); // FilterHolder fh = new FilterHolder(filter); - dispatchFilter = root.addFilter(SolrDispatchFilter.class, "*", - Handler.REQUEST); + dispatchFilter = root.addFilter(SolrDispatchFilter.class, "*", EnumSet.of(DispatcherType.REQUEST) ); if (solrConfigFilename != null) System.clearProperty("solrconfig"); if (schemaFilename != null) System.clearProperty("schema"); System.clearProperty("solr.solr.home"); - } public void lifeCycleFailure(LifeCycle arg0, Throwable arg1) { @@ -305,21 +304,9 @@ class NoLog implements Logger { debug = enabled; } - public void info(String msg, Object arg0, Object arg1) { - } - public void debug(String msg, Throwable th) { } - public void debug(String msg, Object arg0, Object arg1) { - } - - public void warn(String msg, Object arg0, Object arg1) { - } - - public void warn(String msg, Throwable th) { - } - public Logger getLogger(String name) { if ((name == null && this.name == null) || (name != null && name.equals(this.name))) @@ -331,4 +318,53 @@ class NoLog implements Logger { public String toString() { return "NOLOG[" + name + "]"; } + + @Override + public void debug(Throwable arg0) { + + } + + @Override + public void debug(String arg0, Object... arg1) { + + } + + @Override + public String getName() { + return toString(); + } + + @Override + public void ignore(Throwable arg0) { + + } + + @Override + public void info(Throwable arg0) { + + } + + @Override + public void info(String arg0, Object... arg1) { + + } + + @Override + public void info(String arg0, Throwable arg1) { + + } + + @Override + public void warn(Throwable arg0) { + + } + + @Override + public void warn(String arg0, Object... arg1) { + + } + + @Override + public void warn(String arg0, Throwable arg1) { + } } diff --git a/solr/core/src/java/org/apache/solr/handler/CSVRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/CSVRequestHandler.java index 1318ed46dc9..b294e98997e 100755 --- a/solr/core/src/java/org/apache/solr/handler/CSVRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/CSVRequestHandler.java @@ -29,8 +29,8 @@ import org.apache.solr.schema.IndexSchema; import org.apache.solr.schema.SchemaField; import org.apache.solr.update.*; import org.apache.solr.update.processor.UpdateRequestProcessor; -import org.apache.commons.csv.CSVStrategy; -import org.apache.commons.csv.CSVParser; +import org.apache.solr.internal.csv.CSVStrategy; +import org.apache.solr.internal.csv.CSVParser; import org.apache.commons.io.IOUtils; import java.util.regex.Pattern; diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java index 859a44a3251..9c80ea6cf4e 100644 --- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java @@ -642,6 +642,7 @@ public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAw addVal(slave, SnapPuller.REPLICATION_FAILED_AT, props, Date.class); addVal(slave, SnapPuller.PREVIOUS_CYCLE_TIME_TAKEN, props, Long.class); + slave.add("currentDate", new Date().toString()); slave.add("isPollingDisabled", String.valueOf(isPollingDisabled())); boolean isReplicating = isReplicating(); slave.add("isReplicating", String.valueOf(isReplicating)); diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java index 51d100f308d..a0d102d983a 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java @@ -25,6 +25,9 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.response.SolrQueryResponse; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.Map; import java.util.HashSet; @@ -85,7 +88,16 @@ public class SolrInfoMBeanHandler extends RequestHandlerBase { mBeanInfo.add("description", m.getDescription()); mBeanInfo.add("srcId", m.getSourceId()); mBeanInfo.add("src", m.getSource()); - mBeanInfo.add("docs", m.getDocs()); + + // Use an external form + URL[] urls = m.getDocs(); + if(urls!=null) { + List docs = new ArrayList(urls.length); + for(URL url : urls) { + docs.add(url.toExternalForm()); + } + mBeanInfo.add("docs", docs); + } if (req.getParams().getFieldBool(key, "stats", false)) mBeanInfo.add("stats", m.getStatistics()); diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java index 25a1bb1aeae..965917fb127 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java +++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandlerFactory.java @@ -16,54 +16,56 @@ package org.apache.solr.handler.component; * limitations under the License. */ -import java.net.MalformedURLException; -import java.util.Random; -import java.util.concurrent.Executor; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.solr.client.solrj.impl.LBHttpSolrServer; import org.apache.solr.common.SolrException; +import org.apache.solr.common.util.NamedList; import org.apache.solr.core.PluginInfo; -import org.apache.solr.core.SolrCore; import org.apache.solr.util.DefaultSolrThreadFactory; import org.apache.solr.util.plugin.PluginInfoInitialized; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.MalformedURLException; +import java.util.Random; +import java.util.concurrent.*; -public class HttpShardHandlerFactory extends ShardHandlerFactory implements PluginInfoInitialized{ +public class HttpShardHandlerFactory extends ShardHandlerFactory implements PluginInfoInitialized { protected static Logger log = LoggerFactory.getLogger(HttpShardHandlerFactory.class); - // We want an executor that doesn't take up any resources if + // We want an executor that doesn't take up any resources if // it's not used, so it could be created statically for // the distributed search component if desired. // // Consider CallerRuns policy and a lower max threads to throttle // requests at some point (or should we simply return failure?) - ThreadPoolExecutor commExecutor = new ThreadPoolExecutor( - 0, - Integer.MAX_VALUE, - 5, TimeUnit.SECONDS, // terminate idle threads after 5 sec - new SynchronousQueue(), // directly hand off tasks - new DefaultSolrThreadFactory("httpShardExecutor") - ); - + ThreadPoolExecutor commExecutor = new ThreadPoolExecutor( + 0, + Integer.MAX_VALUE, + 5, TimeUnit.SECONDS, // terminate idle threads after 5 sec + new SynchronousQueue(), // directly hand off tasks + new DefaultSolrThreadFactory("httpShardExecutor") + ); HttpClient client; Random r = new Random(); LBHttpSolrServer loadbalancer; int soTimeout = 0; //current default values int connectionTimeout = 0; //current default values + int maxConnectionsPerHost = 20; + int corePoolSize = 0; + int maximumPoolSize = 10; + int keepAliveTime = 5; + int queueSize = 1; + boolean accessPolicy = true; + public String scheme = "http://"; //current default values private MultiThreadedHttpConnectionManager mgr; - // socket timeout measured in ms, closes a socket if read + // socket timeout measured in ms, closes a socket if read // takes longer than x ms to complete. throws // java.net.SocketTimeoutException: Read timed out exception static final String INIT_SO_TIMEOUT = "socketTimeout"; @@ -76,42 +78,63 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements Plug // URL scheme to be used in distributed search. static final String INIT_URL_SCHEME = "urlScheme"; + // Maximum connections allowed per host + static final String INIT_MAX_CONNECTION_PER_HOST = "maxConnectionsPerHost"; + // The core size of the threadpool servicing requests + static final String INIT_CORE_POOL_SIZE = "corePoolSize"; - public ShardHandler getShardHandler(){ + // The maximum size of the threadpool servicing requests + static final String INIT_MAX_POOL_SIZE = "maximumPoolSize"; + + // The amount of time idle threads persist for in the queue, before being killed + static final String MAX_THREAD_IDLE_TIME = "maxThreadIdleTime"; + + // If the threadpool uses a backing queue, what is its maximum size (-1) to use direct handoff + static final String INIT_SIZE_OF_QUEUE = "sizeOfQueue"; + + // Configure if the threadpool favours fairness over throughput + static final String INIT_FAIRNESS_POLICY = "fairnessPolicy"; + + public ShardHandler getShardHandler() { return getShardHandler(null); } - - public ShardHandler getShardHandler(HttpClient httpClient){ + public ShardHandler getShardHandler(HttpClient httpClient) { return new HttpShardHandler(this, httpClient); } public void init(PluginInfo info) { + NamedList args = info.initArgs; + this.soTimeout = getParameter(args, INIT_SO_TIMEOUT, 0); - if (info.initArgs != null) { - Object so = info.initArgs.get(INIT_SO_TIMEOUT); - if (so != null) { - soTimeout = (Integer) so; - log.info("Setting socketTimeout to: " + soTimeout); - } + this.scheme = getParameter(args, INIT_URL_SCHEME, "http://"); + this.scheme = (this.scheme.endsWith("://")) ? this.scheme : this.scheme + "://"; + this.connectionTimeout = getParameter(args, INIT_CONNECTION_TIMEOUT, 0); + this.maxConnectionsPerHost = getParameter(args, INIT_MAX_CONNECTION_PER_HOST, 20); + this.corePoolSize = getParameter(args, INIT_CORE_POOL_SIZE, 0); + this.maximumPoolSize = getParameter(args, INIT_MAX_POOL_SIZE, Integer.MAX_VALUE); + this.keepAliveTime = getParameter(args, MAX_THREAD_IDLE_TIME, 5); + this.queueSize = getParameter(args, INIT_SIZE_OF_QUEUE, -1); + this.accessPolicy = getParameter(args, INIT_FAIRNESS_POLICY, false); + + BlockingQueue blockingQueue = (this.queueSize == -1) ? + new SynchronousQueue(this.accessPolicy) : + new ArrayBlockingQueue(this.queueSize, this.accessPolicy); + + this.commExecutor = new ThreadPoolExecutor( + this.corePoolSize, + this.maximumPoolSize, + this.keepAliveTime, TimeUnit.SECONDS, + blockingQueue, + new DefaultSolrThreadFactory("httpShardExecutor") + ); - Object urlScheme = info.initArgs.get(INIT_URL_SCHEME); - if (urlScheme != null) { - scheme = urlScheme + "://"; - log.info("Setting urlScheme to: " + urlScheme); - } - Object co = info.initArgs.get(INIT_CONNECTION_TIMEOUT); - if (co != null) { - connectionTimeout = (Integer) co; - log.info("Setting shard-connection-timeout to: " + connectionTimeout); - } - } mgr = new MultiThreadedHttpConnectionManager(); - mgr.getParams().setDefaultMaxConnectionsPerHost(256); + mgr.getParams().setDefaultMaxConnectionsPerHost(this.maxConnectionsPerHost); mgr.getParams().setMaxTotalConnections(10000); - mgr.getParams().setConnectionTimeout(connectionTimeout); - mgr.getParams().setSoTimeout(soTimeout); + mgr.getParams().setConnectionTimeout(this.connectionTimeout); + mgr.getParams().setSoTimeout(this.soTimeout); // mgr.getParams().setStaleCheckingEnabled(false); client = new HttpClient(mgr); @@ -124,11 +147,22 @@ public class HttpShardHandlerFactory extends ShardHandlerFactory implements Plug loadbalancer = new LBHttpSolrServer(client); } catch (MalformedURLException e) { // should be impossible since we're not passing any URLs here - throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,e); + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e); } } + private T getParameter(NamedList initArgs, String configKey, T defaultValue) { + T toReturn = defaultValue; + if (initArgs != null) { + T temp = (T) initArgs.get(configKey); + toReturn = (temp != null) ? temp : defaultValue; + } + log.info("Setting {} to: {}", configKey, soTimeout); + return toReturn; + } + + @Override public void close() { try { diff --git a/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java b/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java index dcd46821f93..7fb303d2a0d 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java +++ b/solr/core/src/java/org/apache/solr/handler/component/StatsValuesFactory.java @@ -304,9 +304,8 @@ class NumericStatsValues extends AbstractStatsValues { */ class DateStatsValues extends AbstractStatsValues { - private static final DateField DATE_FIELD = new DateField(); - - private long sum; + private long sum = -1; + double sumOfSquares = 0; public DateStatsValues(SchemaField sf) { super(sf); @@ -317,22 +316,27 @@ class DateStatsValues extends AbstractStatsValues { */ protected void updateTypeSpecificStats(NamedList stv) { sum += ((Date) stv.get("sum")).getTime(); + sumOfSquares += ((Number)stv.get("sumOfSquares")).doubleValue(); } /** * {@inheritDoc} */ @Override - public void updateTypeSpecificStats(Date value) { - sum += value.getTime(); + public void updateTypeSpecificStats(Date v) { + long value = v.getTime(); + sumOfSquares += (value * value); // for std deviation + sum += value; } /** * {@inheritDoc} */ @Override - public void updateTypeSpecificStats(Date value, int count) { - sum += value.getTime() * count; + public void updateTypeSpecificStats(Date v, int count) { + long value = v.getTime(); + sumOfSquares += (value * value * count); // for std deviation + sum += value * count; } /** @@ -353,10 +357,29 @@ class DateStatsValues extends AbstractStatsValues { * @param res NamedList to add the type specific statistics too */ protected void addTypeSpecificStats(NamedList res) { + if(sum<=0) { + return; // date==0 is meaningless + } res.add("sum", new Date(sum)); if (count > 0) { res.add("mean", new Date(sum / count)); } + res.add("sumOfSquares", sumOfSquares); + res.add("stddev", getStandardDeviation()); + } + + + + /** + * Calculates the standard deviation. For dates, this is really the MS deviation + * + * @return Standard deviation statistic + */ + private double getStandardDeviation() { + if (count <= 1) { + return 0.0D; + } + return Math.sqrt(((count * sumOfSquares) - (sum * sum)) / (count * (count - 1.0D))); } } diff --git a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java index 2e0a9838c24..15c865557ca 100644 --- a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java +++ b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java @@ -21,22 +21,20 @@ import org.apache.lucene.index.*; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.search.*; import org.apache.lucene.search.grouping.AbstractAllGroupHeadsCollector; +import org.apache.lucene.search.grouping.term.TermGroupFacetCollector; import org.apache.lucene.util.*; import org.apache.lucene.util.packed.PackedInts; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; -import org.apache.solr.common.params.FacetParams; -import org.apache.solr.common.params.RequiredSolrParams; -import org.apache.solr.common.params.SolrParams; -import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.*; import org.apache.solr.common.params.FacetParams.FacetRangeOther; import org.apache.solr.common.params.FacetParams.FacetRangeInclude; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; -import org.apache.solr.core.SolrCore; import org.apache.solr.schema.*; import org.apache.solr.search.*; +import org.apache.solr.search.grouping.GroupingSpecification; import org.apache.solr.util.BoundedTreeSet; import org.apache.solr.util.DateMathParser; import org.apache.solr.util.DefaultSolrThreadFactory; @@ -290,32 +288,76 @@ public class SimpleFacets { multiToken = true; } - // unless the enum method is explicitly specified, use a counting method. - if (enumMethod) { - counts = getFacetTermEnumCounts(searcher, base, field, offset, limit, mincount,missing,sort,prefix); + if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) { + counts = getGroupedCounts(searcher, base, field, multiToken, offset,limit, mincount, missing, sort, prefix); } else { - if (multiToken) { - UnInvertedField uif = UnInvertedField.getUnInvertedField(field, searcher); - counts = uif.getCounts(searcher, base, offset, limit, mincount,missing,sort,prefix); + // unless the enum method is explicitly specified, use a counting method. + if (enumMethod) { + counts = getFacetTermEnumCounts(searcher, base, field, offset, limit, mincount,missing,sort,prefix); } else { - // TODO: future logic could use filters instead of the fieldcache if - // the number of terms in the field is small enough. - - if (per_segment) { - PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, base, field, offset,limit, mincount, missing, sort, prefix); - Executor executor = threads==0 ? directExecutor : facetExecutor; - ps.setNumThreads(threads); - counts = ps.getFacetCounts(executor); + if (multiToken) { + UnInvertedField uif = UnInvertedField.getUnInvertedField(field, searcher); + counts = uif.getCounts(searcher, base, offset, limit, mincount,missing,sort,prefix); } else { - counts = getFieldCacheCounts(searcher, base, field, offset,limit, mincount, missing, sort, prefix); - } + // TODO: future logic could use filters instead of the fieldcache if + // the number of terms in the field is small enough. + if (per_segment) { + PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, base, field, offset,limit, mincount, missing, sort, prefix); + Executor executor = threads == 0 ? directExecutor : facetExecutor; + ps.setNumThreads(threads); + counts = ps.getFacetCounts(executor); + } else { + counts = getFieldCacheCounts(searcher, base, field, offset,limit, mincount, missing, sort, prefix); + } + } } } return counts; } + public NamedList getGroupedCounts(SolrIndexSearcher searcher, + DocSet base, + String field, + boolean multiToken, + int offset, + int limit, + int mincount, + boolean missing, + String sort, + String prefix) throws IOException { + GroupingSpecification groupingSpecification = rb.getGroupingSpec(); + String groupField = groupingSpecification != null ? groupingSpecification.getFields()[0] : null; + if (groupField == null) { + throw new SolrException ( + SolrException.ErrorCode.BAD_REQUEST, + "Specify the group.field as parameter or local parameter" + ); + } + + BytesRef prefixBR = prefix != null ? new BytesRef(prefix) : null; + TermGroupFacetCollector collector = TermGroupFacetCollector.createTermGroupFacetCollector(groupField, field, multiToken, prefixBR, 128); + searcher.search(new MatchAllDocsQuery(), base.getTopFilter(), collector); + boolean orderByCount = sort.equals(FacetParams.FACET_SORT_COUNT) || sort.equals(FacetParams.FACET_SORT_COUNT_LEGACY); + TermGroupFacetCollector.GroupedFacetResult result = collector.mergeSegmentResults(offset + limit, mincount, orderByCount); + + CharsRef charsRef = new CharsRef(); + FieldType facetFieldType = searcher.getSchema().getFieldType(field); + NamedList facetCounts = new NamedList(); + List scopedEntries = result.getFacetEntries(offset, limit); + for (TermGroupFacetCollector.FacetEntry facetEntry : scopedEntries) { + facetFieldType.indexedToReadable(facetEntry.getValue(), charsRef); + facetCounts.add(charsRef.toString(), facetEntry.getCount()); + } + + if (missing) { + facetCounts.add(null, result.getTotalMissingCount()); + } + + return facetCounts; + } + static final Executor directExecutor = new Executor() { public void execute(Runnable r) { diff --git a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java index 6049dc87b4c..d3e4f3ea789 100755 --- a/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java +++ b/solr/core/src/java/org/apache/solr/response/CSVResponseWriter.java @@ -17,8 +17,8 @@ package org.apache.solr.response; -import org.apache.commons.csv.CSVPrinter; -import org.apache.commons.csv.CSVStrategy; +import org.apache.solr.internal.csv.CSVPrinter; +import org.apache.solr.internal.csv.CSVStrategy; import org.apache.lucene.index.IndexableField; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; diff --git a/solr/core/src/java/org/apache/solr/schema/BCDIntField.java b/solr/core/src/java/org/apache/solr/schema/BCDIntField.java index fb0f0b6519a..d127123a28d 100644 --- a/solr/core/src/java/org/apache/solr/schema/BCDIntField.java +++ b/solr/core/src/java/org/apache/solr/schema/BCDIntField.java @@ -24,16 +24,11 @@ import org.apache.lucene.index.IndexableField; import org.apache.solr.util.BCDUtils; import org.apache.solr.response.TextResponseWriter; -import java.util.Map; import java.io.IOException; /** * */ -public class BCDIntField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class BCDIntField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/BoolField.java b/solr/core/src/java/org/apache/solr/schema/BoolField.java index 992659093c6..0081e5ff84b 100644 --- a/solr/core/src/java/org/apache/solr/schema/BoolField.java +++ b/solr/core/src/java/org/apache/solr/schema/BoolField.java @@ -17,7 +17,6 @@ package org.apache.solr.schema; -import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.FieldCache; @@ -43,11 +42,7 @@ import java.io.IOException; /** * */ -public class BoolField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class BoolField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { field.checkSortability(); diff --git a/solr/core/src/java/org/apache/solr/schema/ByteField.java b/solr/core/src/java/org/apache/solr/schema/ByteField.java index 093c0dde9cc..650a9aae3b4 100644 --- a/solr/core/src/java/org/apache/solr/schema/ByteField.java +++ b/solr/core/src/java/org/apache/solr/schema/ByteField.java @@ -44,9 +44,10 @@ import java.util.Map; * * @see Byte */ -public class ByteField extends FieldType { +public class ByteField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/CurrencyField.java b/solr/core/src/java/org/apache/solr/schema/CurrencyField.java new file mode 100644 index 00000000000..034c5703578 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/schema/CurrencyField.java @@ -0,0 +1,770 @@ +package org.apache.solr.schema; +/** + * 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 org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.queries.function.FunctionValues; +import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SortField; +import org.apache.solr.common.ResourceLoader; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.response.TextResponseWriter; +import org.apache.solr.response.XMLWriter; +import org.apache.solr.search.QParser; +import org.apache.solr.search.SolrConstantScoreQuery; +import org.apache.solr.search.function.ValueSourceRangeFilter; +import org.apache.solr.util.plugin.ResourceLoaderAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Currency; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Field type for support of monetary values. + *

    + * See http://wiki.apache.org/solr/CurrencyField + */ +public class CurrencyField extends FieldType implements SchemaAware, ResourceLoaderAware { + protected static final String PARAM_DEFAULT_CURRENCY = "defaultCurrency"; + protected static final String PARAM_RATE_PROVIDER_CLASS = "providerClass"; + protected static final Object PARAM_PRECISION_STEP = "precisionStep"; + protected static final String DEFAULT_RATE_PROVIDER_CLASS = "org.apache.solr.schema.FileExchangeRateProvider"; + protected static final String DEFAULT_DEFAULT_CURRENCY = "USD"; + protected static final String DEFAULT_PRECISION_STEP = "0"; + protected static final String FIELD_SUFFIX_AMOUNT_RAW = "_amount_raw"; + protected static final String FIELD_SUFFIX_CURRENCY = "_currency"; + + private IndexSchema schema; + protected FieldType fieldTypeCurrency; + protected FieldType fieldTypeAmountRaw; + private String exchangeRateProviderClass; + private String defaultCurrency; + private ExchangeRateProvider provider; + public static Logger log = LoggerFactory.getLogger(CurrencyField.class); + + @Override + protected void init(IndexSchema schema, Map args) { + super.init(schema, args); + this.schema = schema; + this.exchangeRateProviderClass = args.get(PARAM_RATE_PROVIDER_CLASS); + this.defaultCurrency = args.get(PARAM_DEFAULT_CURRENCY); + + if (this.defaultCurrency == null) { + this.defaultCurrency = DEFAULT_DEFAULT_CURRENCY; + } + + if (this.exchangeRateProviderClass == null) { + this.exchangeRateProviderClass = DEFAULT_RATE_PROVIDER_CLASS; + } + + if (java.util.Currency.getInstance(this.defaultCurrency) == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid currency code " + this.defaultCurrency); + } + + String precisionStepString = args.get(PARAM_PRECISION_STEP); + if (precisionStepString == null) { + precisionStepString = DEFAULT_PRECISION_STEP; + } + + // Initialize field type for amount + fieldTypeAmountRaw = new TrieLongField(); + fieldTypeAmountRaw.setTypeName("amount_raw_type_tlong"); + Map map = new HashMap(1); + map.put("precisionStep", precisionStepString); + fieldTypeAmountRaw.init(schema, map); + + // Initialize field type for currency string + fieldTypeCurrency = new StrField(); + fieldTypeCurrency.setTypeName("currency_type_string"); + fieldTypeCurrency.init(schema, new HashMap()); + + args.remove(PARAM_RATE_PROVIDER_CLASS); + args.remove(PARAM_DEFAULT_CURRENCY); + args.remove(PARAM_PRECISION_STEP); + + try { + // TODO: Are we using correct classloader? + Class c = Class.forName(exchangeRateProviderClass); + Object clazz = c.newInstance(); + if (clazz instanceof ExchangeRateProvider) { + provider = (ExchangeRateProvider) clazz; + provider.init(args); + } else { + throw new SolrException(ErrorCode.BAD_REQUEST, "exchangeRateProvider "+exchangeRateProviderClass+" needs to implement ExchangeRateProvider"); + } + } catch (Exception e) { + throw new SolrException(ErrorCode.BAD_REQUEST, "Error instansiating exhange rate provider "+exchangeRateProviderClass+". Please check your FieldType configuration", e); + } + } + + @Override + public boolean isPolyField() { + return true; + } + + @Override + public IndexableField[] createFields(SchemaField field, Object externalVal, float boost) { + CurrencyValue value = CurrencyValue.parse(externalVal.toString(), defaultCurrency); + + IndexableField[] f = new IndexableField[field.stored() ? 3 : 2]; + f[0] = getAmountField(field).createField(String.valueOf(value.getAmount()), boost); + f[1] = getCurrencyField(field).createField(value.getCurrencyCode(), boost); + + if (field.stored()) { + org.apache.lucene.document.FieldType customType = new org.apache.lucene.document.FieldType(); + customType.setStored(true); + String storedValue = externalVal.toString().trim(); + if (storedValue.indexOf(",") < 0) { + storedValue += "," + defaultCurrency; + } + f[2] = createField(field.getName(), storedValue, customType, boost); + } + + return f; + } + + private SchemaField getAmountField(SchemaField field) { + return schema.getField(field.getName() + POLY_FIELD_SEPARATOR + FIELD_SUFFIX_AMOUNT_RAW); + } + + private SchemaField getCurrencyField(SchemaField field) { + return schema.getField(field.getName() + POLY_FIELD_SEPARATOR + FIELD_SUFFIX_CURRENCY); + } + + private void createDynamicCurrencyField(String suffix, FieldType type) { + String name = "*" + POLY_FIELD_SEPARATOR + suffix; + Map props = new HashMap(); + props.put("indexed", "true"); + props.put("stored", "false"); + props.put("multiValued", "false"); + props.put("omitNorms", "true"); + int p = SchemaField.calcProps(name, type, props); + schema.registerDynamicField(SchemaField.create(name, type, p, null)); + } + + /** + * When index schema is informed, add dynamic fields. + * + * @param indexSchema The index schema. + */ + public void inform(IndexSchema indexSchema) { + createDynamicCurrencyField(FIELD_SUFFIX_CURRENCY, fieldTypeCurrency); + createDynamicCurrencyField(FIELD_SUFFIX_AMOUNT_RAW, fieldTypeAmountRaw); + } + + /** + * Load the currency config when resource loader initialized. + * + * @param resourceLoader The resource loader. + */ + public void inform(ResourceLoader resourceLoader) { + provider.inform(resourceLoader); + boolean reloaded = provider.reload(); + if(!reloaded) { + log.warn("Failed reloading currencies"); + } + } + + @Override + public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) { + CurrencyValue value = CurrencyValue.parse(externalVal, defaultCurrency); + CurrencyValue valueDefault; + valueDefault = value.convertTo(provider, defaultCurrency); + + return getRangeQuery(parser, field, valueDefault, valueDefault, true, true); + } + + @Override + public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, final boolean minInclusive, final boolean maxInclusive) { + final CurrencyValue p1 = CurrencyValue.parse(part1, defaultCurrency); + final CurrencyValue p2 = CurrencyValue.parse(part2, defaultCurrency); + + if (!p1.getCurrencyCode().equals(p2.getCurrencyCode())) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Cannot parse range query " + part1 + " to " + part2 + + ": range queries only supported when upper and lower bound have same currency."); + } + + return getRangeQuery(parser, field, p1, p2, minInclusive, maxInclusive); + } + + public Query getRangeQuery(QParser parser, SchemaField field, final CurrencyValue p1, final CurrencyValue p2, final boolean minInclusive, final boolean maxInclusive) { + String currencyCode = p1.getCurrencyCode(); + final CurrencyValueSource vs = new CurrencyValueSource(field, currencyCode, parser); + + return new SolrConstantScoreQuery(new ValueSourceRangeFilter(vs, + p1.getAmount() + "", p2.getAmount() + "", minInclusive, maxInclusive)); + } + + @Override + public SortField getSortField(SchemaField field, boolean reverse) { + try { + // Convert all values to default currency for sorting. + return (new CurrencyValueSource(field, defaultCurrency, null)).getSortField(reverse); + } catch (IOException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); + } + } + + public void write(XMLWriter xmlWriter, String name, IndexableField field) throws IOException { + xmlWriter.writeStr(name, field.stringValue(), false); + } + + @Override + public void write(TextResponseWriter writer, String name, IndexableField field) throws IOException { + writer.writeStr(name, field.stringValue(), false); + } + + public ExchangeRateProvider getProvider() { + return provider; + } + + class CurrencyValueSource extends ValueSource { + private static final long serialVersionUID = 1L; + private String targetCurrencyCode; + private ValueSource currencyValues; + private ValueSource amountValues; + private final SchemaField sf; + + public CurrencyValueSource(SchemaField sfield, String targetCurrencyCode, QParser parser) { + this.sf = sfield; + this.targetCurrencyCode = targetCurrencyCode; + + SchemaField amountField = schema.getField(sf.getName() + POLY_FIELD_SEPARATOR + FIELD_SUFFIX_AMOUNT_RAW); + SchemaField currencyField = schema.getField(sf.getName() + POLY_FIELD_SEPARATOR + FIELD_SUFFIX_CURRENCY); + + currencyValues = currencyField.getType().getValueSource(currencyField, parser); + amountValues = amountField.getType().getValueSource(amountField, parser); + } + + public FunctionValues getValues(Map context, AtomicReaderContext reader) throws IOException { + final FunctionValues amounts = amountValues.getValues(context, reader); + final FunctionValues currencies = currencyValues.getValues(context, reader); + + return new FunctionValues() { + private final int MAX_CURRENCIES_TO_CACHE = 256; + private final int[] fractionDigitCache = new int[MAX_CURRENCIES_TO_CACHE]; + private final String[] currencyOrdToCurrencyCache = new String[MAX_CURRENCIES_TO_CACHE]; + private final double[] exchangeRateCache = new double[MAX_CURRENCIES_TO_CACHE]; + private int targetFractionDigits = -1; + private int targetCurrencyOrd = -1; + private boolean initializedCache; + + private String getDocCurrencyCode(int doc, int currencyOrd) { + if (currencyOrd < MAX_CURRENCIES_TO_CACHE) { + String currency = currencyOrdToCurrencyCache[currencyOrd]; + + if (currency == null) { + currencyOrdToCurrencyCache[currencyOrd] = currency = currencies.strVal(doc); + } + + if (currency == null) { + currency = defaultCurrency; + } + + if (targetCurrencyOrd == -1 && currency.equals(targetCurrencyCode)) { + targetCurrencyOrd = currencyOrd; + } + + return currency; + } else { + return currencies.strVal(doc); + } + } + + public long longVal(int doc) { + if (!initializedCache) { + for (int i = 0; i < fractionDigitCache.length; i++) { + fractionDigitCache[i] = -1; + } + + initializedCache = true; + } + + long amount = amounts.longVal(doc); + int currencyOrd = currencies.ordVal(doc); + + if (currencyOrd == targetCurrencyOrd) { + return amount; + } + + double exchangeRate; + int sourceFractionDigits; + + if (targetFractionDigits == -1) { + targetFractionDigits = Currency.getInstance(targetCurrencyCode).getDefaultFractionDigits(); + } + + if (currencyOrd < MAX_CURRENCIES_TO_CACHE) { + exchangeRate = exchangeRateCache[currencyOrd]; + + if (exchangeRate <= 0.0) { + String sourceCurrencyCode = getDocCurrencyCode(doc, currencyOrd); + exchangeRate = exchangeRateCache[currencyOrd] = provider.getExchangeRate(sourceCurrencyCode, targetCurrencyCode); + } + + sourceFractionDigits = fractionDigitCache[currencyOrd]; + + if (sourceFractionDigits == -1) { + String sourceCurrencyCode = getDocCurrencyCode(doc, currencyOrd); + sourceFractionDigits = fractionDigitCache[currencyOrd] = Currency.getInstance(sourceCurrencyCode).getDefaultFractionDigits(); + } + } else { + String sourceCurrencyCode = getDocCurrencyCode(doc, currencyOrd); + exchangeRate = provider.getExchangeRate(sourceCurrencyCode, targetCurrencyCode); + sourceFractionDigits = Currency.getInstance(sourceCurrencyCode).getDefaultFractionDigits(); + } + + return CurrencyValue.convertAmount(exchangeRate, sourceFractionDigits, amount, targetFractionDigits); + } + + public int intVal(int doc) { + return (int) longVal(doc); + } + + public double doubleVal(int doc) { + return (double) longVal(doc); + } + + public float floatVal(int doc) { + return (float) longVal(doc); + } + + public String strVal(int doc) { + return Long.toString(longVal(doc)); + } + + public String toString(int doc) { + return name() + '(' + amounts.toString(doc) + ',' + currencies.toString(doc) + ')'; + } + }; + } + + public String name() { + return "currency"; + } + + @Override + public String description() { + return name() + "(" + sf.getName() + ")"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CurrencyValueSource that = (CurrencyValueSource) o; + + return !(amountValues != null ? !amountValues.equals(that.amountValues) : that.amountValues != null) && + !(currencyValues != null ? !currencyValues.equals(that.currencyValues) : that.currencyValues != null) && + !(targetCurrencyCode != null ? !targetCurrencyCode.equals(that.targetCurrencyCode) : that.targetCurrencyCode != null); + + } + + @Override + public int hashCode() { + int result = targetCurrencyCode != null ? targetCurrencyCode.hashCode() : 0; + result = 31 * result + (currencyValues != null ? currencyValues.hashCode() : 0); + result = 31 * result + (amountValues != null ? amountValues.hashCode() : 0); + return result; + } + } +} + +/** + * Configuration for currency. Provides currency exchange rates. + */ +class FileExchangeRateProvider implements ExchangeRateProvider { + public static Logger log = LoggerFactory.getLogger(FileExchangeRateProvider.class); + protected static final String PARAM_CURRENCY_CONFIG = "currencyConfig"; + + // Exchange rate map, maps Currency Code -> Currency Code -> Rate + private Map> rates = new HashMap>(); + + private String currencyConfigFile; + private ResourceLoader loader; + + /** + * Returns the currently known exchange rate between two currencies. If a direct rate has been loaded, + * it is used. Otherwise, if a rate is known to convert the target currency to the source, the inverse + * exchange rate is computed. + * + * @param sourceCurrencyCode The source currency being converted from. + * @param targetCurrencyCode The target currency being converted to. + * @return The exchange rate. + * @throws an exception if the requested currency pair cannot be found + */ + public double getExchangeRate(String sourceCurrencyCode, String targetCurrencyCode) { + if (sourceCurrencyCode == null || targetCurrencyCode == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Cannot get exchange rate; currency was null."); + } + + if (sourceCurrencyCode.equals(targetCurrencyCode)) { + return 1.0; + } + + Double directRate = lookupRate(sourceCurrencyCode, targetCurrencyCode); + + if (directRate != null) { + return directRate; + } + + Double symmetricRate = lookupRate(targetCurrencyCode, sourceCurrencyCode); + + if (symmetricRate != null) { + return 1.0 / symmetricRate; + } + + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No available conversion rate between " + sourceCurrencyCode + " to " + targetCurrencyCode); + } + + /** + * Looks up the current known rate, if any, between the source and target currencies. + * + * @param sourceCurrencyCode The source currency being converted from. + * @param targetCurrencyCode The target currency being converted to. + * @return The exchange rate, or null if no rate has been registered. + */ + private Double lookupRate(String sourceCurrencyCode, String targetCurrencyCode) { + Map rhs = rates.get(sourceCurrencyCode); + + if (rhs != null) { + return rhs.get(targetCurrencyCode); + } + + return null; + } + + /** + * Registers the specified exchange rate. + * + * @param ratesMap The map to add rate to + * @param sourceCurrencyCode The source currency. + * @param targetCurrencyCode The target currency. + * @param rate The known exchange rate. + */ + private void addRate(Map> ratesMap, String sourceCurrencyCode, String targetCurrencyCode, double rate) { + Map rhs = ratesMap.get(sourceCurrencyCode); + + if (rhs == null) { + rhs = new HashMap(); + ratesMap.put(sourceCurrencyCode, rhs); + } + + rhs.put(targetCurrencyCode, rate); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FileExchangeRateProvider that = (FileExchangeRateProvider) o; + + return !(rates != null ? !rates.equals(that.rates) : that.rates != null); + } + + @Override + public int hashCode() { + return rates != null ? rates.hashCode() : 0; + } + + public String toString() { + return "["+this.getClass().getName()+" : " + rates.size() + " rates.]"; + } + + @Override + public String[] listAvailableCurrencies() { + List pairs = new ArrayList(); + for(String from : rates.keySet()) { + for(String to : rates.get(from).keySet()) { + pairs.add(from+","+to); + } + } + return pairs.toArray(new String[1]); + } + + @Override + public boolean reload() throws SolrException { + InputStream is = null; + Map> tmpRates = new HashMap>(); + try { + log.info("Reloading exchange rates from file "+this.currencyConfigFile); + + is = loader.openResource(currencyConfigFile); + javax.xml.parsers.DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + try { + dbf.setXIncludeAware(true); + dbf.setNamespaceAware(true); + } catch (UnsupportedOperationException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "XML parser doesn't support XInclude option", e); + } + + try { + Document doc = dbf.newDocumentBuilder().parse(is); + XPathFactory xpathFactory = XPathFactory.newInstance(); + XPath xpath = xpathFactory.newXPath(); + + // Parse exchange rates. + NodeList nodes = (NodeList) xpath.evaluate("/currencyConfig/rates/rate", doc, XPathConstants.NODESET); + + for (int i = 0; i < nodes.getLength(); i++) { + Node rateNode = nodes.item(i); + NamedNodeMap attributes = rateNode.getAttributes(); + Node from = attributes.getNamedItem("from"); + Node to = attributes.getNamedItem("to"); + Node rate = attributes.getNamedItem("rate"); + + if (from == null || to == null || rate == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Exchange rate missing attributes (required: from, to, rate) " + rateNode); + } + + String fromCurrency = from.getNodeValue(); + String toCurrency = to.getNodeValue(); + Double exchangeRate; + + if (java.util.Currency.getInstance(fromCurrency) == null || + java.util.Currency.getInstance(toCurrency) == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Could not find from currency specified in exchange rate: " + rateNode); + } + + try { + exchangeRate = Double.parseDouble(rate.getNodeValue()); + } catch (NumberFormatException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Could not parse exchange rate: " + rateNode, e); + } + + addRate(tmpRates, fromCurrency, toCurrency, exchangeRate); + } + } catch (SAXException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing currency config.", e); + } catch (IOException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing currency config.", e); + } catch (ParserConfigurationException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing currency config.", e); + } catch (XPathExpressionException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing currency config.", e); + } + } catch (IOException e) { + throw new SolrException(ErrorCode.BAD_REQUEST, "Error while opening Currency configuration file "+currencyConfigFile, e); + } finally { + try { + if (is != null) { + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + // Atomically swap in the new rates map, if it loaded successfully + this.rates = tmpRates; + return true; + } + + @Override + public void init(Map params) throws SolrException { + this.currencyConfigFile = params.get(PARAM_CURRENCY_CONFIG); + if(currencyConfigFile == null) { + throw new SolrException(ErrorCode.NOT_FOUND, "Missing required configuration "+PARAM_CURRENCY_CONFIG); + } + + // Removing config params custom to us + params.remove(PARAM_CURRENCY_CONFIG); + } + + @Override + public void inform(ResourceLoader loader) throws SolrException { + if(loader == null) { + throw new SolrException(ErrorCode.BAD_REQUEST, "Needs ResourceLoader in order to load config file"); + } + this.loader = loader; + reload(); + } +} + +/** + * Represents a Currency field value, which includes a long amount and ISO currency code. + */ +class CurrencyValue { + private long amount; + private String currencyCode; + + /** + * Constructs a new currency value. + * + * @param amount The amount. + * @param currencyCode The currency code. + */ + public CurrencyValue(long amount, String currencyCode) { + this.amount = amount; + this.currencyCode = currencyCode; + } + + /** + * Constructs a new currency value by parsing the specific input. + *

    + * Currency values are expected to be in the format <amount>,<currency code>, + * for example, "500,USD" would represent 5 U.S. Dollars. + *

    + * If no currency code is specified, the default is assumed. + * + * @param externalVal The value to parse. + * @param defaultCurrency The default currency. + * @return The parsed CurrencyValue. + */ + public static CurrencyValue parse(String externalVal, String defaultCurrency) { + String amount = externalVal; + String code = defaultCurrency; + + if (externalVal.contains(",")) { + String[] amountAndCode = externalVal.split(","); + amount = amountAndCode[0]; + code = amountAndCode[1]; + } + + Currency currency = java.util.Currency.getInstance(code); + + if (currency == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid currency code " + code); + } + + try { + double value = Double.parseDouble(amount); + long currencyValue = Math.round(value * Math.pow(10.0, currency.getDefaultFractionDigits())); + + return new CurrencyValue(currencyValue, code); + } catch (NumberFormatException e) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e); + } + } + + /** + * The amount of the CurrencyValue. + * + * @return The amount. + */ + public long getAmount() { + return amount; + } + + /** + * The ISO currency code of the CurrencyValue. + * + * @return The currency code. + */ + public String getCurrencyCode() { + return currencyCode; + } + + /** + * Performs a currency conversion & unit conversion. + * + * @param exchangeRates Exchange rates to apply. + * @param sourceCurrencyCode The source currency code. + * @param sourceAmount The source amount. + * @param targetCurrencyCode The target currency code. + * @return The converted indexable units after the exchange rate and currency fraction digits are applied. + */ + public static long convertAmount(ExchangeRateProvider exchangeRates, String sourceCurrencyCode, long sourceAmount, String targetCurrencyCode) { + double exchangeRate = exchangeRates.getExchangeRate(sourceCurrencyCode, targetCurrencyCode); + return convertAmount(exchangeRate, sourceCurrencyCode, sourceAmount, targetCurrencyCode); + } + + /** + * Performs a currency conversion & unit conversion. + * + * @param exchangeRate Exchange rate to apply. + * @param sourceFractionDigits The fraction digits of the source. + * @param sourceAmount The source amount. + * @param targetFractionDigits The fraction digits of the target. + * @return The converted indexable units after the exchange rate and currency fraction digits are applied. + */ + public static long convertAmount(final double exchangeRate, final int sourceFractionDigits, final long sourceAmount, final int targetFractionDigits) { + int digitDelta = targetFractionDigits - sourceFractionDigits; + double value = ((double) sourceAmount * exchangeRate); + + if (digitDelta != 0) { + if (digitDelta < 0) { + for (int i = 0; i < -digitDelta; i++) { + value *= 0.1; + } + } else { + for (int i = 0; i < digitDelta; i++) { + value *= 10.0; + } + } + } + + return (long) value; + } + + /** + * Performs a currency conversion & unit conversion. + * + * @param exchangeRate Exchange rate to apply. + * @param sourceCurrencyCode The source currency code. + * @param sourceAmount The source amount. + * @param targetCurrencyCode The target currency code. + * @return The converted indexable units after the exchange rate and currency fraction digits are applied. + */ + public static long convertAmount(double exchangeRate, String sourceCurrencyCode, long sourceAmount, String targetCurrencyCode) { + if (targetCurrencyCode.equals(sourceCurrencyCode)) { + return sourceAmount; + } + + int sourceFractionDigits = Currency.getInstance(sourceCurrencyCode).getDefaultFractionDigits(); + Currency targetCurrency = Currency.getInstance(targetCurrencyCode); + int targetFractionDigits = targetCurrency.getDefaultFractionDigits(); + return convertAmount(exchangeRate, sourceFractionDigits, sourceAmount, targetFractionDigits); + } + + /** + * Returns a new CurrencyValue that is the conversion of this CurrencyValue to the specified currency. + * + * @param exchangeRates The exchange rate provider. + * @param targetCurrencyCode The target currency code to convert this CurrencyValue to. + * @return The converted CurrencyValue. + */ + public CurrencyValue convertTo(ExchangeRateProvider exchangeRates, String targetCurrencyCode) { + return new CurrencyValue(convertAmount(exchangeRates, this.getCurrencyCode(), this.getAmount(), targetCurrencyCode), targetCurrencyCode); + } + + public String toString() { + return String.valueOf(amount) + "," + currencyCode; + } +} \ No newline at end of file diff --git a/solr/core/src/java/org/apache/solr/schema/DateField.java b/solr/core/src/java/org/apache/solr/schema/DateField.java index 1dab8af9954..4fd40bc66a1 100644 --- a/solr/core/src/java/org/apache/solr/schema/DateField.java +++ b/solr/core/src/java/org/apache/solr/schema/DateField.java @@ -34,7 +34,6 @@ import org.apache.solr.common.util.DateUtil; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.TextResponseWriter; import org.apache.solr.search.QParser; -import org.apache.solr.search.function.*; import org.apache.solr.util.DateMathParser; import java.io.IOException; @@ -102,7 +101,7 @@ import java.util.*; * @see XML schema part 2 * */ -public class DateField extends FieldType { +public class DateField extends PrimitiveFieldType { public static TimeZone UTC = TimeZone.getTimeZone("UTC"); @@ -129,10 +128,6 @@ public class DateField extends FieldType { // The easiest fix is to simply remove the 'Z' for the internal // format. - @Override - protected void init(IndexSchema schema, Map args) { - } - protected static String NOW = "NOW"; protected static char Z = 'Z'; private static char[] Z_ARRAY = new char[] {Z}; diff --git a/solr/core/src/java/org/apache/solr/schema/DoubleField.java b/solr/core/src/java/org/apache/solr/schema/DoubleField.java index 3131fa094b9..0a7b740d405 100644 --- a/solr/core/src/java/org/apache/solr/schema/DoubleField.java +++ b/solr/core/src/java/org/apache/solr/schema/DoubleField.java @@ -41,9 +41,10 @@ import java.util.Map; * * @see TrieDoubleField */ -public class DoubleField extends FieldType { +public class DoubleField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/ExchangeRateProvider.java b/solr/core/src/java/org/apache/solr/schema/ExchangeRateProvider.java new file mode 100644 index 00000000000..eb2fc6c3009 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/schema/ExchangeRateProvider.java @@ -0,0 +1,69 @@ +package org.apache.solr.schema; +/** + * 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.util.Map; + +import org.apache.solr.common.ResourceLoader; +import org.apache.solr.common.SolrException; + +/** + * Interface for providing pluggable exchange rate providers to @CurrencyField + */ +public interface ExchangeRateProvider { + /** + * Get the exchange rate betwen the two given currencies + * @param sourceCurrencyCode + * @param targetCurrencyCode + * @return the exhange rate as a double + * @throws exception if the rate is not defined in the provider + */ + public double getExchangeRate(String sourceCurrencyCode, String targetCurrencyCode) throws SolrException; + + /** + * List all configured currency code pairs + * @return a string array of ISO 4217 currency codes on the format + * ["SRC,DST", "SRC,DST"...] + */ + public String[] listAvailableCurrencies(); + + /** + * Ask the currency provider to explicitly reload/refresh its configuration. + * If this does not make sense for a particular provider, simply do nothing + * @throws SolrException if there is a problem reloading + * @return true if reload of rates succeeded, else false + */ + public boolean reload() throws SolrException; + + /** + * Initializes the provider by passing in a set of key/value configs as a map. + * Note that the map also contains other fieldType parameters, so make sure to + * avoid name clashes. + *

    + * Important: Custom config params must be removed from the map before returning + * @param args a @Map of key/value config params to initialize the provider + */ + public void init(Map args); + + /** + * Passes a ResourceLoader, used to read config files from e.g. ZooKeeper. + * Implementations not needing resource loader can implement this as NOOP. + *

    Typically called after init + * @param loader a @ResourceLoader instance + */ + public void inform(ResourceLoader loader) throws SolrException; +} diff --git a/solr/core/src/java/org/apache/solr/schema/FloatField.java b/solr/core/src/java/org/apache/solr/schema/FloatField.java index 4229638dc96..45c546fbe08 100644 --- a/solr/core/src/java/org/apache/solr/schema/FloatField.java +++ b/solr/core/src/java/org/apache/solr/schema/FloatField.java @@ -40,9 +40,10 @@ import java.io.IOException; * * @see TrieFloatField */ -public class FloatField extends FieldType { +public class FloatField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java index eba2502a823..7cac8994e5b 100644 --- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java +++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java @@ -920,7 +920,7 @@ public final class IndexSchema { /*** REMOVED -YCS if (defaultFieldType != null) return new SchemaField(fieldName,defaultFieldType); ***/ - throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"undefined field "+fieldName); + throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"undefined field: \""+fieldName+"\""); } /** diff --git a/solr/core/src/java/org/apache/solr/schema/IntField.java b/solr/core/src/java/org/apache/solr/schema/IntField.java index c074d5338cd..286c24ebcea 100644 --- a/solr/core/src/java/org/apache/solr/schema/IntField.java +++ b/solr/core/src/java/org/apache/solr/schema/IntField.java @@ -40,9 +40,10 @@ import java.io.IOException; * * @see TrieIntField */ -public class IntField extends FieldType { +public class IntField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/LongField.java b/solr/core/src/java/org/apache/solr/schema/LongField.java index a8fe83b715e..d20040d5986 100644 --- a/solr/core/src/java/org/apache/solr/schema/LongField.java +++ b/solr/core/src/java/org/apache/solr/schema/LongField.java @@ -40,9 +40,10 @@ import java.util.Map; * * @see TrieLongField */ -public class LongField extends FieldType { +public class LongField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/PrimitiveFieldType.java b/solr/core/src/java/org/apache/solr/schema/PrimitiveFieldType.java new file mode 100644 index 00000000000..983b5f3761c --- /dev/null +++ b/solr/core/src/java/org/apache/solr/schema/PrimitiveFieldType.java @@ -0,0 +1,35 @@ +/** + * 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. + */ + +package org.apache.solr.schema; + +import java.util.Map; + +/** + * Abstract class defining shared behavior for primitive types + * Intended to be used as base class for non-analyzed fields like + * int, float, string, date etc, and set proper defaults for them + */ +public abstract class PrimitiveFieldType extends FieldType { + @Override + protected void init(IndexSchema schema, Map args) { + super.init(schema, args); + if(schema.getVersion() > 1.4) { + properties |= OMIT_NORMS; + } + } +} diff --git a/solr/core/src/java/org/apache/solr/schema/ShortField.java b/solr/core/src/java/org/apache/solr/schema/ShortField.java index 59cab5a3f05..c0b6b200094 100644 --- a/solr/core/src/java/org/apache/solr/schema/ShortField.java +++ b/solr/core/src/java/org/apache/solr/schema/ShortField.java @@ -45,9 +45,10 @@ import java.util.Map; * * @see Short **/ -public class ShortField extends FieldType { +public class ShortField extends PrimitiveFieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); restrictProps(SORT_MISSING_FIRST | SORT_MISSING_LAST); } diff --git a/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java b/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java index 678c766b3e3..fb099647983 100644 --- a/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java +++ b/solr/core/src/java/org/apache/solr/schema/SortableDoubleField.java @@ -52,11 +52,7 @@ import java.io.IOException; * @deprecated use {@link DoubleField} or {@link TrieDoubleField} - will be removed in 5.x */ @Deprecated -public class SortableDoubleField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class SortableDoubleField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java b/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java index a497e98f709..62dd5a045a9 100644 --- a/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java +++ b/solr/core/src/java/org/apache/solr/schema/SortableFloatField.java @@ -53,11 +53,7 @@ import java.io.IOException; * @deprecated use {@link FloatField} or {@link TrieFloatField} - will be removed in 5.x */ @Deprecated -public class SortableFloatField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class SortableFloatField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/SortableIntField.java b/solr/core/src/java/org/apache/solr/schema/SortableIntField.java index 6c6983efd74..56241ddd2d5 100644 --- a/solr/core/src/java/org/apache/solr/schema/SortableIntField.java +++ b/solr/core/src/java/org/apache/solr/schema/SortableIntField.java @@ -53,11 +53,7 @@ import java.io.IOException; * @deprecated use {@link IntField} or {@link TrieIntField} - will be removed in 5.x */ @Deprecated -public class SortableIntField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class SortableIntField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/SortableLongField.java b/solr/core/src/java/org/apache/solr/schema/SortableLongField.java index e50e877e4e8..eae24306cfa 100644 --- a/solr/core/src/java/org/apache/solr/schema/SortableLongField.java +++ b/solr/core/src/java/org/apache/solr/schema/SortableLongField.java @@ -52,11 +52,7 @@ import java.io.IOException; * @deprecated use {@link LongField} or {@link TrieLongField} - will be removed in 5.x */ @Deprecated -public class SortableLongField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - } - +public class SortableLongField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/StrField.java b/solr/core/src/java/org/apache/solr/schema/StrField.java index b9aafe102ac..6d50b666458 100644 --- a/solr/core/src/java/org/apache/solr/schema/StrField.java +++ b/solr/core/src/java/org/apache/solr/schema/StrField.java @@ -24,17 +24,11 @@ import org.apache.lucene.util.BytesRef; import org.apache.solr.response.TextResponseWriter; import org.apache.solr.search.QParser; -import java.util.Map; import java.io.IOException; /** * */ -public class StrField extends FieldType { - @Override - protected void init(IndexSchema schema, Map args) { - super.init(schema, args); - } - +public class StrField extends PrimitiveFieldType { @Override public SortField getSortField(SchemaField field,boolean reverse) { return getStringSort(field,reverse); diff --git a/solr/core/src/java/org/apache/solr/schema/TrieField.java b/solr/core/src/java/org/apache/solr/schema/TrieField.java index 92e4764d7e0..3787c731bd7 100644 --- a/solr/core/src/java/org/apache/solr/schema/TrieField.java +++ b/solr/core/src/java/org/apache/solr/schema/TrieField.java @@ -65,7 +65,7 @@ import org.apache.solr.search.function.*; * @see org.apache.lucene.search.NumericRangeQuery * @since solr 1.4 */ -public class TrieField extends org.apache.solr.schema.FieldType { +public class TrieField extends PrimitiveFieldType { public static final int DEFAULT_PRECISION_STEP = 8; protected int precisionStepArg = TrieField.DEFAULT_PRECISION_STEP; // the one passed in or defaulted @@ -81,6 +81,7 @@ public class TrieField extends org.apache.solr.schema.FieldType { @Override protected void init(IndexSchema schema, Map args) { + super.init(schema, args); String p = args.remove("precisionStep"); if (p != null) { precisionStepArg = Integer.parseInt(p); diff --git a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java index 55e15508f35..b74e924ae25 100755 --- a/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/ExtendedDismaxQParserPlugin.java @@ -22,6 +22,16 @@ package org.apache.solr.search; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.queries.function.BoostedQuery; import org.apache.lucene.queries.function.FunctionQuery; import org.apache.lucene.queries.function.ValueSource; @@ -30,7 +40,6 @@ import org.apache.lucene.queries.function.valuesource.QueryValueSource; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.*; -import org.apache.lucene.analysis.Analyzer; import org.apache.solr.common.params.DisMaxParams; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.NamedList; @@ -39,8 +48,6 @@ import org.apache.solr.schema.FieldType; import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.analysis.*; -import java.util.*; - /** * An advanced multi-field query parser. * @lucene.experimental @@ -74,7 +81,10 @@ class ExtendedDismaxQParser extends QParser { /** shorten the class references for utilities */ private static interface DMP extends DisMaxParams { - /* :NOOP */ + /** + * User fields. The fields that can be used by the end user to create field-specific queries. + */ + public static String UF = "uf"; } @@ -82,15 +92,30 @@ class ExtendedDismaxQParser extends QParser { super(qstr, localParams, params, req); } - Map queryFields; - Query parsedUserQuery; + /** + * The field names specified by 'qf' that (most) clauses will + * be queried against + */ + private Map queryFields; + + /** + * The field names specified by 'uf' that users are + * allowed to include literally in their query string. The Float + * boost values will be applied automaticly to any clause using that + * field name. '*' will be treated as an alias for any + * field that exists in the schema. Wildcards are allowed to + * express dynamicFields. + */ + private UserFields userFields; + private Query parsedUserQuery; private String[] boostParams; private String[] multBoosts; private List boostQueries; private Query altUserQuery; private QParser altQParser; + private SolrParams solrParams; @Override @@ -98,11 +123,13 @@ class ExtendedDismaxQParser extends QParser { SolrParams localParams = getLocalParams(); SolrParams params = getParams(); - SolrParams solrParams = SolrParams.wrapDefaults(localParams, params); + solrParams = SolrParams.wrapDefaults(localParams, params); final String minShouldMatch = DisMaxQParser.parseMinShouldMatch(req.getSchema(), solrParams); + userFields = new UserFields(U.parseFieldBoosts(solrParams.getParams(DMP.UF))); + queryFields = SolrPluginUtils.parseFieldBoosts(solrParams.getParams(DisMaxParams.QF)); if (0 == queryFields.size()) { queryFields.put(req.getSchema().getDefaultSearchFieldName(), 1.0f); @@ -158,75 +185,59 @@ class ExtendedDismaxQParser extends QParser { new ExtendedSolrQueryParser(this, IMPOSSIBLE_FIELD_NAME); up.addAlias(IMPOSSIBLE_FIELD_NAME, tiebreaker, queryFields); + addAliasesFromRequest(up, tiebreaker); up.setPhraseSlop(qslop); // slop for explicit user phrase queries up.setAllowLeadingWildcard(true); // defer escaping and only do if lucene parsing fails, or we need phrases // parsing fails. Need to sloppy phrase queries anyway though. List clauses = null; - boolean specialSyntax = false; int numPluses = 0; int numMinuses = 0; - int numOptional = 0; - int numAND = 0; int numOR = 0; int numNOT = 0; - boolean sawLowerAnd=false; - boolean sawLowerOr=false; clauses = splitIntoClauses(userQuery, false); for (Clause clause : clauses) { - if (!clause.isPhrase && clause.hasSpecialSyntax) { - specialSyntax = true; - } if (clause.must == '+') numPluses++; if (clause.must == '-') numMinuses++; if (clause.isBareWord()) { String s = clause.val; - if ("AND".equals(s)) { - numAND++; - } else if ("OR".equals(s)) { + if ("OR".equals(s)) { numOR++; } else if ("NOT".equals(s)) { numNOT++; - } else if (lowercaseOperators) { - if ("and".equals(s)) { - numAND++; - sawLowerAnd=true; - } else if ("or".equals(s)) { - numOR++; - sawLowerOr=true; - } + } else if (lowercaseOperators && "or".equals(s)) { + numOR++; } } } - numOptional = clauses.size() - (numPluses + numMinuses); - // convert lower or mixed case operators to uppercase if we saw them. + // Always rebuild mainUserQuery from clauses to catch modifications from splitIntoClauses + // This was necessary for userFields modifications to get propagated into the query. + // Convert lower or mixed case operators to uppercase if we saw them. // only do this for the lucene query part and not for phrase query boosting // since some fields might not be case insensitive. // We don't use a regex for this because it might change and AND or OR in // a phrase query in a case sensitive field. - if (sawLowerAnd || sawLowerOr) { - StringBuilder sb = new StringBuilder(); - for (int i=0; i0 && i+10 && i+1 it = solrParams.getParameterNamesIterator(); + while(it.hasNext()) { + String param = it.next(); + if(param.startsWith("f.") && param.endsWith(".qf")) { + // Add the alias + String fname = param.substring(2,param.length()-3); + String qfReplacement = solrParams.get(param); + Map parsedQf = SolrPluginUtils.parseFieldBoosts(qfReplacement); + if(parsedQf.size() == 0) + return; + up.addAlias(fname, tiebreaker, parsedQf); + } + } + } + /** * Modifies the main query by adding a new optional Query consisting * of shingled phrase queries across the specified clauses using the @@ -598,6 +636,7 @@ class ExtendedDismaxQParser extends QParser { int end=s.length(); char ch=0; int start; + boolean disallowUserField = false; outer: while (pos < end) { ch = s.charAt(pos); @@ -614,6 +653,10 @@ class ExtendedDismaxQParser extends QParser { } clause.field = getFieldName(s, pos, end); + if(clause.field != null && !userFields.isAllowed(clause.field)) { + disallowUserField = true; + clause.field = null; + } if (clause.field != null) { pos += clause.field.length(); // skip the field name pos++; // skip the ':' @@ -709,15 +752,31 @@ class ExtendedDismaxQParser extends QParser { } if (clause != null) { - clause.raw = s.substring(start, pos); + if(disallowUserField) { + clause.raw = clause.val; + } else { + clause.raw = s.substring(start, pos); + // Add default userField boost if no explicit boost exists + if(userFields.isAllowed(clause.field) && !clause.raw.contains("^")) { + Float boost = userFields.getBoost(clause.field); + if(boost != null) + clause.raw += "^" + boost; + } + } lst.add(clause); } clause = new Clause(); + disallowUserField = false; } return lst; } + /** + * returns a field name or legal field alias from the current + * position of the string + * @param solrParams + */ public String getFieldName(String s, int pos, int end) { if (pos >= end) return null; int p=pos; @@ -731,10 +790,12 @@ class ExtendedDismaxQParser extends QParser { if (!(Character.isJavaIdentifierPart(ch) || ch=='-' || ch=='.')) return null; } String fname = s.substring(pos, p); - return getReq().getSchema().getFieldTypeNoEx(fname) == null ? null : fname; + boolean isInSchema = getReq().getSchema().getFieldTypeNoEx(fname) != null; + boolean isAlias = solrParams.get("f."+fname+".qf") != null; + + return (isInSchema || isAlias) ? fname : null; } - public static List split(String s, boolean ignoreQuote) { ArrayList lst = new ArrayList(4); int pos=0, start=0, end=s.length(); @@ -792,7 +853,7 @@ class ExtendedDismaxQParser extends QParser { * A subclass of SolrQueryParser that supports aliasing fields for * constructing DisjunctionMaxQueries. */ - class ExtendedSolrQueryParser extends SolrQueryParser { + static class ExtendedSolrQueryParser extends SolrQueryParser { /** A simple container for storing alias info @@ -870,6 +931,16 @@ class ExtendedDismaxQParser extends QParser { a.fields = fieldBoosts; aliases.put(field, a); } + + /** + * Returns the aliases found for a field. + * Returns null if there are no aliases for the field + * @param field + * @return + */ + public Alias getAlias(String field) { + return aliases.get(field); + } QType type; @@ -980,9 +1051,9 @@ class ExtendedDismaxQParser extends QParser { * DisjunctionMaxQuery. (so yes: aliases which point at other * aliases should work) */ - protected Query getAliasedQuery() - throws ParseException { + protected Query getAliasedQuery() throws ParseException { Alias a = aliases.get(field); + this.validateCyclicAliasing(field); if (a != null) { List lst = getQueries(a); if (lst == null || lst.size()==0) @@ -1018,15 +1089,43 @@ class ExtendedDismaxQParser extends QParser { } } + /** + * Validate there is no cyclic referencing in the aliasing + */ + private void validateCyclicAliasing(String field) throws ParseException { + Set set = new HashSet(); + set.add(field); + if(validateField(field, set)) { + throw new ParseException("Field aliases lead to a cycle"); + } + } + + private boolean validateField(String field, Set set) { + if(this.getAlias(field) == null) { + return false; + } + boolean hascycle = false; + for(String referencedField:this.getAlias(field).fields.keySet()) { + if(!set.add(referencedField)) { + hascycle = true; + } else { + if(validateField(referencedField, set)) { + hascycle = true; + } + set.remove(referencedField); + } + } + return hascycle; + } - protected List getQueries(Alias a) throws ParseException { + protected List getQueries(Alias a) throws ParseException { if (a == null) return null; if (a.fields.size()==0) return null; List lst= new ArrayList(4); for (String f : a.fields.keySet()) { this.field = f; - Query sub = getQuery(); + Query sub = getAliasedQuery(); if (sub != null) { Float boost = a.fields.get(f); if (boost != null) { @@ -1127,4 +1226,129 @@ class ExtendedDismaxQParser extends QParser { if (q instanceof BooleanQuery && ((BooleanQuery)q).clauses().size()==0) return true; return false; } + + /** + * Class that encapsulates the input from userFields parameter and can answer whether + * a field allowed or disallowed as fielded query in the query string + */ + static class UserFields { + private Map userFieldsMap; + private DynamicField[] dynamicUserFields; + private DynamicField[] negativeDynamicUserFields; + + UserFields(Map ufm) { + userFieldsMap = ufm; + if (0 == userFieldsMap.size()) { + userFieldsMap.put("*", null); + } + + // Process dynamic patterns in userFields + ArrayList dynUserFields = new ArrayList(); + ArrayList negDynUserFields = new ArrayList(); + for(String f : userFieldsMap.keySet()) { + if(f.contains("*")) { + if(f.startsWith("-")) + negDynUserFields.add(new DynamicField(f.substring(1))); + else + dynUserFields.add(new DynamicField(f)); + } + } + Collections.sort(dynUserFields); + dynamicUserFields = dynUserFields.toArray(new DynamicField[dynUserFields.size()]); + Collections.sort(negDynUserFields); + negativeDynamicUserFields = negDynUserFields.toArray(new DynamicField[negDynUserFields.size()]); +// System.out.println("** userF="+userFieldsMap+", dynUF="+Arrays.toString(dynamicUserFields)+", negDynUF="+Arrays.toString(negativeDynamicUserFields)); + } + + /** + * Is the given field name allowed according to UserFields spec given in the uf parameter? + * @param fname the field name to examine + * @return true if the fielded queries are allowed on this field + */ + public boolean isAllowed(String fname) { + boolean res = ((userFieldsMap.containsKey(fname) || isDynField(fname, false)) && + !userFieldsMap.containsKey("-"+fname) && + !isDynField(fname, true)); + return res; + } + + private boolean isDynField(String field, boolean neg) { + return getDynFieldForName(field, neg) == null ? false : true; + } + + private String getDynFieldForName(String f, boolean neg) { + for( DynamicField df : neg?negativeDynamicUserFields:dynamicUserFields ) { + if( df.matches( f ) ) return df.wildcard; + } + return null; + } + + /** + * Finds the default user field boost associated with the given field. + * This is parsed from the uf parameter, and may be specified as wildcards, e.g. *name^2.0 or *^3.0 + * @param field the field to find boost for + * @return the float boost value associated with the given field or a wildcard matching the field + */ + public Float getBoost(String field) { + return (userFieldsMap.containsKey(field)) ? + userFieldsMap.get(field) : // Exact field + userFieldsMap.get(getDynFieldForName(field, false)); // Dynamic field + } + } + + /* Represents a dynamic field, for easier matching, inspired by same class in IndexSchema */ + static class DynamicField implements Comparable { + final static int STARTS_WITH=1; + final static int ENDS_WITH=2; + final static int CATCHALL=3; + + final String wildcard; + final int type; + + final String str; + + protected DynamicField(String wildcard) { + this.wildcard = wildcard; + if (wildcard.equals("*")) { + type=CATCHALL; + str=null; + } + else if (wildcard.startsWith("*")) { + type=ENDS_WITH; + str=wildcard.substring(1); + } + else if (wildcard.endsWith("*")) { + type=STARTS_WITH; + str=wildcard.substring(0,wildcard.length()-1); + } + else { + throw new RuntimeException("dynamic field name must start or end with *"); + } + } + + /* + * Returns true if the regex wildcard for this DynamicField would match the input field name + */ + public boolean matches(String name) { + if (type==CATCHALL) return true; + else if (type==STARTS_WITH && name.startsWith(str)) return true; + else if (type==ENDS_WITH && name.endsWith(str)) return true; + else return false; + } + + /** + * Sort order is based on length of regex. Longest comes first. + * @param other The object to compare to. + * @return a negative integer, zero, or a positive integer + * as this object is less than, equal to, or greater than + * the specified object. + */ + public int compareTo(DynamicField other) { + return other.wildcard.length() - wildcard.length(); + } + + public String toString() { + return this.wildcard; + } + } } diff --git a/solr/core/src/java/org/apache/solr/search/ReturnFields.java b/solr/core/src/java/org/apache/solr/search/ReturnFields.java index a6db404cb30..e5e6df75e8e 100644 --- a/solr/core/src/java/org/apache/solr/search/ReturnFields.java +++ b/solr/core/src/java/org/apache/solr/search/ReturnFields.java @@ -118,11 +118,6 @@ public class ReturnFields augmenters.addTransformer( new RenameFieldsTransformer( rename ) ); } - // Legacy behavior: "score" == "*,score" - if( fields.size() == 1 && _wantsScore && augmenters.size() == 1 && globs.isEmpty() ) { - _wantsAllFields = true; - } - if( !_wantsAllFields ) { if( !globs.isEmpty() ) { // TODO??? need to fill up the fields with matching field names in the index diff --git a/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java b/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java index 008ec76727e..934773e667a 100644 --- a/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java +++ b/solr/core/src/java/org/apache/solr/servlet/ZookeeperInfoServlet.java @@ -17,11 +17,11 @@ package org.apache.solr.servlet; +import org.apache.lucene.util.BytesRef; import org.apache.noggit.CharArr; import org.apache.noggit.JSONWriter; import org.apache.solr.cloud.ZkController; import org.apache.solr.common.cloud.SolrZkClient; -import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.CoreContainer; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Stat; @@ -34,7 +34,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Date; import java.util.List; @@ -72,11 +71,16 @@ public final class ZookeeperInfoServlet extends HttpServlet { String detailS = request.getParameter("detail"); boolean detail = detailS != null && detailS.equals("true"); + + String dumpS = request.getParameter("dump"); + boolean dump = dumpS != null && dumpS.equals("true"); + PrintWriter out = response.getWriter(); ZKPrinter printer = new ZKPrinter(response, out, cores.getZkController(), addr); printer.detail = detail; + printer.dump = dump; try { printer.print(path); @@ -103,6 +107,7 @@ public final class ZookeeperInfoServlet extends HttpServlet { boolean indent = true; boolean fullpath = FULLPATH_DEFAULT; boolean detail = false; + boolean dump = false; String addr; // the address passed to us String keeperAddr; // the address we're connected to @@ -260,6 +265,7 @@ public final class ZookeeperInfoServlet extends HttpServlet { Stat stat = new Stat(); try { + // Trickily, the call to zkClient.getData fills in the stat variable byte[] data = zkClient.getData(path, null, stat, true); if (stat.getEphemeralOwner() != 0) { @@ -267,6 +273,11 @@ public final class ZookeeperInfoServlet extends HttpServlet { writeKeyValue(json, "version", stat.getVersion(), false); } + if (dump) { + json.writeValueSeparator(); + printZnode(json, path); + } + /* if (stat.getNumChildren() != 0) { @@ -275,32 +286,12 @@ public final class ZookeeperInfoServlet extends HttpServlet { } */ - //if (data != null) - if (stat.getDataLength() != 0) { - String str; - try { - str = new String(data, "UTF-8"); - str = str.replaceAll("\\\"", "\\\\\""); - - //writeKeyValue(json, "content", str, false ); - } catch (UnsupportedEncodingException e) { - // not UTF8 - StringBuilder sb = new StringBuilder("BIN("); - sb.append("len=" + data.length); - sb.append("hex="); - int limit = Math.min(data.length, maxData / 2); - for (int i = 0; i < limit; i++) { - byte b = data[i]; - sb.append(StrUtils.HEX_DIGITS[(b >> 4) & 0xf]); - sb.append(StrUtils.HEX_DIGITS[b & 0xf]); - } - if (limit != data.length) { - sb.append("..."); - } - sb.append(")"); - str = sb.toString(); - //?? writeKeyValue(json, "content", str, false ); - } + //if (stat.getDataLength() != 0) + if (data != null) { + String str = new BytesRef(data).utf8ToString(); + //?? writeKeyValue(json, "content", str, false ); + // Does nothing now, but on the assumption this will be used later we'll leave it in. If it comes out + // the catches below need to be restructured. } } catch (IllegalArgumentException e) { // path doesn't exist (must have been removed) @@ -375,6 +366,7 @@ public final class ZookeeperInfoServlet extends HttpServlet { boolean printZnode(JSONWriter json, String path) throws IOException { try { Stat stat = new Stat(); + // Trickily, the call to zkClient.getData fills in the stat variable byte[] data = zkClient.getData(path, null, stat, true); json.writeString("znode"); @@ -400,28 +392,8 @@ public final class ZookeeperInfoServlet extends HttpServlet { writeKeyValue(json, "pzxid", stat.getPzxid(), false); json.endObject(); - if (stat.getDataLength() != 0) { - String str; - try { - str = new String(data, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // The results are unspecified - // when the bytes are not properly encoded. - - // not UTF8 - StringBuilder sb = new StringBuilder(data.length * 2); - for (int i = 0; i < data.length; i++) { - byte b = data[i]; - sb.append(StrUtils.HEX_DIGITS[(b >> 4) & 0xf]); - sb.append(StrUtils.HEX_DIGITS[b & 0xf]); - if ((i & 0x3f) == 0x3f) { - sb.append("\n"); - } - } - str = sb.toString(); - } - str = str.replaceAll("\\\"", "\\\\\""); - writeKeyValue(json, "data", str, false); + if (data != null) { + writeKeyValue(json, "data", new BytesRef(data).utf8ToString(), false); } json.endObject(); } catch (KeeperException e) { diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/LookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/LookupFactory.java index cd24fa7fb4d..d6212959883 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/LookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/LookupFactory.java @@ -26,4 +26,5 @@ import org.apache.solr.core.SolrCore; */ public abstract class LookupFactory { public abstract Lookup create(NamedList params, SolrCore core); + public abstract String storeFileName(); } diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/Suggester.java b/solr/core/src/java/org/apache/solr/spelling/suggest/Suggester.java index 525ce3b97dc..5286dd9d451 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/Suggester.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/Suggester.java @@ -18,6 +18,8 @@ package org.apache.solr.spelling.suggest; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; @@ -73,6 +75,8 @@ public class Suggester extends SolrSpellChecker { protected Lookup lookup; protected String lookupImpl; protected SolrCore core; + + private LookupFactory factory; @Override public String init(NamedList config, SolrCore core) { @@ -92,7 +96,8 @@ public class Suggester extends SolrSpellChecker { lookupImpl = FSTLookupFactory.class.getName(); } - LookupFactory factory = (LookupFactory) core.getResourceLoader().newInstance(lookupImpl); + factory = (LookupFactory) core.getResourceLoader().newInstance(lookupImpl); + lookup = factory.create(config, core); String store = (String)config.get(STORE_DIR); if (store != null) { @@ -105,7 +110,7 @@ public class Suggester extends SolrSpellChecker { } else { // attempt reload of the stored lookup try { - lookup.load(storeDir); + lookup.load(new FileInputStream(new File(storeDir, factory.storeFileName()))); } catch (IOException e) { LOG.warn("Loading stored lookup data failed", e); } @@ -132,8 +137,19 @@ public class Suggester extends SolrSpellChecker { try { lookup.build(dictionary); if (storeDir != null) { - lookup.store(storeDir); + File target = new File(storeDir, factory.storeFileName()); + if(!lookup.store(new FileOutputStream(target))) { + if (sourceLocation == null) { + assert reader != null && field != null; + LOG.error("Store Lookup build from index on field: " + field + " failed reader has: " + reader.maxDoc() + " docs"); + } else { + LOG.error("Store Lookup build from sourceloaction: " + sourceLocation + " failed"); + } + } else { + LOG.info("Stored suggest data to: " + target.getAbsolutePath()); + } } + } catch (Exception e) { LOG.error("Error while building or storing Suggester data", e); } @@ -144,7 +160,7 @@ public class Suggester extends SolrSpellChecker { LOG.info("reload()"); if (dictionary == null && storeDir != null) { // this may be a firstSearcher event, try loading it - if (lookup.load(storeDir)) { + if (lookup.load(new FileInputStream(new File(storeDir, factory.storeFileName())))) { return; // loaded ok } LOG.debug("load failed, need to build Lookup again"); diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/FSTLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/FSTLookupFactory.java index ed7d86e2391..b32af8ee184 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/FSTLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/FSTLookupFactory.java @@ -18,7 +18,7 @@ package org.apache.solr.spelling.suggest.fst; */ import org.apache.lucene.search.suggest.Lookup; -import org.apache.lucene.search.suggest.fst.*; +import org.apache.lucene.search.suggest.fst.FSTCompletionLookup; import org.apache.solr.common.util.NamedList; import org.apache.solr.core.SolrCore; import org.apache.solr.spelling.suggest.LookupFactory; @@ -27,6 +27,12 @@ import org.apache.solr.spelling.suggest.LookupFactory; * Factory for {@link FSTCompletionLookup} */ public class FSTLookupFactory extends LookupFactory { + + /** + * File name for the automaton. + */ + private static final String FILENAME = "fst.bin"; + /** * The number of separate buckets for weights (discretization). The more buckets, * the more fine-grained term weights (priorities) can be assigned. The speed of lookup @@ -56,4 +62,9 @@ public class FSTLookupFactory extends LookupFactory { return new FSTCompletionLookup(buckets, exactMatchFirst); } + + @Override + public String storeFileName() { + return FILENAME; + } } diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/WFSTLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/WFSTLookupFactory.java index 233fe1e3ada..523816a5d40 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/fst/WFSTLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/fst/WFSTLookupFactory.java @@ -17,6 +17,8 @@ package org.apache.solr.spelling.suggest.fst; * limitations under the License. */ +import java.io.File; + import org.apache.lucene.search.suggest.Lookup; import org.apache.lucene.search.suggest.fst.*; import org.apache.solr.common.util.NamedList; @@ -33,6 +35,12 @@ public class WFSTLookupFactory extends LookupFactory { * of other strings in the automaton (possibly with larger weights). */ public static final String EXACT_MATCH_FIRST = "exactMatchFirst"; + + /** + * File name for the automaton. + * + */ + private static final String FILENAME = "wfst.bin"; @Override public Lookup create(NamedList params, SolrCore core) { @@ -42,4 +50,9 @@ public class WFSTLookupFactory extends LookupFactory { return new WFSTCompletionLookup(exactMatchFirst); } + + @Override + public String storeFileName() { + return FILENAME; + } } diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/jaspell/JaspellLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/jaspell/JaspellLookupFactory.java index 720d9593500..94a7f7a68b5 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/jaspell/JaspellLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/jaspell/JaspellLookupFactory.java @@ -30,10 +30,16 @@ import org.slf4j.LoggerFactory; */ public class JaspellLookupFactory extends LookupFactory { private static final Logger LOG = LoggerFactory.getLogger(JaspellLookup.class); + private static final String FILENAME = "jaspell.dat"; @Override public Lookup create(NamedList params, SolrCore core) { LOG.info("init: " + params); return new JaspellLookup(); } + + @Override + public String storeFileName() { + return FILENAME; + } } diff --git a/solr/core/src/java/org/apache/solr/spelling/suggest/tst/TSTLookupFactory.java b/solr/core/src/java/org/apache/solr/spelling/suggest/tst/TSTLookupFactory.java index 37e7e4b7e86..a5aa8ab25d8 100644 --- a/solr/core/src/java/org/apache/solr/spelling/suggest/tst/TSTLookupFactory.java +++ b/solr/core/src/java/org/apache/solr/spelling/suggest/tst/TSTLookupFactory.java @@ -27,9 +27,15 @@ import org.apache.solr.spelling.suggest.LookupFactory; * Factory for {@link TSTLookup} */ public class TSTLookupFactory extends LookupFactory { + private static final String FILENAME = "tst.dat"; @Override public Lookup create(NamedList params, SolrCore core) { return new TSTLookup(); } + + @Override + public String storeFileName() { + return FILENAME; + } } diff --git a/solr/core/src/java/org/apache/solr/update/UpdateLog.java b/solr/core/src/java/org/apache/solr/update/UpdateLog.java index e85b4bbe271..0ab9735ff41 100644 --- a/solr/core/src/java/org/apache/solr/update/UpdateLog.java +++ b/solr/core/src/java/org/apache/solr/update/UpdateLog.java @@ -49,6 +49,9 @@ import java.util.concurrent.*; /** @lucene.experimental */ public class UpdateLog implements PluginInfoInitialized { + public static String LOG_FILENAME_PATTERN = "%s.%019d"; + public static String TLOG_NAME="tlog"; + public static Logger log = LoggerFactory.getLogger(UpdateLog.class); public boolean debug = log.isDebugEnabled(); public boolean trace = log.isTraceEnabled(); @@ -76,10 +79,6 @@ public class UpdateLog implements PluginInfoInitialized { } } - - - public static String TLOG_NAME="tlog"; - long id = -1; private State state = State.ACTIVE; @@ -615,7 +614,7 @@ public class UpdateLog implements PluginInfoInitialized { private void ensureLog() { if (tlog == null) { - String newLogName = String.format(Locale.ENGLISH, "%s.%019d", TLOG_NAME, id); + String newLogName = String.format(Locale.ENGLISH, LOG_FILENAME_PATTERN, TLOG_NAME, id); try { tlog = new TransactionLog(new File(tlogDir, newLogName), globalStrings); } catch (IOException e) { diff --git a/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessor.java new file mode 100644 index 00000000000..f6e3d4f590f --- /dev/null +++ b/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessor.java @@ -0,0 +1,234 @@ +/** + * 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. + */ +package org.apache.solr.update.processor; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashSet; + +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.update.AddUpdateCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Update processor which examines a URL and outputs to various other fields + * characteristics of that URL, including length, number of path levels, whether + * it is a top level URL (levels==0), whether it looks like a landing/index page, + * a canonical representation of the URL (e.g. stripping index.html), the domain + * and path parts of the URL etc. + *

    + * This processor is intended used in connection with processing web resuources, + * and helping to produce values which may be used for boosting or filtering later. + */ +public class URLClassifyProcessor extends UpdateRequestProcessor { + + private static final String INPUT_FIELD_PARAM = "inputField"; + private static final String OUTPUT_LENGTH_FIELD_PARAM = "lengthOutputField"; + private static final String OUTPUT_LEVELS_FIELD_PARAM = "levelsOutputField"; + private static final String OUTPUT_TOPLEVEL_FIELD_PARAM = "toplevelOutputField"; + private static final String OUTPUT_LANDINGPAGE_FIELD_PARAM = "landingpageOutputField"; + private static final String OUTPUT_DOMAIN_FIELD_PARAM = "domainOutputField"; + private static final String OUTPUT_CANONICALURL_FIELD_PARAM = "canonicalUrlOutputField"; + private static final String DEFAULT_URL_FIELDNAME = "url"; + private static final String DEFAULT_LENGTH_FIELDNAME = "url_length"; + private static final String DEFAULT_LEVELS_FIELDNAME = "url_levels"; + private static final String DEFAULT_TOPLEVEL_FIELDNAME = "url_toplevel"; + private static final String DEFAULT_LANDINGPAGE_FIELDNAME = "url_landingpage"; + private final static Logger log = LoggerFactory.getLogger(URLClassifyProcessor.class); + private boolean enabled = true; + private String urlFieldname = DEFAULT_URL_FIELDNAME; + private String lengthFieldname = DEFAULT_LENGTH_FIELDNAME; + private String levelsFieldname = DEFAULT_LEVELS_FIELDNAME; + private String toplevelpageFieldname = DEFAULT_TOPLEVEL_FIELDNAME; + private String landingpageFieldname = DEFAULT_LANDINGPAGE_FIELDNAME; + private String domainFieldname = null; + private String canonicalUrlFieldname = null; + private String[] landingPageSuffixes = { + "/", + "index.html", + "index.htm", + "index.phtml", + "index.shtml", + "index.xml", + "index.php", + "index.asp", + "index.aspx", + "welcome.html", + "welcome.htm", + "welcome.phtml", + "welcome.shtml", + "welcome.xml", + "welcome.php", + "welcome.asp", + "welcome.aspx" + }; + + public URLClassifyProcessor(SolrParams parameters, + SolrQueryRequest request, + SolrQueryResponse response, + UpdateRequestProcessor nextProcessor) { + super(nextProcessor); + + HashSet landingPageSuffixesSet = new HashSet(); + for(String s : landingPageSuffixes) { + landingPageSuffixesSet.add(s); + } + this.initParameters(parameters); + } + + private void initParameters(SolrParams parameters) { + if (parameters != null) { + this.setEnabled(parameters.getBool("enabled", true)); + this.urlFieldname = parameters.get(INPUT_FIELD_PARAM, DEFAULT_URL_FIELDNAME); + this.lengthFieldname = parameters.get(OUTPUT_LENGTH_FIELD_PARAM, DEFAULT_LENGTH_FIELDNAME); + this.levelsFieldname = parameters.get(OUTPUT_LEVELS_FIELD_PARAM, DEFAULT_LEVELS_FIELDNAME); + this.toplevelpageFieldname = parameters.get(OUTPUT_TOPLEVEL_FIELD_PARAM, DEFAULT_TOPLEVEL_FIELDNAME); + this.landingpageFieldname = parameters.get(OUTPUT_LANDINGPAGE_FIELD_PARAM, DEFAULT_LANDINGPAGE_FIELDNAME); + this.domainFieldname = parameters.get(OUTPUT_DOMAIN_FIELD_PARAM); + this.canonicalUrlFieldname = parameters.get(OUTPUT_CANONICALURL_FIELD_PARAM); + } + } + + @Override + public void processAdd(AddUpdateCommand command) throws IOException { + if (isEnabled()) { + SolrInputDocument document = command.getSolrInputDocument(); + if (document.containsKey(urlFieldname)) { + String url = (String) document.getFieldValue(urlFieldname); + try { + URL normalizedURL = getNormalizedURL(url); + document.setField(lengthFieldname, length(normalizedURL)); + document.setField(levelsFieldname, levels(normalizedURL)); + document.setField(toplevelpageFieldname, isTopLevelPage(normalizedURL) ? 1 : 0); + document.setField(landingpageFieldname, isLandingPage(normalizedURL) ? 1 : 0); + if (domainFieldname != null) { + document.setField(domainFieldname, normalizedURL.getHost()); + } + if (canonicalUrlFieldname != null) { + document.setField(canonicalUrlFieldname, getCanonicalUrl(normalizedURL)); + } + log.debug(document.toString()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + } + super.processAdd(command); + } + + /** + * Gets a canonical form of the URL for use as main URL + * @param url The input url + * @return The URL object representing the canonical URL + */ + public URL getCanonicalUrl(URL url) { + // NOTE: Do we want to make sure this URL is normalized? (Christian thinks we should) + String urlString = url.toString(); + try { + String lps = landingPageSuffix(url); + return new URL(urlString.replaceFirst("/"+lps+"$", "/")); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return url; + } + + /** + * Calculates the length of the URL in characters + * @param url The input URL + * @return the length of the URL + */ + public int length(URL url) { + return url.toString().length(); + } + + /** + * Calculates the number of path levels in the given URL + * @param url The input URL + * @return the number of levels, where a top-level URL is 0 + */ + public int levels(URL url) { + // Remove any trailing slashes for the purpose of level counting + String path = getPathWithoutSuffix(url).replaceAll("/+$", ""); + int levels = 0; + for (int i = 0; i < path.length(); i++) { + if (path.charAt(i) == '/') { + levels++; + } + } + return levels; + } + + /** + * Calculates whether a URL is a top level page + * @param url The input URL + * @return true if page is a top level page + */ + public boolean isTopLevelPage(URL url) { + // Remove any trailing slashes for the purpose of level counting + String path = getPathWithoutSuffix(url).replaceAll("/+$", ""); + return path.length() == 0 && url.getQuery() == null; + } + + /** + * Calculates whether the URL is a landing page or not + * @param url The input URL + * @return true if URL represents a landing page (index page) + */ + public boolean isLandingPage(URL url) { + if (url.getQuery() != null) { + return false; + } else { + return landingPageSuffix(url) != ""; + } + } + + public URL getNormalizedURL(String url) throws MalformedURLException, URISyntaxException { + return new URI(url).normalize().toURL(); + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + private String landingPageSuffix(URL url) { + String path = url.getPath().toLowerCase(); + for(String suffix : landingPageSuffixes) { + if(path.endsWith(suffix)) { + return suffix; + } + } + return ""; + } + + private String getPathWithoutSuffix(URL url) { + return url.getPath().toLowerCase().replaceFirst(landingPageSuffix(url)+"$", ""); + } +} diff --git a/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessorFactory.java b/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessorFactory.java new file mode 100644 index 00000000000..79eb8e79b2f --- /dev/null +++ b/solr/core/src/java/org/apache/solr/update/processor/URLClassifyProcessorFactory.java @@ -0,0 +1,44 @@ +/** + * 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. + */ +package org.apache.solr.update.processor; + +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * Creates URLClassifyProcessor + */ +public class URLClassifyProcessorFactory extends UpdateRequestProcessorFactory { + + private SolrParams params; + + @Override + public void init(@SuppressWarnings("rawtypes") final NamedList args) { + if (args != null) { + this.params = SolrParams.toSolrParams(args); + } + } + + @Override + public UpdateRequestProcessor getInstance(SolrQueryRequest request, + SolrQueryResponse response, + UpdateRequestProcessor nextProcessor) { + return new URLClassifyProcessor(params, request, response, nextProcessor); + } +} diff --git a/solr/core/src/java/overview.html b/solr/core/src/java/overview.html index d475f5300a5..151b3246572 100644 --- a/solr/core/src/java/overview.html +++ b/solr/core/src/java/overview.html @@ -1,5 +1,5 @@ -Apache Solr Search Server +Apache Solr Search Server, new users should familiarize themselves with the Solr Tutorial. diff --git a/solr/site-src/src/documentation/content/xdocs/issue_tracking.xml b/solr/core/src/test-files/solr/conf/currency.xml old mode 100755 new mode 100644 similarity index 58% rename from solr/site-src/src/documentation/content/xdocs/issue_tracking.xml rename to solr/core/src/test-files/solr/conf/currency.xml index 126381895b0..f74f6e96399 --- a/solr/site-src/src/documentation/content/xdocs/issue_tracking.xml +++ b/solr/core/src/test-files/solr/conf/currency.xml @@ -1,5 +1,4 @@ - - + - - -

    - Solr Issue Tracking -
    - - -

    - Solr issues (bugs, as well as enhancement requests) are tracked in - JIRA here. - If you aren't sure whether something is a bug, post a question on the - Solr user mailing list. -

    - - - + + + + + + + + + + + + + + + + + diff --git a/solr/core/src/test-files/solr/conf/schema.xml b/solr/core/src/test-files/solr/conf/schema.xml index 7cb97e59d9e..c6b286768c5 100644 --- a/solr/core/src/test-files/solr/conf/schema.xml +++ b/solr/core/src/test-files/solr/conf/schema.xml @@ -394,6 +394,10 @@ + + + + @@ -467,6 +471,9 @@ + + + diff --git a/solr/core/src/test-files/solr/conf/schema15.xml b/solr/core/src/test-files/solr/conf/schema15.xml new file mode 100755 index 00000000000..48c2c9ec9a2 --- /dev/null +++ b/solr/core/src/test-files/solr/conf/schema15.xml @@ -0,0 +1,591 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text + id + + + + + + + + diff --git a/solr/core/src/test/org/apache/solr/ConvertedLegacyTest.java b/solr/core/src/test/org/apache/solr/ConvertedLegacyTest.java index 6ab552f49a6..2ae4df02891 100644 --- a/solr/core/src/test/org/apache/solr/ConvertedLegacyTest.java +++ b/solr/core/src/test/org/apache/solr/ConvertedLegacyTest.java @@ -1127,7 +1127,7 @@ public class ConvertedLegacyTest extends SolrTestCaseJ4 { // test addition of score field args = new HashMap(); - args.put("fl","score "); + args.put("fl","score,* "); req = new LocalSolrQueryRequest(h.getCore(), "id:44", "standard", 0, 10, args); assertQ(req @@ -1190,7 +1190,7 @@ public class ConvertedLegacyTest extends SolrTestCaseJ4 { ,"//@maxScore = //doc/float[@name='score']" ); args = new HashMap(); - args.put("fl","score"); + args.put("fl","*,score"); args.put("defType","lucenePlusSort"); req = new LocalSolrQueryRequest(h.getCore(), "id:44;id desc;", "standard", 0, 0 , args); diff --git a/solr/core/src/test/org/apache/solr/TestDistributedSearch.java b/solr/core/src/test/org/apache/solr/TestDistributedSearch.java index 3ea5ed6c0d7..4a88a35e7bb 100755 --- a/solr/core/src/test/org/apache/solr/TestDistributedSearch.java +++ b/solr/core/src/test/org/apache/solr/TestDistributedSearch.java @@ -128,7 +128,7 @@ public class TestDistributedSearch extends BaseDistributedSearchTestCase { query("q","*:*", "sort","{!func}testfunc(add("+i1+",5))"+" desc"); query("q","*:*", "sort",i1+" asc"); query("q","*:*", "sort",i1+" desc", "fl","*,score"); - query("q","*:*", "sort","n_tl1 asc", "fl","score"); // test legacy behavior - "score"=="*,score" + query("q","*:*", "sort","n_tl1 asc", "fl","*,score"); query("q","*:*", "sort","n_tl1 desc"); handle.put("maxScore", SKIPVAL); query("q","{!func}"+i1);// does not expect maxScore. So if it comes ,ignore it. JavaBinCodec.writeSolrDocumentList() diff --git a/solr/core/src/test/org/apache/solr/cloud/AbstractZkTestCase.java b/solr/core/src/test/org/apache/solr/cloud/AbstractZkTestCase.java index 3db5d1cfe90..9c99f96f35d 100644 --- a/solr/core/src/test/org/apache/solr/cloud/AbstractZkTestCase.java +++ b/solr/core/src/test/org/apache/solr/cloud/AbstractZkTestCase.java @@ -90,6 +90,7 @@ public abstract class AbstractZkTestCase extends SolrTestCaseJ4 { putConfig(zkClient, "solrconfig.xml"); putConfig(zkClient, "stopwords.txt"); putConfig(zkClient, "protwords.txt"); + putConfig(zkClient, "currency.xml"); putConfig(zkClient, "mapping-ISOLatin1Accent.txt"); putConfig(zkClient, "old_synonyms.txt"); putConfig(zkClient, "synonyms.txt"); diff --git a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java index 4a5609ee633..5a2880d81dd 100644 --- a/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java +++ b/solr/core/src/test/org/apache/solr/cloud/BasicDistributedZkTest.java @@ -166,7 +166,7 @@ public class BasicDistributedZkTest extends AbstractDistributedZkTestCase { query("q","*:*", "sort",i1+" desc"); query("q","*:*", "sort",i1+" asc"); query("q","*:*", "sort",i1+" desc", "fl","*,score"); - query("q","*:*", "sort","n_tl1 asc", "fl","score"); // test legacy behavior - "score"=="*,score" + query("q","*:*", "sort","n_tl1 asc", "fl","*,score"); query("q","*:*", "sort","n_tl1 desc"); handle.put("maxScore", SKIPVAL); query("q","{!func}"+i1);// does not expect maxScore. So if it comes ,ignore it. JavaBinCodec.writeSolrDocumentList() diff --git a/solr/core/src/test/org/apache/solr/cloud/ChaosMonkey.java b/solr/core/src/test/org/apache/solr/cloud/ChaosMonkey.java index 34f54679fc9..2a90fd441ee 100644 --- a/solr/core/src/test/org/apache/solr/cloud/ChaosMonkey.java +++ b/solr/core/src/test/org/apache/solr/cloud/ChaosMonkey.java @@ -34,7 +34,7 @@ import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.core.CoreContainer; import org.apache.solr.servlet.SolrDispatchFilter; import org.apache.zookeeper.KeeperException; -import org.mortbay.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.FilterHolder; /** * The monkey can stop random or specific jetties used with SolrCloud. diff --git a/solr/core/src/test/org/apache/solr/handler/component/StatsComponentTest.java b/solr/core/src/test/org/apache/solr/handler/component/StatsComponentTest.java index 9805dd8309e..c52634f95ec 100644 --- a/solr/core/src/test/org/apache/solr/handler/component/StatsComponentTest.java +++ b/solr/core/src/test/org/apache/solr/handler/component/StatsComponentTest.java @@ -196,9 +196,10 @@ public class StatsComponentTest extends AbstractSolrTestCase { "//long[@name='count'][.='2']", "//long[@name='missing'][.='1']", "//date[@name='min'][.='1970-01-02T10:17:36Z']", - "//date[@name='max'][.='1970-01-12T10:20:54Z']", - "//date[@name='sum'][.='1970-01-13T20:38:30Z']", - "//date[@name='mean'][.='1970-01-07T10:19:15Z']"); + "//date[@name='max'][.='1970-01-12T10:20:54Z']" + // "//date[@name='sum'][.='1970-01-13T20:38:30Z']", // sometimes 29.999Z + // "//date[@name='mean'][.='1970-01-07T10:19:15Z']" // sometiems 14.999Z + ); } diff --git a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java index bc2747c5542..6e7361fb9fe 100644 --- a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java +++ b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java @@ -19,6 +19,7 @@ package org.apache.solr.request; import org.apache.noggit.ObjectBuilder; import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.schema.SchemaField; import org.junit.BeforeClass; @@ -66,8 +67,9 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 { indexFacetSingleValued(); indexFacetPrefixMultiValued(); indexFacetPrefixSingleValued(); - - Collections.shuffle(pendingDocs, random); + indexSimpleGroupedFacetCounts(); + + Collections.shuffle(pendingDocs, random); for (String[] doc : pendingDocs) { assertU(adoc(doc)); randomCommit(random_commit_percent); @@ -102,6 +104,88 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 { "zerolen_s",""); } + static void indexSimpleGroupedFacetCounts() { + add_doc("id", "2000", "hotel_s1", "a", "airport_s1", "ams", "duration_i1", "5"); + add_doc("id", "2001", "hotel_s1", "a", "airport_s1", "dus", "duration_i1", "10"); + add_doc("id", "2002", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "10"); + add_doc("id", "2003", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "5"); + add_doc("id", "2004", "hotel_s1", "b", "airport_s1", "ams", "duration_i1", "5"); + } + + @Test + public void testSimpleGroupedFacets() throws Exception { + assertQ( + "Return 5 docs with id range 1937 till 1940", + req("id:[2000 TO 2004]"), + "*[count(//doc)=5]" + ); + assertQ( + "Return two facet counts for field airport_a", + req( + "q", "*:*", + "fq", "id:[2000 TO 2004]", + "group", "true", + "group.facet", "true", + "group.field", "hotel_s1", + "facet", "true", + "facet.field", "airport_s1" + ), + "//lst[@name='facet_fields']/lst[@name='airport_s1']", + "*[count(//lst[@name='airport_s1']/int)=2]", + "//lst[@name='airport_s1']/int[@name='ams'][.='2']", + "//lst[@name='airport_s1']/int[@name='dus'][.='1']" + ); + assertQ( + "Return two facet counts for field airport_a with fq", + req( + "q", "*:*", + "fq", "id:[2000 TO 2004]", + "fq", "duration_i1:5", + "group", "true", + "group.facet", "true", + "group.field", "hotel_s1", + "facet", "true", + "facet.field", "airport_s1" + ), + "//lst[@name='facet_fields']/lst[@name='airport_s1']", + "*[count(//lst[@name='airport_s1']/int)=2]", + "//lst[@name='airport_s1']/int[@name='ams'][.='2']", + "//lst[@name='airport_s1']/int[@name='dus'][.='0']" + ); + assertQ( + "Return one facet count for field airport_s1 with prefix a", + req( + "q", "*:*", + "fq", "id:[2000 TO 2004]", + "group", "true", + "group.facet", "true", + "group.field", "hotel_s1", + "facet", "true", + "facet.field", "airport_s1", + "facet.prefix", "a" + ), + "//lst[@name='facet_fields']/lst[@name='airport_s1']", + "*[count(//lst[@name='airport_s1']/int)=1]", + "//lst[@name='airport_s1']/int[@name='ams'][.='2']" + ); + + try { + h.query( + req( + "q", "*:*", + "fq", "id:[2000 TO 2004]", + "group.facet", "true", + "facet", "true", + "facet.field", "airport_s1", + "facet.prefix", "a" + ) + ); + fail("Exception should have been thrown"); + } catch (SolrException e) { + assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, e.code()); + } + } + @Test public void testEmptyFacetCounts() throws Exception { doEmptyFacetCounts(); diff --git a/solr/core/src/test/org/apache/solr/schema/CurrencyFieldTest.java b/solr/core/src/test/org/apache/solr/schema/CurrencyFieldTest.java new file mode 100644 index 00000000000..bfd190e59dc --- /dev/null +++ b/solr/core/src/test/org/apache/solr/schema/CurrencyFieldTest.java @@ -0,0 +1,211 @@ +package org.apache.solr.schema; +/** + * 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 org.apache.lucene.index.IndexableField; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.core.SolrCore; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Random; + +/** + * Tests currency field type. + */ +public class CurrencyFieldTest extends SolrTestCaseJ4 { + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml", "schema.xml"); + } + + @Test + public void testCurrencySchema() throws Exception { + IndexSchema schema = h.getCore().getSchema(); + + SchemaField amount = schema.getField("amount"); + assertNotNull(amount); + assertTrue(amount.isPolyField()); + + SchemaField[] dynFields = schema.getDynamicFieldPrototypes(); + boolean seenCurrency = false; + boolean seenAmount = false; + + for (SchemaField dynField : dynFields) { + if (dynField.getName().equals("*" + FieldType.POLY_FIELD_SEPARATOR + CurrencyField.FIELD_SUFFIX_CURRENCY)) { + seenCurrency = true; + } + + if (dynField.getName().equals("*" + FieldType.POLY_FIELD_SEPARATOR + CurrencyField.FIELD_SUFFIX_AMOUNT_RAW)) { + seenAmount = true; + } + } + + assertTrue("Didn't find the expected currency code dynamic field", seenCurrency); + assertTrue("Didn't find the expected value dynamic field", seenAmount); + } + + @Test + public void testCurrencyFieldType() throws Exception { + SolrCore core = h.getCore(); + IndexSchema schema = core.getSchema(); + SchemaField amount = schema.getField("amount"); + assertNotNull(amount); + assertTrue("amount is not a poly field", amount.isPolyField()); + FieldType tmp = amount.getType(); + assertTrue(tmp instanceof CurrencyField); + String currencyValue = "1.50,EUR"; + IndexableField[] fields = amount.createFields(currencyValue, 2); + assertEquals(fields.length, 3); + + // First field is currency code, second is value, third is stored. + for (int i = 0; i < 3; i++) { + boolean hasValue = fields[i].readerValue() != null + || fields[i].numericValue() != null + || fields[i].stringValue() != null; + assertTrue("Doesn't have a value: " + fields[i], hasValue); + } + + assertEquals(schema.getFieldTypeByName("string").toExternal(fields[2]), "1.50,EUR"); + + // A few tests on the provider directly + ExchangeRateProvider p = ((CurrencyField) tmp).getProvider(); + String[] available = p.listAvailableCurrencies(); + assert(available.length == 5); + assert(p.reload() == true); + assert(p.getExchangeRate("USD", "EUR") == 2.5); + } + + @Test + public void testCurrencyRangeSearch() throws Exception { + for (int i = 1; i <= 10; i++) { + assertU(adoc("id", "" + i, "amount", i + ",USD")); + } + + assertU(commit()); + + assertQ(req("fl", "*,score", "q", + "amount:[2.00,USD TO 5.00,USD]"), + "//*[@numFound='4']"); + + assertQ(req("fl", "*,score", "q", + "amount:[0.50,USD TO 1.00,USD]"), + "//*[@numFound='1']"); + + assertQ(req("fl", "*,score", "q", + "amount:[24.00,USD TO 25.00,USD]"), + "//*[@numFound='0']"); + + // "GBP" currency code is 1/2 of a USD dollar, for testing. + assertQ(req("fl", "*,score", "q", + "amount:[0.50,GBP TO 1.00,GBP]"), + "//*[@numFound='2']"); + + // "EUR" currency code is 2.5X of a USD dollar, for testing. + assertQ(req("fl", "*,score", "q", + "amount:[24.00,EUR TO 25.00,EUR]"), + "//*[@numFound='1']"); + + // Slight asymmetric rate should work. + assertQ(req("fl", "*,score", "q", + "amount:[24.99,EUR TO 25.01,EUR]"), + "//*[@numFound='1']"); + } + + @Test + public void testCurrencyPointQuery() throws Exception { + assertU(adoc("id", "" + 1, "amount", "10.00,USD")); + assertU(adoc("id", "" + 2, "amount", "15.00,EUR")); + assertU(commit()); + assertQ(req("fl", "*,score", "q", "amount:10.00,USD"), "//int[@name='id']='1'"); + assertQ(req("fl", "*,score", "q", "amount:9.99,USD"), "//*[@numFound='0']"); + assertQ(req("fl", "*,score", "q", "amount:10.01,USD"), "//*[@numFound='0']"); + assertQ(req("fl", "*,score", "q", "amount:15.00,EUR"), "//int[@name='id']='2'"); + assertQ(req("fl", "*,score", "q", "amount:7.50,USD"), "//int[@name='id']='2'"); + assertQ(req("fl", "*,score", "q", "amount:7.49,USD"), "//*[@numFound='0']"); + assertQ(req("fl", "*,score", "q", "amount:7.51,USD"), "//*[@numFound='0']"); + } + + @Ignore + public void testPerformance() throws Exception { + Random r = new Random(); + int initDocs = 200000; + + for (int i = 1; i <= initDocs; i++) { + assertU(adoc("id", "" + i, "amount", (r.nextInt(10) + 1.00) + ",USD")); + if (i % 1000 == 0) + System.out.println(i); + } + + assertU(commit()); + for (int i = 0; i < 1000; i++) { + double lower = r.nextInt(10) + 1.00; + assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",USD TO " + (lower + 10.00) + ",USD]"), "//*"); + assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",EUR TO " + (lower + 10.00) + ",EUR]"), "//*"); + } + + for (int j = 0; j < 3; j++) { + long t1 = System.currentTimeMillis(); + for (int i = 0; i < 1000; i++) { + double lower = r.nextInt(10) + 1.00; + assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",USD TO " + (lower + (9.99 - (j * 0.01))) + ",USD]"), "//*"); + } + + System.out.println(System.currentTimeMillis() - t1); + } + + System.out.println("---"); + + for (int j = 0; j < 3; j++) { + long t1 = System.currentTimeMillis(); + for (int i = 0; i < 1000; i++) { + double lower = r.nextInt(10) + 1.00; + assertQ(req("fl", "*,score", "q", "amount:[" + lower + ",EUR TO " + (lower + (9.99 - (j * 0.01))) + ",EUR]"), "//*"); + } + + System.out.println(System.currentTimeMillis() - t1); + } + } + + @Test + public void testCurrencySort() throws Exception { + assertU(adoc("id", "" + 1, "amount", "10.00,USD")); + assertU(adoc("id", "" + 2, "amount", "15.00,EUR")); + assertU(adoc("id", "" + 3, "amount", "7.00,EUR")); + assertU(adoc("id", "" + 4, "amount", "6.00,GBP")); + assertU(adoc("id", "" + 5, "amount", "2.00,GBP")); + assertU(commit()); + + assertQ(req("fl", "*,score", "q", "*:*", "sort", "amount desc", "limit", "1"), "//int[@name='id']='4'"); + assertQ(req("fl", "*,score", "q", "*:*", "sort", "amount asc", "limit", "1"), "//int[@name='id']='3'"); + } + + @Test + public void testMockExchangeRateProvider() throws Exception { + assertU(adoc("id", "1", "mock_amount", "1.00,USD")); + assertU(adoc("id", "2", "mock_amount", "1.00,EUR")); + assertU(adoc("id", "3", "mock_amount", "1.00,NOK")); + assertU(commit()); + + assertQ(req("fl", "*,score", "q", "mock_amount:5.0,NOK"), "//*[@numFound='1']", "//int[@name='id']='1'"); + assertQ(req("fl", "*,score", "q", "mock_amount:1.2,USD"), "//*[@numFound='1']", "//int[@name='id']='2'"); + assertQ(req("fl", "*,score", "q", "mock_amount:0.2,USD"), "//*[@numFound='1']", "//int[@name='id']='3'"); + assertQ(req("fl", "*,score", "q", "mock_amount:99,USD"), "//*[@numFound='0']"); + } +} diff --git a/solr/core/src/test/org/apache/solr/schema/MockExchangeRateProvider.java b/solr/core/src/test/org/apache/solr/schema/MockExchangeRateProvider.java new file mode 100644 index 00000000000..8fa50815c94 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/schema/MockExchangeRateProvider.java @@ -0,0 +1,81 @@ +package org.apache.solr.schema; +/** + * 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.util.HashMap; +import java.util.Map; + +import org.apache.solr.common.ResourceLoader; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; + +/** + * Simple mock provider with fixed rates and some assertions + */ +public class MockExchangeRateProvider implements ExchangeRateProvider { + private static Map map = new HashMap(); + static { + map.put("USD,EUR", 0.8); + map.put("EUR,USD", 1.2); + map.put("USD,NOK", 5.0); + map.put("NOK,USD", 0.2); + map.put("EUR,NOK", 10.0); + map.put("NOK,EUR", 0.1); + } + + private boolean gotArgs = false; + private boolean gotLoader = false; + + @Override + public double getExchangeRate(String sourceCurrencyCode, String targetCurrencyCode) { +// System.out.println("***** getExchangeRate("+sourceCurrencyCode+targetCurrencyCode+")"); + if(sourceCurrencyCode.equals(targetCurrencyCode)) return 1.0; + + Double result = map.get(sourceCurrencyCode+","+targetCurrencyCode); + if(result == null) { + throw new SolrException(ErrorCode.NOT_FOUND, "No exchange rate found for the pair "+sourceCurrencyCode+","+targetCurrencyCode); + } + return result; + } + + @Override + public String[] listAvailableCurrencies() { + return map.keySet().toArray(new String[1]); + } + + @Override + public boolean reload() throws SolrException { + assert(gotArgs == true); + assert(gotLoader == true); + return true; + } + + @Override + public void init(Map args) { + assert(args.get("foo").equals("bar")); + gotArgs = true; + args.remove("foo"); + } + + @Override + public void inform(ResourceLoader loader) throws SolrException { + assert(loader != null); + gotLoader = true; + assert(gotArgs == true); + } + +} diff --git a/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java b/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java new file mode 100644 index 00000000000..16136d8684a --- /dev/null +++ b/solr/core/src/test/org/apache/solr/schema/PrimitiveFieldTypeTest.java @@ -0,0 +1,191 @@ +/** + * 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. + */ + +package org.apache.solr.schema; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.core.SolrConfig; +import org.junit.Test; + +import java.io.File; +import java.util.HashMap; +import java.util.TimeZone; + +/** + * Tests that defaults are set for Primitive (non-analyzed) fields + */ +public class PrimitiveFieldTypeTest extends SolrTestCaseJ4 { + private final String testConfHome = TEST_HOME() + File.separator + "conf"+ File.separator; + public static TimeZone UTC = TimeZone.getTimeZone("UTC"); + protected SolrConfig config; + protected IndexSchema schema; + protected HashMap initMap; + + @Override + public void setUp() throws Exception { + super.setUp(); + // set some system properties for use by tests + System.setProperty("solr.test.sys.prop1", "propone"); + System.setProperty("solr.test.sys.prop2", "proptwo"); + + initMap = new HashMap(); + config = new SolrConfig(testConfHome + "solrconfig.xml"); + } + + @SuppressWarnings("deprecation") + @Test + public void testDefaultOmitNorms() throws Exception { + BinaryField bin; + TextField t; + DateField dt; + StrField s; + IntField i; + TrieIntField ti; + SortableIntField si; + LongField l; + ShortField sf; + FloatField f; + DoubleField d; + BoolField b; + ByteField bf; + + + // *********************** + // With schema version 1.4: + // *********************** + schema = new IndexSchema(config, testConfHome + "schema12.xml", null); + + dt = new DateField(); + dt.init(schema, initMap); + assertFalse(dt.hasProperty(FieldType.OMIT_NORMS)); + + s = new StrField(); + s.init(schema, initMap); + assertFalse(s.hasProperty(FieldType.OMIT_NORMS)); + + i = new IntField(); + i.init(schema, initMap); + assertFalse(i.hasProperty(FieldType.OMIT_NORMS)); + + ti = new TrieIntField(); + ti.init(schema, initMap); + assertFalse(ti.hasProperty(FieldType.OMIT_NORMS)); + + si = new SortableIntField(); + si.init(schema, initMap); + assertFalse(si.hasProperty(FieldType.OMIT_NORMS)); + + l = new LongField(); + l.init(schema, initMap); + assertFalse(l.hasProperty(FieldType.OMIT_NORMS)); + + sf = new ShortField(); + sf.init(schema, initMap); + assertFalse(sf.hasProperty(FieldType.OMIT_NORMS)); + + f = new FloatField(); + f.init(schema, initMap); + assertFalse(f.hasProperty(FieldType.OMIT_NORMS)); + + d = new DoubleField(); + d.init(schema, initMap); + assertFalse(d.hasProperty(FieldType.OMIT_NORMS)); + + dt = new DateField(); + dt.init(schema, initMap); + assertFalse(dt.hasProperty(FieldType.OMIT_NORMS)); + + b = new BoolField(); + b.init(schema, initMap); + assertFalse(b.hasProperty(FieldType.OMIT_NORMS)); + + bf = new ByteField(); + bf.init(schema, initMap); + assertFalse(bf.hasProperty(FieldType.OMIT_NORMS)); + + // Non-primitive fields + t = new TextField(); + t.init(schema, initMap); + assertFalse(t.hasProperty(FieldType.OMIT_NORMS)); + + bin = new BinaryField(); + bin.init(schema, initMap); + assertFalse(bin.hasProperty(FieldType.OMIT_NORMS)); + + // *********************** + // With schema version 1.5 + // *********************** + schema = new IndexSchema(config, testConfHome + "schema15.xml", null); + + dt = new DateField(); + dt.init(schema, initMap); + assertTrue(dt.hasProperty(FieldType.OMIT_NORMS)); + + s = new StrField(); + s.init(schema, initMap); + assertTrue(s.hasProperty(FieldType.OMIT_NORMS)); + + i = new IntField(); + i.init(schema, initMap); + assertTrue(i.hasProperty(FieldType.OMIT_NORMS)); + + ti = new TrieIntField(); + ti.init(schema, initMap); + assertTrue(ti.hasProperty(FieldType.OMIT_NORMS)); + + si = new SortableIntField(); + si.init(schema, initMap); + assertTrue(si.hasProperty(FieldType.OMIT_NORMS)); + + l = new LongField(); + l.init(schema, initMap); + assertTrue(l.hasProperty(FieldType.OMIT_NORMS)); + + sf = new ShortField(); + sf.init(schema, initMap); + assertTrue(sf.hasProperty(FieldType.OMIT_NORMS)); + + f = new FloatField(); + f.init(schema, initMap); + assertTrue(f.hasProperty(FieldType.OMIT_NORMS)); + + d = new DoubleField(); + d.init(schema, initMap); + assertTrue(d.hasProperty(FieldType.OMIT_NORMS)); + + dt = new DateField(); + dt.init(schema, initMap); + assertTrue(dt.hasProperty(FieldType.OMIT_NORMS)); + + b = new BoolField(); + b.init(schema, initMap); + assertTrue(b.hasProperty(FieldType.OMIT_NORMS)); + + bf = new ByteField(); + bf.init(schema, initMap); + assertTrue(bf.hasProperty(FieldType.OMIT_NORMS)); + + // Non-primitive fields + t = new TextField(); + t.init(schema, initMap); + assertFalse(t.hasProperty(FieldType.OMIT_NORMS)); + + bin = new BinaryField(); + bin.init(schema, initMap); + assertFalse(bin.hasProperty(FieldType.OMIT_NORMS)); + } +} diff --git a/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java b/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java index 9cf0f03449c..b49935e190f 100755 --- a/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java +++ b/solr/core/src/test/org/apache/solr/search/TestExtendedDismaxParser.java @@ -17,6 +17,9 @@ package org.apache.solr.search; +import java.io.IOException; + +import org.apache.solr.common.SolrException; import org.apache.solr.util.AbstractSolrTestCase; public class TestExtendedDismaxParser extends AbstractSolrTestCase { @@ -31,16 +34,6 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { // if you override setUp or tearDown, you better call // the super classes version super.setUp(); - } - @Override - public void tearDown() throws Exception { - // if you override setUp or tearDown, you better call - // the super classes version - super.tearDown(); - } - - // test the edismax query parser based on the dismax parser - public void testFocusQueryParser() { assertU(adoc("id", "42", "trait_ss", "Tool", "trait_ss", "Obnoxious", "name", "Zapp Brannigan")); assertU(adoc("id", "43" , @@ -63,6 +56,16 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { assertU(adoc("id", "50", "text_sw", "start new big city end")); assertU(commit()); + } + @Override + public void tearDown() throws Exception { + // if you override setUp or tearDown, you better call + // the super classes version + super.tearDown(); + } + + // test the edismax query parser based on the dismax parser + public void testFocusQueryParser() { String allq = "id:[42 TO 50]"; String allr = "*[count(//doc)=9]"; String oner = "*[count(//doc)=1]"; @@ -249,4 +252,155 @@ public class TestExtendedDismaxParser extends AbstractSolrTestCase { } + public void testUserFields() { + String oner = "*[count(//doc)=1]"; + String nor = "*[count(//doc)=0]"; + + // User fields + // Default is allow all "*" + // If a list of fields are given, only those are allowed "foo bar" + // Possible to invert with "-" syntax: + // Disallow all: "-*" + // Allow all but id: "* -id" + // Also supports "dynamic" field name wildcarding + assertQ(req("defType","edismax", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","*", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","id", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","-*", "q","id:42"), + nor); + + assertQ(req("defType","edismax", "uf","loremipsum", "q","id:42"), + nor); + + assertQ(req("defType","edismax", "uf","* -id", "q","id:42"), + nor); + + assertQ(req("defType","edismax", "uf","* -loremipsum", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","id^5.0", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","*^5.0", "q","id:42"), + oner); + + assertQ(req("defType","edismax", "uf","id^5.0", "q","id:42^10.0"), + oner); + + assertQ(req("defType","edismax", "uf","na*", "q","name:Zapp"), + oner); + + assertQ(req("defType","edismax", "uf","*me", "q","name:Zapp"), + oner); + + assertQ(req("defType","edismax", "uf","* -na*", "q","name:Zapp"), + nor); + + assertQ(req("defType","edismax", "uf","*me -name", "q","name:Zapp"), + nor); + + assertQ(req("defType","edismax", "uf","*ame -*e", "q","name:Zapp"), + nor); + + // Boosts from user fields + assertQ(req("defType","edismax", "debugQuery","true", "rows","0", "q","id:42"), + "//str[@name='parsedquery_toString'][.='+id:42']"); + + assertQ(req("defType","edismax", "debugQuery","true", "rows","0", "uf","*^5.0", "q","id:42"), + "//str[@name='parsedquery_toString'][.='+id:42^5.0']"); + + assertQ(req("defType","edismax", "debugQuery","true", "rows","0", "uf","*^2.0 id^5.0 -xyz", "q","name:foo"), + "//str[@name='parsedquery_toString'][.='+name:foo^2.0']"); + + assertQ(req("defType","edismax", "debugQuery","true", "rows","0", "uf","i*^5.0", "q","id:42"), + "//str[@name='parsedquery_toString'][.='+id:42^5.0']"); + + + assertQ(req("defType","edismax", "uf","-*", "q","cannons"), + oner); + + assertQ(req("defType","edismax", "uf","* -id", "q","42", "qf", "id"), oner); + + } + + public void testAliasing() throws IOException, Exception { + String oner = "*[count(//doc)=1]"; + String twor = "*[count(//doc)=2]"; + String nor = "*[count(//doc)=0]"; + + // Aliasing + // Single field + assertQ(req("defType","edismax", "q","myalias:Zapp"), + nor); + + assertQ(req("defType","edismax", "q","myalias:Zapp", "f.myalias.qf","name"), + oner); + + // Multi field + assertQ(req("defType","edismax", "uf", "myalias", "q","myalias:(Zapp Obnoxious)", "f.myalias.qf","name^2.0 mytrait_ss^5.0", "mm", "50%"), + oner); + + // Multi field + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "f.myalias.qf","name^2.0 mytrait_ss^5.0"), + nor); + + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "qf","myalias^10.0", "f.myalias.qf","name^2.0 mytrait_ss^5.0"), oner); + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "qf","myalias^10.0", "f.myalias.qf","name^2.0 trait_ss^5.0"), twor); + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "qf","myalias^10.0", "f.myalias.qf","name^2.0 trait_ss^5.0", "mm", "100%"), oner); + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "qf","who^10.0 where^3.0", "f.who.qf","name^2.0", "f.where.qf", "mytrait_ss^5.0"), oner); + + assertQ(req("defType","edismax", "q","Zapp Obnoxious", "qf","myalias", "f.myalias.qf","name mytrait_ss", "uf", "myalias"), oner); + + assertQ(req("defType","edismax", "uf","who", "q","who:(Zapp Obnoxious)", "f.who.qf", "name^2.0 trait_ss^5.0", "qf", "id"), twor); + assertQ(req("defType","edismax", "uf","* -name", "q","who:(Zapp Obnoxious)", "f.who.qf", "name^2.0 trait_ss^5.0"), twor); + + } + + public void testAliasingBoost() throws IOException, Exception { + assertQ(req("defType","edismax", "q","Zapp Pig", "qf","myalias", "f.myalias.qf","name trait_ss^0.5"), "//result/doc[1]/str[@name='id']=42", "//result/doc[2]/str[@name='id']=47");//doc 42 should score higher than 46 + assertQ(req("defType","edismax", "q","Zapp Pig", "qf","myalias^100 name", "f.myalias.qf","trait_ss^0.5"), "//result/doc[1]/str[@name='id']=47", "//result/doc[2]/str[@name='id']=42");//Now the order should be inverse + } + + public void testCyclicAliasing() throws IOException, Exception { + try { + h.query(req("defType","edismax", "q","Zapp Pig", "qf","who", "f.who.qf","name","f.name.qf","who")); + fail("Simple cyclic alising"); + } catch (SolrException e) { + assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle")); + } + + try { + h.query(req("defType","edismax", "q","Zapp Pig", "qf","who", "f.who.qf","name","f.name.qf","myalias", "f.myalias.qf","who")); + fail(); + } catch (SolrException e) { + assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle")); + } + + try { + h.query(req("defType","edismax", "q","Zapp Pig", "qf","field1", "f.field1.qf","field2 field3","f.field2.qf","field4 field5", "f.field4.qf","field5", "f.field5.qf","field6", "f.field3.qf","field6")); + } catch (SolrException e) { + fail("This is not cyclic alising"); + } + + try { + h.query(req("defType","edismax", "q","Zapp Pig", "qf","field1", "f.field1.qf","field2 field3", "f.field2.qf","field4 field5", "f.field4.qf","field5", "f.field5.qf","field4")); + fail(); + } catch (SolrException e) { + assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle")); + } + + try { + h.query(req("defType","edismax", "q","who:(Zapp Pig)", "qf","field1", "f.who.qf","name","f.name.qf","myalias", "f.myalias.qf","who")); + fail(); + } catch (SolrException e) { + assertTrue(e.getCause().getMessage().contains("Field aliases lead to a cycle")); + } + } + } diff --git a/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java b/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java index ce069b25389..7e458852190 100644 --- a/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java +++ b/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java @@ -42,7 +42,7 @@ public class TestPseudoReturnFields extends SolrTestCaseJ4 { * values of the fl param that mean all real fields and score */ private static String[] SCORE_AND_REAL_FIELDS = new String[] { - "score", "score,*", "*,score" + "score,*", "*,score" }; @BeforeClass diff --git a/solr/core/src/test/org/apache/solr/search/TestRecovery.java b/solr/core/src/test/org/apache/solr/search/TestRecovery.java index e253507052b..8e09d86b79f 100644 --- a/solr/core/src/test/org/apache/solr/search/TestRecovery.java +++ b/solr/core/src/test/org/apache/solr/search/TestRecovery.java @@ -785,7 +785,11 @@ public class TestRecovery extends SolrTestCaseJ4 { findReplace("CCCCCC".getBytes("UTF-8"), "cccccc".getBytes("UTF-8"), content); // WARNING... assumes format of .00000n where n is less than 9 - String fname2 = fname.substring(0, fname.length()-1) + (char)(fname.charAt(fname.length()-1)+1); + long logNumber = Long.parseLong(fname.substring(fname.lastIndexOf(".") + 1)); + String fname2 = String.format(Locale.ENGLISH, + UpdateLog.LOG_FILENAME_PATTERN, + UpdateLog.TLOG_NAME, + logNumber + 1); raf = new RandomAccessFile(new File(logDir, fname2), "rw"); raf.write(content); raf.close(); diff --git a/solr/core/src/test/org/apache/solr/update/processor/URLClassifyProcessorTest.java b/solr/core/src/test/org/apache/solr/update/processor/URLClassifyProcessorTest.java new file mode 100644 index 00000000000..df92fc928d9 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/update/processor/URLClassifyProcessorTest.java @@ -0,0 +1,104 @@ +/** + * 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. + */ +package org.apache.solr.update.processor; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrInputDocument; +import org.apache.solr.update.AddUpdateCommand; +import org.apache.solr.update.processor.URLClassifyProcessor; +import org.apache.solr.update.processor.URLClassifyProcessorFactory; +import org.junit.BeforeClass; +import org.junit.Test; + +public class URLClassifyProcessorTest extends SolrTestCaseJ4 { + + private static URLClassifyProcessor classifyProcessor; + + @BeforeClass + public static void initTest() { + classifyProcessor = + (URLClassifyProcessor) new URLClassifyProcessorFactory().getInstance(null, null, null); + } + + @Test + public void testProcessor() throws IOException { + AddUpdateCommand addCommand = new AddUpdateCommand(null); + SolrInputDocument document = new SolrInputDocument(); + document.addField("id", "test"); + document.addField("url", "http://www.example.com"); + addCommand.solrDoc = document; + classifyProcessor.processAdd(addCommand); + } + + @Test + public void testNormalizations() throws MalformedURLException, URISyntaxException { + String url1 = "http://www.example.com/research/"; + String url2 = "http://www.example.com/research/../research/"; + assertEquals(classifyProcessor.getNormalizedURL(url1), classifyProcessor.getNormalizedURL(url2)); + } + + @Test + public void testLength() throws MalformedURLException, URISyntaxException { + assertEquals(22, classifyProcessor.length(classifyProcessor.getNormalizedURL("http://www.example.com"))); + } + + @Test + public void testLevels() throws MalformedURLException, URISyntaxException { + assertEquals(1, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com/research/"))); + assertEquals(1, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com/research/index.html"))); + assertEquals(1, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com/research/../research/"))); + assertEquals(0, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com/"))); + assertEquals(0, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com/index.htm"))); + assertEquals(0, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com"))); + assertEquals(0, classifyProcessor.levels(classifyProcessor.getNormalizedURL("https://www.example.com"))); + assertEquals(0, classifyProcessor.levels(classifyProcessor.getNormalizedURL("http://www.example.com////"))); + } + + @Test + public void testLandingPage() throws MalformedURLException, URISyntaxException { + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/index.html"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/index.htm"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/welcome.html"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/welcome.htm"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/index.php"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/index.asp"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/research/"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("https://www.example.com/research/"))); + assertTrue(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/"))); + assertFalse(classifyProcessor.isLandingPage(classifyProcessor.getNormalizedURL("http://www.example.com/intro.htm"))); + } + + @Test + public void testTopLevelPage() throws MalformedURLException, URISyntaxException { + assertTrue(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://www.example.com"))); + assertTrue(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://www.example.com/"))); + assertTrue(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://subdomain.example.com:1234/#anchor"))); + assertTrue(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://www.example.com/index.html"))); + + assertFalse(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://www.example.com/foo"))); + assertFalse(classifyProcessor.isTopLevelPage(classifyProcessor.getNormalizedURL("http://subdomain.example.com/?sorting=lastModified%253Adesc&tag=myTag&view=feed"))); + } + + @Test + public void testCanonicalUrl() throws MalformedURLException, URISyntaxException { + assertEquals("http://www.example.com/", classifyProcessor.getCanonicalUrl(classifyProcessor.getNormalizedURL("http://www.example.com/index.html")).toString()); + } +} diff --git a/solr/example/etc/jetty.xml b/solr/example/etc/jetty.xml index e455d0dd06c..f287280d8a1 100755 --- a/solr/example/etc/jetty.xml +++ b/solr/example/etc/jetty.xml @@ -1,57 +1,37 @@ - + - + - - - - - org.mortbay.jetty.Request.maxFormContentSize - 1000000 - + - - + + 10 10000 - 20 + false - - - - - - - - + 50000 @@ -80,148 +60,85 @@ - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - - - - - - - - - - - - - - - - /contexts - 5 - - - - - - - - - - - - - - - - - - - - - /webapps - false - true - false - /etc/webdefault.xml - - - - - - - - - - - - - - - - - - - - - true - false - false + true + true 1000 + false + false + + + + + + + + + + + + org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern + .*/servlet-api-[^/]*\.jar$ + + + + + + + + + + + + + + + /webapps + /etc/webdefault.xml + 1 + /contexts + true + + + + diff --git a/solr/example/etc/webdefault.xml b/solr/example/etc/webdefault.xml index b8d01141697..213138b35fe 100644 --- a/solr/example/etc/webdefault.xml +++ b/solr/example/etc/webdefault.xml @@ -1,118 +1,126 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + Default web.xml file. This file is applied to a Web application before it's own WEB_INF/web.xml file + + + + + + org.eclipse.jetty.servlet.listener.ELContextCleaner + + + + + + + + org.eclipse.jetty.servlet.listener.IntrospectorCleaner + + - - - org.mortbay.jetty.webapp.NoTLDJarPattern - start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + default - org.mortbay.jetty.servlet.DefaultServlet + org.eclipse.jetty.servlet.DefaultServlet + + aliases + false + acceptRanges true @@ -135,15 +143,11 @@ maxCachedFileSize - 10000000 + 200000000 maxCachedFiles - 1000 - - - cacheType - both + 2048 gzip @@ -152,7 +156,13 @@ useFileMappedBuffer true - + + 0 - + + + + default + / + - default / - @@ -250,20 +263,22 @@ - + jsp org.apache.jasper.servlet.JspServlet - logVerbosityLevel - DEBUG + logVerbosityLevel + DEBUG - fork - false + fork + false - xpoweredBy - false + xpoweredBy + false @@ -305,26 +320,11 @@ - @@ -337,7 +337,7 @@ - + - arISO-8859-6 - beISO-8859-5 - bgISO-8859-5 - caISO-8859-1 - csISO-8859-2 - daISO-8859-1 - deISO-8859-1 - elISO-8859-7 - enISO-8859-1 - esISO-8859-1 - etISO-8859-1 - fiISO-8859-1 - frISO-8859-1 - hrISO-8859-2 - huISO-8859-2 - isISO-8859-1 - itISO-8859-1 - iwISO-8859-8 - jaShift_JIS - koEUC-KR - ltISO-8859-2 - lvISO-8859-2 - mkISO-8859-5 - nlISO-8859-1 - noISO-8859-1 - plISO-8859-2 - ptISO-8859-1 - roISO-8859-2 - ruISO-8859-5 - shISO-8859-5 - skISO-8859-2 - slISO-8859-2 - sqISO-8859-2 - srISO-8859-5 - svISO-8859-1 - trISO-8859-9 - ukISO-8859-5 - zhGB2312 - zh_TWBig5 + + ar + ISO-8859-6 + + + be + ISO-8859-5 + + + bg + ISO-8859-5 + + + ca + ISO-8859-1 + + + cs + ISO-8859-2 + + + da + ISO-8859-1 + + + de + ISO-8859-1 + + + el + ISO-8859-7 + + + en + ISO-8859-1 + + + es + ISO-8859-1 + + + et + ISO-8859-1 + + + fi + ISO-8859-1 + + + fr + ISO-8859-1 + + + hr + ISO-8859-2 + + + hu + ISO-8859-2 + + + is + ISO-8859-1 + + + it + ISO-8859-1 + + + iw + ISO-8859-8 + + + ja + Shift_JIS + + + ko + EUC-KR + + + lt + ISO-8859-2 + + + lv + ISO-8859-2 + + + mk + ISO-8859-5 + + + nl + ISO-8859-1 + + + no + ISO-8859-1 + + + pl + ISO-8859-2 + + + pt + ISO-8859-1 + + + ro + ISO-8859-2 + + + ru + ISO-8859-5 + + + sh + ISO-8859-5 + + + sk + ISO-8859-2 + + + sl + ISO-8859-2 + + + sq + ISO-8859-2 + + + sr + ISO-8859-5 + + + sv + ISO-8859-1 + + + tr + ISO-8859-9 + + + uk + ISO-8859-5 + + + zh + GB2312 + + + zh_TW + Big5 + - + Disable TRACE @@ -405,6 +522,6 @@ - + diff --git a/solr/example/exampledocs/money.xml b/solr/example/exampledocs/money.xml new file mode 100644 index 00000000000..b1b8036c369 --- /dev/null +++ b/solr/example/exampledocs/money.xml @@ -0,0 +1,65 @@ + + + + + + USD + One Dollar + Bank of America + boa + currency + Coins and notes + 1,USD + true + + + + EUR + One Euro + European Union + eu + currency + Coins and notes + 1,EUR + true + + + + GBP + One British Pound + U.K. + uk + currency + Coins and notes + 1,GBP + true + + + + NOK + One Krone + Bank of Norway + nor + currency + Coins and notes + 1,NOK + true + + + + diff --git a/solr/example/lib/jetty-6.1.26-patched-JETTY-1340.jar b/solr/example/lib/jetty-6.1.26-patched-JETTY-1340.jar deleted file mode 100644 index 16de87a3020..00000000000 --- a/solr/example/lib/jetty-6.1.26-patched-JETTY-1340.jar +++ /dev/null @@ -1,2 +0,0 @@ -AnyObjectId[6be492c92fd7b36ed008e8461c8657cc6bd27c2a] was removed in git history. -Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-continuation-8.1.2.v20120308.jar b/solr/example/lib/jetty-continuation-8.1.2.v20120308.jar new file mode 100644 index 00000000000..f13a9aca235 --- /dev/null +++ b/solr/example/lib/jetty-continuation-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[04ad836024b2ed0ed66d46bbfed1bc184e645a06] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-deploy-8.1.2.v20120308.jar b/solr/example/lib/jetty-deploy-8.1.2.v20120308.jar new file mode 100644 index 00000000000..04ad14bc14e --- /dev/null +++ b/solr/example/lib/jetty-deploy-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[2989c267911ce65d6ab048f5728c20b560cb2827] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-http-8.1.2.v20120308.jar b/solr/example/lib/jetty-http-8.1.2.v20120308.jar new file mode 100644 index 00000000000..380495b7899 --- /dev/null +++ b/solr/example/lib/jetty-http-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[fbf6531c638988bc2748854234da312af0671d15] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-io-8.1.2.v20120308.jar b/solr/example/lib/jetty-io-8.1.2.v20120308.jar new file mode 100644 index 00000000000..6d62296c6fd --- /dev/null +++ b/solr/example/lib/jetty-io-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[566b499e0d29c7cfc4ab1bf58f6d80e4471a93dc] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-jmx-8.1.2.v20120308.jar b/solr/example/lib/jetty-jmx-8.1.2.v20120308.jar new file mode 100644 index 00000000000..b7f3263b4ab --- /dev/null +++ b/solr/example/lib/jetty-jmx-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[983b8a404148e036b09ad363f201b76e16d93875] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-security-8.1.2.v20120308.jar b/solr/example/lib/jetty-security-8.1.2.v20120308.jar new file mode 100644 index 00000000000..7d066b498aa --- /dev/null +++ b/solr/example/lib/jetty-security-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[fd6db76dc3ac8fdffa6ff34e44ce14b7c26bf748] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-server-8.1.2.v20120308.jar b/solr/example/lib/jetty-server-8.1.2.v20120308.jar new file mode 100644 index 00000000000..581d05f5c8a --- /dev/null +++ b/solr/example/lib/jetty-server-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[72b572682fed36ff5717e380be1a2ff2a985d7fa] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-servlet-8.1.2.v20120308.jar b/solr/example/lib/jetty-servlet-8.1.2.v20120308.jar new file mode 100644 index 00000000000..68ef41fa973 --- /dev/null +++ b/solr/example/lib/jetty-servlet-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[bd559dde856dba9dcf2e83336ad9d0b7d5120735] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-util-6.1.26-patched-JETTY-1340.jar b/solr/example/lib/jetty-util-6.1.26-patched-JETTY-1340.jar deleted file mode 100644 index 0304e37bbf7..00000000000 --- a/solr/example/lib/jetty-util-6.1.26-patched-JETTY-1340.jar +++ /dev/null @@ -1,2 +0,0 @@ -AnyObjectId[1a9ace88dd00cf94e17d231805cc8bdc60886376] was removed in git history. -Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-util-8.1.2.v20120308.jar b/solr/example/lib/jetty-util-8.1.2.v20120308.jar new file mode 100644 index 00000000000..752c6f618b2 --- /dev/null +++ b/solr/example/lib/jetty-util-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[c40b60abada935e753a4b98bee2bf1bdb31557ea] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-util-NOTICE.txt b/solr/example/lib/jetty-util-NOTICE.txt deleted file mode 100644 index 21d4ed3d0ee..00000000000 --- a/solr/example/lib/jetty-util-NOTICE.txt +++ /dev/null @@ -1,36 +0,0 @@ -============================================================== - Jetty Web Container - Copyright 1995-2009 Mort Bay Consulting Pty Ltd -============================================================== - -The Jetty Web Container is Copyright Mort Bay Consulting Pty Ltd -unless otherwise noted. It is licensed under the apache 2.0 -license. - -The javax.servlet package used by Jetty is copyright -Sun Microsystems, Inc and Apache Software Foundation. It is -distributed under the Common Development and Distribution License. -You can obtain a copy of the license at -https://glassfish.dev.java.net/public/CDDLv1.0.html. - -The UnixCrypt.java code ~Implements the one way cryptography used by -Unix systems for simple password protection. Copyright 1996 Aki Yoshida, -modified April 2001 by Iris Van den Broeke, Daniel Deville. -Permission to use, copy, modify and distribute UnixCrypt -for non-commercial or commercial purposes and without fee is -granted provided that the copyright notice appears in all copies. - -The default JSP implementation is provided by the Glassfish JSP engine -from project Glassfish http://glassfish.dev.java.net. Copyright 2005 -Sun Microsystems, Inc. and portions Copyright Apache Software Foundation. - -Some portions of the code are Copyright: - 2006 Tim Vernum - 1999 Jason Gilbert. - -The jboss integration module contains some LGPL code. - -The win32 Java Service Wrapper (v3.2.3) is Copyright (c) 1999, 2006 -Tanuki Software, Inc. and 2001 Silver Egg Technology. It is -covered by an open license which is viewable at -http://svn.codehaus.org/jetty/jetty/branches/jetty-6.1/extras/win32service/LICENSE.txt diff --git a/solr/example/lib/jetty-webapp-8.1.2.v20120308.jar b/solr/example/lib/jetty-webapp-8.1.2.v20120308.jar new file mode 100644 index 00000000000..7e466256b7e --- /dev/null +++ b/solr/example/lib/jetty-webapp-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[c176e13b3799112d3c8f00fc03112b632de98e41] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/jetty-xml-8.1.2.v20120308.jar b/solr/example/lib/jetty-xml-8.1.2.v20120308.jar new file mode 100644 index 00000000000..a7e93edbf27 --- /dev/null +++ b/solr/example/lib/jetty-xml-8.1.2.v20120308.jar @@ -0,0 +1,2 @@ +AnyObjectId[b8fba3039f3392469ff9f86c9fbccb8dc2160e58] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/servlet-api-2.5-20081211.jar b/solr/example/lib/servlet-api-2.5-20081211.jar deleted file mode 100644 index 7db0068dda9..00000000000 --- a/solr/example/lib/servlet-api-2.5-20081211.jar +++ /dev/null @@ -1,2 +0,0 @@ -AnyObjectId[b0537c4dbdfbaeaf91078d79a32d01110f8f1de4] was removed in git history. -Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/lib/servlet-api-3.0.jar b/solr/example/lib/servlet-api-3.0.jar new file mode 100644 index 00000000000..422a2041cf4 --- /dev/null +++ b/solr/example/lib/servlet-api-3.0.jar @@ -0,0 +1,2 @@ +AnyObjectId[b135409682ee2173ee74cc9e9b00469d7fa0a27e] was removed in git history. +Apache SVN contains full history. \ No newline at end of file diff --git a/solr/example/solr/conf/currency.xml b/solr/example/solr/conf/currency.xml new file mode 100644 index 00000000000..3a9c58afee8 --- /dev/null +++ b/solr/example/solr/conf/currency.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/solr/example/solr/conf/schema.xml b/solr/example/solr/conf/schema.xml index 8605fa1cbd4..dd0c9e71d65 100755 --- a/solr/example/solr/conf/schema.xml +++ b/solr/example/solr/conf/schema.xml @@ -45,16 +45,17 @@ that avoids logging every request --> - + @@ -67,10 +68,10 @@ --> - + - + @@ -93,10 +94,10 @@ - - - - + + + + - - - - + + + + - + - + - - - - - + + + + + + + + @@ -824,6 +835,7 @@ this field (this disables length normalization and index-time boosting for the field, and saves some memory). Only full-text fields or fields that need an index-time boost need norms. + Norms are omitted for primitive (non-analyzed) types by default. termVectors: [false] set to true to store the term vector for a given field. When using MoreLikeThis, fields used for similarity should be @@ -918,7 +930,7 @@ - + @@ -931,6 +943,7 @@ + @@ -966,6 +979,9 @@ + + + - - -
    - Introduction to The Solr Enterprise Search Server -
    - - - -
    -Solr in a Nutshell -

    - Solr is a standalone enterprise search server with a REST-like API. You put documents in it (called "indexing") via XML, JSON or binary over HTTP. You query it via HTTP GET and receive XML, JSON, or binary results. -

    -
      -
    • Advanced Full-Text Search Capabilities
    • -
    • Optimized for High Volume Web Traffic
    • -
    • Standards Based Open Interfaces - XML,JSON and HTTP
    • -
    • Comprehensive HTML Administration Interfaces
    • -
    • Server statistics exposed over JMX for monitoring
    • -
    • Scalability - Efficient Replication to other Solr Search Servers
    • -
    • Flexible and Adaptable with XML configuration
    • -
    • Extensible Plugin Architecture
    • -
    -
    - -
    -Solr Uses the Lucene Search Library and Extends it! -
      -
    • A Real Data Schema, with Numeric Types, Dynamic Fields, Unique Keys
    • -
    • Powerful Extensions to the Lucene Query Language
    • -
    • Faceted Search and Filtering
    • -
    • Geospatial Search
    • -
    • Advanced, Configurable Text Analysis
    • -
    • Highly Configurable and User Extensible Caching
    • -
    • Performance Optimizations
    • -
    • External Configuration via XML
    • -
    • An Administration Interface
    • -
    • Monitorable Logging
    • -
    • Fast Incremental Updates and Index Replication
    • -
    • Highly Scalable Distributed search with sharded index across multiple hosts
    • -
    • JSON, XML, CSV/delimited-text, and binary update formats
    • -
    • Easy ways to pull in data from databases and XML files from local disk and HTTP sources
    • -
    • Rich Document Parsing and Indexing (PDF, Word, HTML, etc) using Apache Tika
    • -
    • Apache UIMA integration for configurable metadata extraction
    • -
    • Multiple search indices
    • -
    -
    - -
    -Detailed Features - -
    Schema -
      -
    • Defines the field types and fields of documents
    • -
    • Can drive more intelligent processing
    • -
    • Declarative Lucene Analyzer specification
    • -
    • Dynamic Fields enables on-the-fly addition of new fields
    • -
    • CopyField functionality allows indexing a single field multiple ways, or combining multiple fields into a single searchable field
    • -
    • Explicit types eliminates the need for guessing types of fields
    • -
    • External file-based configuration of stopword lists, synonym lists, and protected word lists
    • -
    • Many additional text analysis components including word splitting, regex and sounds-like filters
    • -
    -
    - -
    Query -
      -
    • HTTP interface with configurable response formats (XML/XSLT, JSON, Python, Ruby, PHP, Velocity, binary)
    • -
    • Sort by any number of fields, and by complex functions of numeric fields
    • -
    • Advanced DisMax query parser for high relevancy results from user-entered queries
    • -
    • Highlighted context snippets
    • -
    • Faceted Searching based on unique field values, explicit queries, date ranges, and numeric ranges
    • -
    • Multi-Select Faceting by tagging and selectively excluding filters
    • -
    • Spelling suggestions for user queries
    • -
    • More Like This suggestions for given document
    • -
    • Function Query - influence the score by user specified complex functions of - numeric fields or query relevancy scores.
    • -
    • Range filter over Function Query results
    • -
    • Date Math - specify dates relative to "NOW" in queries and updates
    • -
    • Dynamic search results clustering using Carrot2
    • -
    • Numeric field statistics such as min, max, average, standard deviation
    • -
    • Combine queries derived from different syntaxes
    • -
    • Auto-suggest functionality for completing user queries
    • -
    • Allow configuration of top results for a query, overriding normal scoring and sorting
    • -
    • Performance Optimizations
    • -
    -
    - -
    Core -
      -
    • Dynamically create and delete document collections without restarting
    • -
    • Pluggable query handlers and extensible XML data format
    • -
    • Pluggable user functions for Function Query
    • -
    • Customizable component based request handler with distributed search support
    • -
    • Document uniqueness enforcement based on unique key field
    • -
    • Duplicate document detection, including fuzzy near duplicates
    • -
    • Custom index processing chains, allowing document manipulation before indexing
    • -
    • User configurable commands triggered on index changes
    • -
    • Ability to control where docs with the sort field missing will be placed
    • -
    • "Luke" request handler for corpus information
    • -
    -
    - -
    Caching -
      -
    • Configurable Query Result, Filter, and Document cache instances
    • -
    • Pluggable Cache implementations, including a lock free, high concurrency implementation
    • -
    • Cache warming in background -
      • When a new searcher is opened, configurable searches are run against - it in order to warm it up to avoid - slow first hits. During warming, the current searcher handles live requests. -
      -
    • -
    • Autowarming in background -
        -
      • The most recently accessed items in the caches of the current - searcher are re-populated in the new searcher, enabling high cache hit - rates across index/searcher changes.
      • -
      -
    • -
    • Fast/small filter implementation
    • -
    • User level caching with autowarming support
    • -
    -
    - -
    Replication -
      -
    • Efficient distribution of index parts that have changed
    • -
    • Pull strategy allows for easy addition of searchers
    • -
    • Configurable distribution interval allows tradeoff between timeliness and cache utilization
    • -
    • Replication and automatic reloading of configuration files
    • -
    -
    - -
    Admin Interface -
      -
    • Comprehensive statistics on cache utilization, updates, and queries
    • -
    • Interactive schema browser that includes index statistics
    • -
    • Replication monitoring
    • -
    • Full logging control
    • -
    • Text analysis debugger, showing result of every stage in an analyzer
    • -
    • Web Query Interface w/ debugging output -
        -
      • parsed query output
      • -
      • Lucene explain() document score detailing
      • -
      • explain score for documents outside of the requested range to debug why a given document wasn't ranked higher.
      • -
      -
    • -
    -
    - - - -
    - - -
    diff --git a/solr/site-src/src/documentation/content/xdocs/images/as3ess_book.jpg b/solr/site-src/src/documentation/content/xdocs/images/as3ess_book.jpg deleted file mode 100644 index 88c849069d1..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/as3ess_book.jpg and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/lucene_green_150.gif b/solr/site-src/src/documentation/content/xdocs/images/lucene_green_150.gif deleted file mode 100644 index 49480177629..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/lucene_green_150.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/lucidworks_reference_guide.png b/solr/site-src/src/documentation/content/xdocs/images/lucidworks_reference_guide.png deleted file mode 100644 index 2e239ab9872..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/lucidworks_reference_guide.png and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.ai b/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.ai deleted file mode 100755 index b88e098153a..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.ai +++ /dev/null @@ -1,1405 +0,0 @@ -%PDF-1.5 %âăÏÓ -1 0 obj <>/OCGs[6 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream - - - - - application/pdf - - - powered-by-solr - - - - - Adobe Illustrator CS4 - 2009-09-10T13:01:32-04:00 - 2009-09-10T13:01:32-04:00 - 2009-09-10T13:01:32-04:00 - - - - 256 - 132 - JPEG - /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAhAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXlOpedPzX1H8x/M HlbylFoK2uhx2kpl1VbwSMLqFZPtW7kGjE/sjbFU38p+evNX+Kz5P866ba2WtSWzXun3unSPJaXU UbcJOKyfvI2B3ox6eHdVn+KuxV2KuxV2KuxV2KuxVLvMl3cWfl3VLy2f07i2s55oXoDxdImZTRgQ aEd8VSj8r9a1PW/y+0LVtUm+sahe2qS3M3FE5OSanigVR9AxVLPKHmbW9Q/M/wA+aJeXPq6Xo36M /RtvwjX0vrNs0kvxqoduTCvxE07YqzvFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXhQ/wCVh/8AK9vPP+DP0R631fTPrn6Z+s8eP1VOHpfVt61rXlilHeZtE84eVdE8wfmT5g1aHUvN lrpxs9KitYfSs7FJpFVjGrHlI3JuXJ/prihuX8jvLMflFvMCX18POKWn1/8AxN9bm9c3CxerUrz9 P0+Q6ca074qh7fXL+f8A5Vf+ZF4As98v6E12WgRWS9BWKRv5VFwnPw+LFUPpeoXUf5qw+fTMz6Vr usXXlVQKhBDFEiWrAf5V1byffhVC6p5j1Wz8qfmD+Y+mlxf6xqMej6JcoObR2drKtmskYO1WYuRT 9qmKsaurO10i2s9V8l+TvPFr50s5IZJdWvLOcreqHX10ul9WZeLpWgVPAHAlnfmTyvD5q/Pi60e8 vLq10t/LkNxe29rIYWuBHdsqxSOvxcKyciB1pihIPOQ0SPzu3ke70DXtW8k+W7C3FpoOgxzTq01x +89S7cSpKVUfCnx9a+9VUZ5M1/WfKukeepdK0XW9L8safp/1/wAv2uvwSRtb3KoyyRRNIZA0XLi3 HkTQfMlVGWf5OeWLr8t2806lc3t75tvNKbUpteF3Os3qTQevwVQ/pGMV40KGoxVnn5K/+Sp8sf8A MCn6zirArLyB5d84/nN+Ytvr6T3NjanSG+oRzywQSs9maNKIWRmKcfg+LapxSh/8FudT86/ldpl1 NLpUWnW2reX47iRpXsbxSDHHFIxqqF1HyH0klCZeYPOl350/LfyzoVmxj1zznMum6ki/bgS1P+5N mA3XiEIpTo2BXsVlZ21lZwWdqgitraNIYIx0WONQqqPkBiqtirsVdirsVdirzLzR+aWqW99c2Ol2 0UYt5HiNzJWQsUYqWVRxUdO9c0Wp7VkJGMRy6vTaLsSEoicydxdclD8vtV82a/5lM1/qMr2Vmhkm hWkcbM1VjUrGFHX4vow6DPkzZLJ2DLtXT4MGGoxHFL4+/mnP5ueZbjStCjsrGZob+/agljYq6RRk FmDDcEmi/fmZ2hqOCIA5lwexNGMuQykLjH73kFv5989WLBodZuWp2nb1x90vPNZDV5B/EXqJ9m6e fOEfht9z2H8rPM/m3zDp893rMMK2aHha3KKUeVwfi+GpWi9Kim/yObfSZpzFnk8r2vpMGCQjjJ4u o7mbGeETCAyKJmUusVRyKggFgvWgJGZdi6dRwmr6L8KHYq7FXYq7FWLaN5H/AEb5+8w+bfrvq/p6 K0i+pelx9H6pGI6+pzbnypX7Ip74qn2r6TYavpd1peoQiexvYmguIm6Mjih+R8D2xV5oPyW8zmx/ w/L5+1CTybQRfoj6tALk24HH0DfV5lOO1OFKdsVZh5l8h6Zq3kaTylaEabapDFFp8qJ6n1ZrdlaF 1UlalGQftCvjiqUXn5UQT/lha+SYtRaG5svRlt9ZEVXW7im9drgRcxu7ltuffriqawfl5oI/L6Ly PdK0+lraJayuPgdmWjGYbtxf1R6g60OKpDpn5ffmZp09vFF+Y9zNpFuy0tLjTbSadoxvwe6cl2r/ ADUrTFU/i8len+ZE/nX65X1tKXSfqHpdOM4m9X1eftTjw+nFUB5v/Le41bW4/Mega7c+W/MSQi2k vII0nhmhViyrPbyUWTiTsScVRvlzyr5kg07UbHzf5iPmqHUE9H03srexjSJlZZE4wVLeoG3q3bbF WIJ+Svmi305/L1h5+vrbye3KNdJ+qwvcLA9awrelhIE7UC0ptTFWfeSvLf8AhjyppegfWfrf6NgW D6zw9Lnxr8XDk/H/AII4qwzVvyk8yN5z1jzZ5d84zaDf6u0HqwrZx3MPpwQLCFkjlfjI1V5K1Bxq RTvirIfIn5fxeV/rt7d6hNrXmHVSjaprFyAryemKIiICRHGvZQT+rFWE/lV5W024/Mvzl5tsiW0m C+msdIStYxcScG1CWICigNKoUEdRXFXsWKuxV2KuxVi3mzziNN5WdhSS+I+NzusVfbu3t9+anX9p DF6Ybz+52ug7O8T1T2j96ZeXZ5k8t21zdyNLJ6TTSyMasakv+o5laOZ8ASkb2txtZEHOYxFb08Zu IGlleVxVpGLN8yanOPMrNvZxlQp6p+XejLpvl9ZnXjNen1nJ/k6Rj7vi+nOq7Lw8GKzzlv8AqeV7 X1HiZqHKO363mHnjUm1vX7i6Brbp+5th29NOhH+sat9OaLWanxMhPTo9N2bgGHEI9eZ96A8seTbj zDrCWaVS3X47ucfsRjrT/KboMno8Ryz4Ry6tuu1scGPi69B5vbtU1LRvKfl71WUQ2Vmgjt7dOrMB 8KL4k+P0nOlyTjhh5B4rDiyanLXOUuZ/S+bPMnmjXtV8wtrslw8F6rVtTEzL6KqfhSMjoB+OaU6g ylxHm95ptHjx4vDAuPXz971n8svzhj1h4tF8wMsOrNRLa7ACx3B/lYDZJPwbtTpm00+q4tpc3me1 exTivJi3h1Hd+x6nma887FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq06B0ZDUBgQSpKnfw IoR9GKpf5e8u6N5d0qHSdHtha2EBYxxBnc1kYu7M8hd2JZiasTiqY4q7FXYq6orTvirynVLLhqd2 tOk0g/4Y5w2pFZJD+kfvetwZLxx9wZ5ff6J5RZBsVtFi+llCfxzqMx8PSf5gH6HQYvXqf86/0vPt O0dr7UILUD+9cBiOy9WP0DOY0+I5MgiOr0ObUcEDLuZ35x1BdP0JreD4JLgehEo7JSjH6F2zpu0s 4xYaHOWwdD2di8TLZ5Dd5UmmzXM6QQoZJZWCog6knOWhEyIA5l6o5hEWeQes6Bo2n+WNEYSOqlR6 t7cnarAfqHRc67TYI6fHv8S8lqtRPU5dvcA8g89+Y7rzFqJkPJLGCq2kB7D+dv8AKb+zNBqtYcs7 /h6PW9m6KOCFfxHmWGSafLNKsUMbSSyEKkaglmJ6AAZHHK3ZGYAs8nq35e/kzb2UkOseYkEt4pEl vYHdIyNw0vZmH8vQe/be6bSVvP5PLdp9tmd48X09T3+56mbu1F0LQzRi6ZDKtvyHqGMEKXCV5cQS BXNhYunneA1xVt3quFi7FXYq7FXYq7FXYq7FXYq8487/AJ5eV/KPnKw8sX0Uss136RurqMqI7ZZn 4qZAdzt8Rp2xV6PirsVdirsVdirF/Jnnyz803/mGzt7WS3by9qEumzPIykSPEzKXWnQHj3xVlGKu xV2KuxVpmVVLMaKoqT7DASALKQLSLQNQku9RvpHrSTi0ansqEgD8c03ZuqOXLMnrVe4Ow1mEQhED okesWddZuFA3eSv/AAVD/HNNrof4RId5+9z9Nk/dD3J/5rIXSlhX9t1WnsoJ/gM3nbM+HCB3l13Z 4vJfkgfJ+mhZZr1h9kenH8zux/VmJ2JgsnIfcP0uR2lm2EfilHmu6N9qTcTWGD93H4bfaP0nMLtP U+JlNco7OXoMfh4/M7px5P8ALyWkf6RuFpcSD9yD+wh7/Nv1ZteytFwR8SXM8vIOF2jq+M8EeQ5s d88eYX1KT6lbNSxiPxEf7scd/wDVHbMDtLX+KeGP0D7XY9maTwhxS+s/Yw+20O91K6W1s4jLM3Yd APFj2GYGHFLJLhiLLt56mOOPFI0HqPlDyFpmgKLmQLcamw+O5I2SvVYwenz6n8M6nR6GOEWd5fjk 8tr+1J6g0Nod360g89fm1baakljoPG6v91e7PxQxH/J/nYfd8+mV6jtED0w3Pe5nZ/YkslSy+mPd 1P6vveNad5t1nTfNtv5knnkuruOXlcl2qZIm+F4/ChUkDsMw8WYiXF1enzaLHPCcQFRrbyL6otLq C7tIbu3YPb3EaywuOjI4DKfpBzfRNi3zqcDGRieYVcLF2KuxV2KuxV2KuxVBa3q9lo2j3urXz+nZ 2EMlxO3fhGpY08SabDxxV8iW/m38u/MHlfzpfea79o/OHmWcz2KiCaVbZbb4rWMSKnEAn4G/yKd8 UvZPyw8/v5p/I7U2lnJ1nRdPurO8atH/AHduxglr1+KOm/8AMDigvNIPJ2kyfkov5jz+Zb6XzZZI rWdy12WFvJHN6aWqLUsCyitK179MUs3883/l7zTD5S0zWtI1XzF5qvNHhv5dA0+4NtbKJ41Zpblq oqlZFIX8f2cUJB5V8xeYfLv5I/mGbI3NncaNqsljYWssvrS2MckkMDxrIK7wiRt125CoxS9E/Lb8 nvKukfoDzZZX17JqzWyz3c7XJeG8e6typMqNyBC+pVOJHQdcUPPvKv5YaD5z1v8AM6fWLi742eu3 4tLeCYxwpL6kresUGzOKAb9sUoW787+apP8AnHfypGl1M9xq2pnSLu7WURzvbLJNxiE8h4oWVFTk xpxG/fFWV/lv5K86+XfzEtLnTfLF15c8o3dvJFrVrcapb36GZY2aK4VVkeTm0gVTQdz0GKHueKux VbJGskbRturgqw9iKZGcRIEHkUxNG2OaJC1rq7Qt1o8Z96b/AMM5jswHFqjA+Y/T+h2mqlx4rX39 uH19NtneMn5Cg/hktZjvWgd5j+hGGdYfmqeZqu0EQ3oGYj50A/Vlvbk94x97DQbWUTKRpeihF2mK 8R/rtuT9GZmSX5XS1/F+k/qao/vct9Em0PRRdXHrzLW3iO4P7TeHy8c1PZei8WXFL6I/aXO1ep4I 0PqKv5q1luDafbN1FLhx4fyD+OZnauv/AMlD4/q/W1aDTfxy+H62M6b5bu9Um4xjhAp/eTsPhX5e J9s1ej0c852+nqXZ59ZHEN+fczaGDQ/LOmliRDH+3I28kjfRuT7DOnjDFpcfd95dHKWXUz7/ALg8 484+dtS1ZXtbYta6edjGD8cg/wAsjt/kj8c0Wr7Sll2G0fxzej7P7Nhi9UvVP7vc88vIOu2YkJO9 BQ+neUde12f0dLspLg1o0gHGNf8AWkaij782GCEp/SLa8+sxYRc5U+ifIWjarovlSx0vVJI5Lq1V k5REsoQsWRakDdQeOb/BAxgAXhO0M8MuaU4ciyDLnCdirsVdirsVdirsVSPzn5Q03zdoE2hanLPF YXDI04tnEbuI25BSSG+HkAae2Kppp9haadYW1hZxiK0tIkgt4h0WONQqr9AGKsPH5UaHY3XmrUdG eW21DzTazw3cLuPqvqzKaScFXkCGYnr+0cVY35O/5xv8iabpmmtrunx6hrVqA13Kks/1aaRGYo7Q syo1FYChWh796qsi86fk75Z8169aa9Pd6hpeq2kP1ZbnTJxbs8NW+BiUc0+Nh8NNjTFVXy5+T/kr QdO1rTbWGe4sdfoNRt7ueSZWATieNTsWJLFvtVPWgACqA8pfkd5Y8saxaalZ6lq10mns7WGnXl0J bOFpEZCUiEabhXNKn3xVkXl7yJoegza7LYmbl5iu5b7UPUcMBLMWL+nRRxHxmg3xVLrb8o/JkPkT /BEkEl1oYZ5FE71mV3cyc1kULRlZtvuxVB+VPya0Ty5rVrq0es61qUtiHFlbajeCa3i5xtESkaon 7DkCuKs/xV2KoO/ujby2xr8DMQ/ypT+Oa7XarwZQPQndvw4+IFD3kIj1S3uF6SEKx9+n6jmHrMfh 6qGQcpGv0fc24pXjMe5fcQ8tZgb/ACan6OWT1GO9bD+r91sYS/dFZLB9Y1kV3SFQT9G4/E5DJj8b W10gB+v7yyjLhxe9R1FJL+/W2j+xHsx7A/tH+GU67i1OoGKPKP4P6meAjHDiPVV1K7SwtFtbXaSl AR+yPH5nMnW6qOmxjFj+r7vP3sMGM5JcUuSVad5fe7YTXFUt+tP2n/s981+h7Mll9U9ofaXMz6wQ 2jzTDUtbstKg+rWqK8qCixLsq/6xH+3m11Ovx6ccEBch07ve4uDSzzHikdmA6xd3l/OZrqQyN0Ud lHgo7ZzebUTyy4pG3oNPjjjFRChY+Tta1UgwQ+nCf93y/AlPbu30DMjT6DLl5Ch3lll7QxYuZs9w ZXo/5WaFassuok6hMN+DfBCD/qA1b6TT2zeafsqEN5eo/Y6nUdt5Z7Q9A+1PdS8weWfLtsEu7mCy jjHwWyUDU/yYkHL7hmdPNjxCiQPJwMWmzZzcQZef7UF5Q896X5puNQj06KVItP8ASrLMAvP1ef2V BJAHp98GDUDJddGzW9nT0wiZkXK+XlX62SZkOA7FXYq7FXYq7FXYq7FUi80eevKHlWFZfMGqwaeH FY45GrK4HUpEgaRh8lxV57c/85T/AJUwylI5b64Uf7sitiFP/Ixo2/DFaVrL/nKH8pLhgJry6swf 2p7WQgf8ifVxWmdeW/P/AJL8zMY9C1m1v5lXm1vFIPWC/wAxiajgb9aYqn+KuxV2KuxV2Ksb8wfm P5G8u340/W9Zt7C9KCUQTMQ3BiQG2B2PE4qu8u/mJ5J8yXr2Ohaxb6hdxxGeSGFiWEasqljUDbk6 j6cVT26gjniMT991PgR3zG1WnjmgYHry97PHMxNhClHksij/AN9bn/iPQ/SM1soyyacxP95iP3fr DdYE7HKSIZA13FIOnBqfh/XM2UeLPCX9GX6P1tQNRI81JD6Uc9xSrysQg8aGi5jY5eHDJl/inLb7 oszuRHuWKFsbetOdzL261P8AZkI1o8W++Wf3/sZH95L+iFGDTkBN3fEFvtcW6fT/AEyvTaAC82oO /Pf9P6mc85+mCF1LWZpKw2lVU7cx9o/LwzH1na0p+jFsO/qfc3YNKBvJAW/lm9uTyl/cRnu27f8A A/1ynT9k5cm8vSPPn8nInroR5blNoND0PTE9ecIWX/d05HX2B2zdYtDgwDil85OFPVZcpofIIDVf PumWoK2sbXcg6H7CfeRX8MqzdsY47RHF9gb8HZU5fUeEME17z35kvQyJcfU4T+xb/Af+D3f8c1Wb tPNk68I8ne6XsvDDcjiPn+Kef6grOzM5LMdyxNSTmPCTu47PR/yChZV12Qj4Ha1UH3USk/8AEhm/ 7M5S+H6XmvaQ74x/W/Q9bzavMOxV2KuxV2KuxV2KsU/NHzqvkvyPqWvqgkuYFWOzib7LTysEjrTs pPI+wxV8H63rmra5qlxqmrXL3d/csXmnkNSSew7ADsBsO2LJn2g/846/mrrOnxX8Wmx2lvOoeEXc yROyMKg+nVnWv+UBii1HWf8AnHv829KRpH0J7uJQTzspIrgmnhGjer/wuK2yb/nF60u9P/Nqa0v4 JLS6GnXCmCdGjkDc4jQqwB6A4qX17ih2KuxV2KuxV8e/85Yf+TQi/wC2Zb/8nJcUhE/84i/+TJ1L /tjT/wDUVbYqX1tNF6iihKsp5Kw7HKM+HxAKNEGwfNMZU5UJbmwo9OLjscYQJPERUqo+f4/SpPRc FApT9kUHyyyOMCvIUglYYwOJpUIPgX36ZVLEBRq+HkPPl+Pini+1RbjG/qOPVuG+yq70Ht4D3zDl w458UvXmPIDp7u4ebYNxQ2ioyWU9yeV1JwjG4jXt/DKJ6HLnPFmlwx/mj8frZxyxhtEbrTdaVYgi IB38V3P/AARw/mtLphUNz5b/AGsvDyZOaV33mK9cFYAIV8ftN952/DMDP2zlltH0j5ly8WigOe7H 7mO+vHLUkuHPejOf45rT4mU3vI/N2EDCA6BCt5W1242js3Fe70T/AIkRmTDs/PLlE/d97P8APYo8 5ND8steuP7ySCAd+TMx+5VI/HMuHY+Y86CntrFHlZVY/yXjk3u9UO/VIov8AjZm/41zMx9j1zl9j XL2hP8MPmWYeU/KGmeWLKW2sXkl9d/UlkmKliwULQcVXbbNnp9PHEKDptbrp6iQlKhXcnmZDhuxV 2KuxV2KuxV2KvIv+cpLK5ufypmkhQutpe2089P2Y6tHX/gpFxUPjaORo5EkWhZCGAIBFQa7g9cWT 6t8r/wDOW/lG6to4/MWn3WnXoUCWW3Vbi3Zh1I+JZFr2HE/PFFPRvLv5yfll5gkSLTfMFsbiT7Fv cFraQn+ULOI+R/1a4oZa9jZSXUd49vG93CCsNwyKZEVvtBXI5AHvTFUt83ebNF8qaDc65rM3o2Vs OgFXdzssca92Y7D+mKvkfzx/zkn+YfmG6lTS7o6BpZJEVvZmk3HsZLinPl/qcR7YppgZ8+eeSxY+ YtTLE1LG8uK18ft4pZp5M/5yM/Mny7cx/W79td08H97aagxkcj/JuDWVW+ZI9jiin1v5E886H518 vQa3o7kwyfBPA+0kMygFopB4ivXoRuMUPl7/AJyw/wDJoRf9sy3/AOTkuKQif+cRf/Jk6l/2xp/+ oq2xUvUv+clPzC84eTNP0Kby3f8A1CS8muEuW9GCbksaoVH75JKU5Hpioea/l9/zk15stDrVz5uv /wBKpDYF9Ks/Rt4PUu/WjRV5QxxtTi5Zuvwg98Vp5t5g/Nr8xtd1GS+vNfvImcnhb2s0lvBGD+yk cbKo+fU9ycU0+mv+cZta89a35OutQ8y3739l64h0mS4+K4KxAiZmlPxSKWIA5VNQ2+KC9cZZBXhx QHqx3P8ADMacJj6OGPnz/V97IEdUNLbRP/vRck+1Qo+7Ndl0kJ/3uW/iAPk2xyEfTFRMGix/bZWP uxP6sgMOhhzIPxv7mfHmPJab/QoPsqlR/LGa/eRlg1mjh9Nf6X9ifBzS/tUpfNemRDZJWp0ooH6y Mke2sI5CXy/ayj2fkPcl9x5+to68LR3/ANZgv6g2VHtyPSJciHZMjzklN3+Z90lfRsEX/XkLfqVc qPbcukR83Lx9iRPOR+SUp+Zvm2+voLGxtrVZriRY4/gkJqxpuS9KdztjDtTNkkIgRs/jvcmXY2nx wM5GVAeX6nqqBgihm5MAAzUpU+NM6APLHm3hQ7FXYq7FXYq7FXYqhdV0uw1bTLrTNQhE9leRNBcQ t0ZHFCP7cVfJHn3/AJxg87aNeTT+W0/TukliYgjKl1Gpr8MkbFQ9OlUrXrQYpt5Pq3l/XtHl9LVt OudPkrx4XUMkJJHhzArilL8Ve6/845fnBrOm+Y7Lyhq1y91omosLex9VizW07f3axk/sSN8PHxII 71UEJj/zl/5juJNb0Ty2jkW1vbHUJox0aSZ2iQn3RYmp/rYqHmv5KeRLXzr5/s9JvqnTYUe7v1U0 LRRUHCooRzdlUkdjikvtGPyL5Kj08acmg2AsQnp/VzbRFStKUIK7/TixfH3/ADkB+Xlh5K88+hpa GPSNShF5Zw1JERLMkkQJ3orLUexAxSGT/wDOJfmO4s/PN7oZc/VNUtGk9Mnb17YhlYD/AIxs4/2s VKD/AOcsP/JoRf8AbMt/+TkuKhE/84i/+TJ1L/tjT/8AUVbYqWYf85i/8cnyx/xnuv8AiEeKh8++ RfLS+Z/N+laA05tk1G4WF5wvIopqWIWoqaDbFJfXP/QtX5TfoVtNGmSeuUK/pMzym5D0/vPtenX2 4cfbFjbP/LHl+x8u+X9P0OwFLTT4EgjJ6txHxO3+U7VY+5xVHSgHf0llH0E/jmHniDvwCY+F/b+t nH30hXbTa8ZYzGfAgj9Wa2U9FdThwHzBH3NwGTobWGy0mX7MgB8A+/44RotFP6Zf7L9bLxco6KUv ly2cVSZxXpWh/phl2HjPKR+xlHWyHMIC48nyN/d3I+TIR+onKZdhHpP7HIh2kBzilV35G1cgmOSF /bkwP4rlMuxcw5GJ/HucqHamPqCkV95G8zipW09QeKSRn8CwOUnsvOP4ftDnY+08H86vgU+/L3yb Pp0smq6lF6d2ax20LUqi9GY+7dB7fPNr2ZoTD1zHq6Ov7W7RGQDHA3HqzrNy6J2KuxV2KuxV2Kux V2Ksd8++drDyX5efXtQt5rixhliinFvxMirK3AOAxUGhI2riqE8sfmz+XfmVEOla5bPM/S0mf0J6 9CPSl4OfoFMVZVLDBPE0UyLLC4oyOAysPcHY4q+V/wDnKny75D0q70qXRYLay124Mn1+0tAqAwgD hJJEnwq3KoB2Lb9aYpDyDyAkj+e/LixAmQ6pZcAvWv1hOlMUl6x/zl3pMsHnjSdT4kQXuniFW7GS 3mcuB/sZkxQGP/8AOM/maw0L8z4RfSrBDqtrLp6yuaKJJHSSMEn+Z4go9zipfaWKHyD/AM5WeZbH VPP1tpto6y/oa19G5dTWk8jl3T/Yrxr71HbFIQ//ADirpc93+aAvFU+lp1lPLI/YGTjCo+n1PwxU qn/OWH/k0Iv+2Zb/APJyXFQif+cRf/Jk6l/2xp/+oq2xUsw/5zF/45Plj/jPdf8AEI8VDxj8kP8A ybHln/mMH/EGxUvvDFC1m34g0Yiqn5ZVOe/CNiRskDqo/wB4W9NvSnX7S9j8x3+eYP8AeE8B8PKO Y6H4dR5tnLnuFF7yMH0r2Lif5qVX55jy10b8PUQr7Q2DEecCozaRbTrztpONen7S/wBchk7IxZBx YpV9oZx1MompBKLux1Szqyhwo35xE0/DcZrMmj1GDcXXfFzMeXHP9qAPmDVoPsXBI8HAb/iQORh2 lnj/ABfPdyPyeOXMNDz7qUO0kMUoHhyUn8SPwzMh21lHMAoPZWM8iQy3Rr+8vrQXFzaGzLfYjZuT Ff5qUWn05v8ATZpZI8Uo8Lp9RijjlwxlxI7MhodirsVdirsVdirsVdirsVedf85CafdX/wCUWu29 pbSXd1/orxQwo0knw3kJYqqgnZORPtiofDskckUjRyKUkQ0ZGBBBHYg4skTDquqQRelBeTxREU9N JHVaVrSgNOuKoYmSSSpq8jn3JJJ/EnFX0R/zjn+SWtJrdt5z8x2z2VrZ1fSrKZSssspUgTMh3VEr Va7k79BuoJe0/m5+Wln5/wDKr6YziDUbZvX0y7bokwBHF6b8HGzfQe2KHxN5q8m+ZfKmpPp2vWEt lOpIRnH7uQA/aikHwuvupxZJhF+an5kRab+jY/MuorZheIT6xJyC9OIevMCnauK0lXl/y15i8zam thotlNqN7Kassalqcj9uRz8KL4sxpir7Q/JP8qYvy+8tvDcOs+uaiVl1OdPsqVB4QoepWPkd+5JO LF4D/wA5Yf8Ak0Iv+2Zb/wDJyXFIRP8AziL/AOTJ1L/tjT/9RVtipZh/zmL/AMcnyx/xnuv+IR4q HjH5If8Ak2PLP/MYP+INipfeGKFksfqKKHiymqt4HKNRh8QCjUgbBZRlSjdW7uokjNJ06Ed/bMXW 6WUwJw2yx+3yZ45gbHkox3EN2v1e5XhL2B2r7rXvmPh1ENTHw8wqfy+TZKBh6o8ktvbW909vVhc+ n/Ovb/WGazUaTNpTxQJ4e/8AW5WLJDIKPNfZ+ZXLCO4iLk/txCp+lczNL2ySeGcb8x+pjl0I5xPz R13o2magnOWHizCokUGN9/Hp+IzZ5dFhzCzHc/A/j3uPj1OTGaB/ShtM8qaXYy+txM8wNUeWh4+F ANq++V6bszFiN/UfNsz6/JkFch5JzmxcJ2KuxV2KuxV2KuxV2KuxV2KuxVBajoWiakKajp9tejpS 4hjl2/2YbFUhl/Kf8sZX5t5V0oHwS0hQfcqgYqmOkeSPJujSCXSdDsLCZek1vbRRyf8ABqob8cVT rFXYqoXthYX9u1tfW0V3bt9qGdFkQ/NWBGKsbP5T/liZfVPlXSuVQaC0hC7f5IXj+GKsi07S9M0y 3Ftp1nBZW46Q28aRIP8AYoFGKonFXYq7FXYq7FXYq7FXYq4gHrjSuIBFDuD1GJFq0qqoooAHgNsA iBySTbeFDsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVd irsVdirsVdirsVdirsVdirsVdirsVdir/9k= - - - - - - uuid:97F791515BCCDD11AF63EF92CDF8F265 - xmp.did:21751C902B9EDE11AF46E5AC2E9F05D5 - uuid:fba65d0c-4cd5-4bc4-b94c-66b8930a6f77 - proof:pdf - - - - converted - from application/postscript to application/vnd.adobe.illustrator - - - saved - xmp.iid:E24134F3A094DE118E78DA3089A18CAE - 2009-08-29T09:37:05-04:00 - Adobe Illustrator CS4 - / - - - converted - from application/postscript to application/vnd.adobe.illustrator - - - saved - xmp.iid:21751C902B9EDE11AF46E5AC2E9F05D5 - 2009-09-10T13:01:20-04:00 - Adobe Illustrator CS4 - / - - - - - xmp.iid:E24134F3A094DE118E78DA3089A18CAE - xmp.did:E24134F3A094DE118E78DA3089A18CAE - uuid:97F791515BCCDD11AF63EF92CDF8F265 - - - - Print - Document - - - 1 - False - False - - 118.344542 - 54.999916 - Millimeters - - - - - Corbel - Corbel - Regular - Open Type - Version 5.00 - False - corbel.ttf - - - - - - Cyan - Magenta - Yellow - Black - - - - - - Default Swatch Group - 0 - - - - White - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - - Black - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 100.000000 - - - CMYK Red - CMYK - PROCESS - 0.000000 - 100.000000 - 100.000000 - 0.000000 - - - CMYK Yellow - CMYK - PROCESS - 0.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Green - CMYK - PROCESS - 100.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Cyan - CMYK - PROCESS - 100.000000 - 0.000000 - 0.000000 - 0.000000 - - - CMYK Blue - CMYK - PROCESS - 100.000000 - 100.000000 - 0.000000 - 0.000000 - - - CMYK Magenta - CMYK - PROCESS - 0.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=15 M=100 Y=90 K=10 - CMYK - PROCESS - 14.999998 - 100.000000 - 90.000004 - 10.000002 - - - C=0 M=90 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 90.000004 - 84.999996 - 0.000000 - - - C=0 M=80 Y=95 K=0 - CMYK - PROCESS - 0.000000 - 80.000001 - 94.999999 - 0.000000 - - - C=0 M=50 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 50.000000 - 100.000000 - 0.000000 - - - C=0 M=35 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 35.000002 - 84.999996 - 0.000000 - - - C=5 M=0 Y=90 K=0 - CMYK - PROCESS - 5.000001 - 0.000000 - 90.000004 - 0.000000 - - - C=20 M=0 Y=100 K=0 - CMYK - PROCESS - 19.999999 - 0.000000 - 100.000000 - 0.000000 - - - C=50 M=0 Y=100 K=0 - CMYK - PROCESS - 50.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=75 M=0 Y=100 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=85 M=10 Y=100 K=10 - CMYK - PROCESS - 84.999996 - 10.000002 - 100.000000 - 10.000002 - - - C=90 M=30 Y=95 K=30 - CMYK - PROCESS - 90.000004 - 30.000001 - 94.999999 - 30.000001 - - - C=75 M=0 Y=75 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 75.000000 - 0.000000 - - - C=80 M=10 Y=45 K=0 - CMYK - PROCESS - 80.000001 - 10.000002 - 44.999999 - 0.000000 - - - C=70 M=15 Y=0 K=0 - CMYK - PROCESS - 69.999999 - 14.999998 - 0.000000 - 0.000000 - - - C=85 M=50 Y=0 K=0 - CMYK - PROCESS - 84.999996 - 50.000000 - 0.000000 - 0.000000 - - - C=100 M=95 Y=5 K=0 - CMYK - PROCESS - 100.000000 - 94.999999 - 5.000001 - 0.000000 - - - C=100 M=100 Y=25 K=25 - CMYK - PROCESS - 100.000000 - 100.000000 - 25.000000 - 25.000000 - - - C=75 M=100 Y=0 K=0 - CMYK - PROCESS - 75.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=50 M=100 Y=0 K=0 - CMYK - PROCESS - 50.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=35 M=100 Y=35 K=10 - CMYK - PROCESS - 35.000002 - 100.000000 - 35.000002 - 10.000002 - - - C=10 M=100 Y=50 K=0 - CMYK - PROCESS - 10.000002 - 100.000000 - 50.000000 - 0.000000 - - - C=0 M=95 Y=20 K=0 - CMYK - PROCESS - 0.000000 - 94.999999 - 19.999999 - 0.000000 - - - C=25 M=25 Y=40 K=0 - CMYK - PROCESS - 25.000000 - 25.000000 - 39.999998 - 0.000000 - - - C=40 M=45 Y=50 K=5 - CMYK - PROCESS - 39.999998 - 44.999999 - 50.000000 - 5.000001 - - - C=50 M=50 Y=60 K=25 - CMYK - PROCESS - 50.000000 - 50.000000 - 60.000002 - 25.000000 - - - C=55 M=60 Y=65 K=40 - CMYK - PROCESS - 55.000001 - 60.000002 - 64.999998 - 39.999998 - - - C=25 M=40 Y=65 K=0 - CMYK - PROCESS - 25.000000 - 39.999998 - 64.999998 - 0.000000 - - - C=30 M=50 Y=75 K=10 - CMYK - PROCESS - 30.000001 - 50.000000 - 75.000000 - 10.000002 - - - C=35 M=60 Y=80 K=25 - CMYK - PROCESS - 35.000002 - 60.000002 - 80.000001 - 25.000000 - - - C=40 M=65 Y=90 K=35 - CMYK - PROCESS - 39.999998 - 64.999998 - 90.000004 - 35.000002 - - - C=40 M=70 Y=100 K=50 - CMYK - PROCESS - 39.999998 - 69.999999 - 100.000000 - 50.000000 - - - C=50 M=70 Y=80 K=70 - CMYK - PROCESS - 50.000000 - 69.999999 - 80.000001 - 69.999999 - - - C=0 M=30 Y=70 K=0 - CMYK - PROCESS - 0.000000 - 30.000001 - 69.999999 - 0.000000 - - - C=5 M=70 Y=90 K=0 - CMYK - PROCESS - 5.000001 - 69.999999 - 90.000004 - 0.000000 - - - C=5 M=90 Y=75 K=0 - CMYK - PROCESS - 5.000001 - 90.000004 - 75.000000 - 0.000000 - - - C=30 M=0 Y=95 K=0 - CMYK - PROCESS - 30.000001 - 0.000000 - 94.999999 - 0.000000 - - - C=60 M=5 Y=95 K=0 - CMYK - PROCESS - 60.000002 - 5.000001 - 94.999999 - 0.000000 - - - C=30 M=0 Y=10 K=0 - CMYK - PROCESS - 30.000001 - 0.000000 - 10.000002 - 0.000000 - - - C=60 M=10 Y=5 K=0 - CMYK - PROCESS - 60.000002 - 10.000002 - 5.000001 - 0.000000 - - - C=80 M=5 Y=10 K=0 - CMYK - PROCESS - 80.000001 - 5.000001 - 10.000002 - 0.000000 - - - K=100 - GRAY - PROCESS - 255 - - - K=90 - GRAY - PROCESS - 229 - - - K=80 - GRAY - PROCESS - 203 - - - K=70 - GRAY - PROCESS - 178 - - - K=60 - GRAY - PROCESS - 152 - - - K=50 - GRAY - PROCESS - 127 - - - K=40 - GRAY - PROCESS - 101 - - - K=30 - GRAY - PROCESS - 76 - - - K=20 - GRAY - PROCESS - 50 - - - K=10 - GRAY - PROCESS - 25 - - - K=5 - GRAY - PROCESS - 12 - - - - - - - - - Adobe PDF library 9.00 - - - - - - - - - - - - - - - - - - - - - - - - - -endstream endobj 3 0 obj <> endobj 8 0 obj <>/Resources<>/Font<>/ProcSet[/PDF/Text]/Properties<>/Shading<>>>/Thumb 13 0 R/TrimBox[0.0 0.0 335.465 155.905]/Type/Page>> endobj 9 0 obj <>stream -H‰”—ËÉ E÷ơù•÷c+ÙđÊ0„1àÙ7<0ƠÆóÿ€Ï%#««S]öÔɨxẠ̃Ÿ₫öeûô×/aûü§/Ûå·KJs#n1§½„¸½^Ŕ{miku¹o·K*ui0¥í©Œä¹—Öm$„¾¥\öd—½Ö´7}¹ücû7‡}úËOaû×ï—ăk*³åíë̃R®¡´Ó÷4bÜR,{k}¶1¶̉÷Pæ¥o/¯—Ï?oŸ~ú¶ß¿m₫yûzùj1ŽáF“WE!uüˆ ̣^Ø‚ÚØ«;<·ÄÄVƯgÁ {Ó¢«¹âưy„ơú­déml©¶}N¿£‰¿µîYçá+°ĂûÛåÛGWRj˜u¬kàÚăù{M¡­+™3ÅiWRC1=½’@¼̀Œ$·ÇjiÎ{lskm/U±Ñ̃;w’ú{³Î.…*¤•ă÷¹ßBçM?Ls¥¦{“|¾×¤Üë÷i#QFOb³í£Çm=QŒ¯doM•;cV!í¡ăpP=*ïqṆD"BƯ;·µ¸h3IÜiÏ#*eÔzDßçï5©‡´"³ÔüG²Dß°÷V3÷I@¿•xrYu³èôßØr {hu£˜Gf¦·ÎÊùår¸ÙÎl½́}-˜\pŸ{Ẹ́̀62U×ú‡gU›g¨Gó¦4̣<}û¤1U2çªM=SaO3œñ¯mƒ¶£T_m ²B?×MfÛH{êѬRê6‚®÷OªN¯Â…T“U_¬ï«^dÓÁ(ºÇjdÁĂ{o>® rZñ÷ûéûảÖFUŸl-̉IƠ>ë^IºoVë×¹FRÚ3a$<,‰IơH p±¨Ă§`D°ty–á1i˜C‘ß?L:÷p½öñ4ªL§²•¹g‡j:O¾–HN†•)ÍV¾»ÿ$b%°©Ç,Á A¤{`€:œ®r½—~đY”<[€£7Ä™³o§ ?åàâ=¥%„q₫~˜dáôdEúÿÓ¶ˆF)l™]щüçŸæJØô/n¿̃]ú ËÇ¢÷L“ªzjp~Ñÿ×´W¢$¯“M+é/JưupÉÑ+ï…å?^éơ+Bb*?'Jj™̀Lz"ÄûïQ+ Åq7Qÿ3Ùô5“ơÖÍÁûDâÊ]i+2Ĥ đ”0!¦e¤0—ĂÊ›Ơ´›¾¨ă*±B©q×ÍX—ÂTË–3È:@ñ>#GĂ¥+ÜÙa×+¥­1²³Œ—‡‰¡å¤[Ö³öû€¥v¬]VX7Ëç÷E¬‘z_NºAëøË^Îøº»“~ƠP=tßüyÆăĐv2 -2ự‹¤‡î sà‚øyKÆƠ(êRr(L²«ơàûÂy^u O{¥VÏÂU~ơ¥ X9‹›UÈ9}4·.-¢̉}gꥄl¦à«Ù́>,‹̉6Wqµµiƒ3º+·8_y·å¯_»ïÅ9*o§JEHo›‚ˆ”̃.‡³*í wĂW{o.0ª=Oq-WÉKǸ»LƯ.Ü’ºù0Ecæ­ÚA!åÁN(Çô«0,¿ÙÄ"¯Ănß¹/ ^t4À¼¤®?ü§”üç>™cÿ¦sµ–Ơ\6ZcTuq\âßv¯’nâm@¦5†¼b`p®gf’K Ư,F‚‘E\ «ßÖÄ«Ăß«Bn-öí°ç.S›é₫̀H'Ó vŸ¢,$:|­¾p*©ZsCáZj GP₫]ˆLa¸́ 3‰ç¼ơLÀ,ĂÙrĂºÂ¹jEˆ(U¿}Ÿ5‘^ZT]Éí¡#)ê¬Y´ ÜÔ!"¦eq–¥d™$Đè¹ÙÛMs„Yn©-¹™vÿ•6h¦jPD½YĂ˜ØÍ`U¶öŒöz=̀dt‚Ñí<‘IqV+»Øîµ;›Älô3½@đCœ] .V+»Ë\¯ø]v_§me¼oú¢ê¨)Ư%J<́Dæé6››ZØ+±8†ơ÷Ú­Û²8ñ₫ >e1`äÚ\€\=’»!eư₫÷…_bR4Qåƒ.ª5’DtđU;«•†gV/@^rĂ’äg`-?R^–?{!ÀQi>Ă1¨hñd•EÚúN̉C”!‚"O\œ´4æ#3ÄRƠAÁîÓXÂÄ7£Œ‘߈ï®̃;‡å´âÉ]]ùB!ºIà­–Ư©ºûøƠY’D‚™÷«Ơ”k"¡¾IÛnï'¯*÷é*“܈§½¾®ËrjË÷'Ê`̃\V2 cªÛà˜đ€u}[R›?±loB\[*Aè˜!Y-«?Èï@¨XLG%̀Go\À5a‚̃Vo†SÓ²‚°O.sàó½}–1í]đrY&ª~ª$«¡çÙª i—Ù-8´Ä½N̉ñ±§ĂxQÛ{3ÊTî,ϱ‹HT¶IáíQ i˜÷vøˆîZĐœzQIF?Tl̉[«•tûÑèợD¶‡%n­^Juóº¹STÖËPW(Áưjv¶à qW‘ fơoPă2VR†¡°'"́†–/’a½"B3‰1—₫íL¦ß’ô$[|™ X–ù–ăÛÀ!8g̀»¸øëjÙè̃PªHvªTưú4-ó²ªs„·ñ2¨%ƒÔ»™a»»¡½«?VüÆ -ÿzùü÷KXÉûợ_é»(¤s釄€B̉¸Àíx`#ØbÆ.¨kŒ“\®hM3|M#r‘ªid¨Qeh¤€‘"*5cC¼¸\C¸\}¸ 5:” -endstream endobj 13 0 obj <>stream -8;W:gYn=i,$q2r\SRu@P<4Ob!>I]P@j2$J21h+u1-ki,+JNKW:s`h6j``8Gd#'9CRfn5ti@ApLia(Mfb;< -d+f(*adDlCdA?(QY1"GK`kpeBM=4BhLi#N@J^2XF7sA-T@\'7>m/P?\Zg)29"op>]"^q~> -endstream endobj 14 0 obj [/Indexed/DeviceRGB 255 15 0 R] endobj 15 0 obj <>stream -8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> -endstream endobj 12 0 obj <> endobj 16 0 obj /DeviceCMYK endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 6 0 obj <> endobj 20 0 obj [/View/Design] endobj 21 0 obj <>>> endobj 5 0 obj <> endobj 22 0 obj <> endobj 23 0 obj <>stream -H‰ÔVy\TG₫ºê  xêø1x‹ˆ -ˆ‚‚ -ƒ²Y‰¬¢k\ÆhŒ$̃Æh4F£îz; "¢€GÔhV×ÑxÄï‰G<`ktu7¿ü·lÏÔ¼®êêï½®ïë :¦ƒëßfŒû›K%rÀœ”–˜^ot¦3 ‚Ǥ‰º_Oà4Yb¦”ôÑijY“€óJ‰Ơ=æí”Kw ơ·€áLjrâ¨Ë‘sd½©¨ææ”#~¡øSÓ2&…d÷¿p3flRâó+å₫@Óh JnZâ¤tĂç.@ë*’¯¿•˜–\m}Á)ñ›ZVúØñăsÇ̃ÚŸµ§KN?ums#ñÖÜÔàhXnh+ˆW₫) -TƠÀD|Wƒî…BïÊ«ëj†̉ç¥jc•Uä«ŸÛÆ8Çhû7yb²”}Bmñ¤GF8³- P9̣ï¦$›́=Âï·™¬np ẫ¢¶p'ªJƠ¨oSVuM]!p<'đ`ÂCyçlæDU¬8‡wđnu›;C“{w@8 Ψj²Ó5àWÔD-¸ ê:¨‹zp‡Á2,ǧX•äFjs{Áo‚Z¢ b‘€±X…Ïäù­Áj¬ĂVdÁ‚ĺÄ~âá4¾ÇœÅ8‡{(ÁO¸Oî¼—{qọ$Gª›ÂÁÜEe«C¼~Æùlá\̃Ă1|ˆ ø0I}¹ăxÄßpIƯQ79÷s4ïâê™z®®Ê.á…xŒÀe’}W÷U©*Q÷8› SxwSƠ¯jÚD䤪G¼Uöº-¡=Z£+º!L*c ÉÀ¤á8ªrT¡:ª¾Vß«oÔyuZ]TyФd„YÓe`gÙ;jZ¤Ê©>¥ct‘5väj\‹ë°‡ñ~‡3ù#^Ă'5Í_‹ÖFhIÚm¡q–±TwƠëèºQ7é¾z+=PÖĂôt}¾ÅËÍ«—ÉË׫¥‰L&S-S“§Éhjf4™MÉ>ÇŸjåå•t¬OÈ‚³ v`g;_A!f -‚y¼Vƒæªµ̉b´‘Ú8mqºñ¾ pÓƯơºnGĐ©A†AƯ×Ô/Œªü½¸ó+¼¬́E¹—,Û”‡•{”×oketZÙâWIa=ó²WX$ơí¤Îó»<@=缉7«;»FR}^vUđ]h-ÊĐV8ZÉ©wE'ØyÎÂ0á•]c13pSÔc•èÇjQuÂî­¢Yv) QhÈQ‘³¢ ?p6.‰Ü³é+ƒpÛ_ 1Ö¢ ₫7°Í° -°ͱ₫؆VØØ…ÈAv£rÑ;ˆ=èŒ<a/‚±!ÈG ;"_"‡‰#è£è‰¯Đ ÇĐÇñ5úàïè‹“ˆÂ Äà[DăôĂ)ÄáŸèà;LWŒÁ8!r̃ÅE›*Á +F¢‰(B®`®"בŒkøî ·ñ&îâ-üŒt”âOøăđăñx„?ă ₫‚2LÁT)ùiDunÁëÑ0?b4nØÖƠÊ«Ô¬‹¢_§•EíT¹ê€:¦N¨sê°:¥¾SûÔꂺD³h6µ£9Ô:PG -¤ N ¨!©éäE&̣¦ÆäC¾Ô„ü¨)½Aͨ9µ –äO­¨5µ¡¶@)ÓJ#)˜B(”ºR7êNaN=(’zR/êM}(úR4ơ£êO±GiÅÓ0J¤d¦$ÚI»(‹¶S6í ÚM¹´—öQ>åÑ* BºA7é*Ó5ºN·è6Ư¡{ôưL%t—îS)í 8@è =$ ]Á<ÆDüIx·ñ̀v6ƒídÀvF<Åô GQÇYü>Ïæxhơ‡¢•syZÄóy/äE¼˜—đǼ”?áe¼œ?å¼’?ă|NΧíäÄûƠÛ٢֫͆ đ7 Ω0QÀë/¬̀ßvưŸ·¥̣™!œ—71›Ú.|m|…Øb³Äf₫Æüăóÿk¼ ̉l-1eSË’ ±ÚblƠêh-Ey2…ÏkI©í¸vÁåùOÊÇf>&oÂMà«Đ£F 6tÈà„øq±ưûÅD÷êÓ»WÏÈáaƯ»u íÔ9°S@Çíư[¶hîçëÓØÛÔȽvMW—êU«84&…æá̃fƯâk¶h¾̃‘‘-l¾w¢_ ˜-º„"^ϱèf{₫zf¨d¦üGfhEfèËLåª!¨Es=Ü[·œóÖsƠà~ñ̉Ÿæ [îÚûQö¾ækwª‹ăå%3ôp÷Ô0Ư¢̀z¸%bbjf¸9LÖ˪êÜƯ»{̣¿ø®–Ø63<³Krù¾Eyci×£%E‘%QÔ+¸‘H™²*Gesi[6)J¤^”Ø -\ĂƠÆmÚ9ôP÷á\ NQ XÅNJ¡(êE{2^z(PÔÚ(j =ä’TQÿÙ%UÛH»Zjf¾æŸï̀́Œ5E›VTmPS;ÈÚ&îÁZ…éÈ o2Èl§Óª¬”)/¨Ó9%“æE±¨ahLÓ¥ÆTNÓ%¬PÎ膰}X½Ys¢ùR¤i,”O**[†AU6S­^U]5L̉jøü_`̣¢%éŒ! lrfo¬%'ªŸ" OüăY¤\GL’óSD«ÔÄ=7¼QGÀ ‚}¢H¹Ü¨ÉhêFNÑÛç?DrÏÿ¤¦÷¦„ÂBf1ưÁg”ëëÚ¾œ'C}QŸF˜i8³ +ÁÊŒ5D£T4-(d‘ ä<­PÛ¨¯µøNæÉdE»%³Ï´tùà¬^S™1HÀñ߈©Ö>¤µ÷ÙçÄ ±P5“É|•j&u…H€å›‚åƒî>X—ă°µ‘ñ2“₫xµ\ÛƯ˜¯nÊru-SZ¦:ÈÄB•ä•ƒ¼FmF¹ÈŸ§S¹Ñ$œEaăƯ$øZnSÆ×̣Ç•-'BµYåC8B•F‹›í S¶ø$h(CQ -̉†@TÓ 4̀Z~K†û“&5h€Ö®Ô0̉0sèRct̀ÙÀÀ :&k} Beđ/́µaÆæBq¹Z*̉•…üGxáDFÊ‘M̀˜T+YUmd”â)§tÜDq²û18‡nHƠM -²IA<Öó¥*…Úîî¬">âŸEȳ“đ;®¨–lüFé0ô;D%€©•2å*t,'MT³ …ĐeBµ€K]ô×ÆĐ\„Aˆ P¿ u£¨#tRe¥¨å²SEY2 a×uƒt¢x±ê&½Ú„u`•®̉ÂÜP^Ñ0YQw×̀+D•’̃6 J̣\ßH­¼,Â~h.j?+_"j+Ù́VỞ -á¥u[]F‰+ụZëj½̀íTmÀ(ø”+ëÀ;  \འ-Ti×_R5¹!ç`[¡¤5MˆU»4Q†_o„ 6›éa«ëøµrỘ&đ;+ÍÖv‰O=±(¡_˜ˆß¢gbơy@=‰EÍÏ£v ®VÍö/ ûËlß+)(dà“¾…‡}Ç$.ƒ6¸J É­\oñlfl -±éØZŒ 0[L,ÇXQêQœ₫ÍÅçá¸^ä|ÔÓ]¢‹₫¤dÂçñ:ÎÁú|$Lö0ưưɾ 9`â’ưư‰̃V†}đﯰ…vœÿÄÓÖát -Æ­±Á!Zœè=ÙNæ{^.§ÛĂŸưÆ8ü¹h¸üù…+/2ï“₫˜ạ̈„bC’s{%;½-ĂÑ¡TÀ9ëĩŸ< ë|†(Øä„kb»́¶Œ·¸P;{½XÈ2 6£Tê‰{(₫Ç·{ăÛ=Ư"q‰½₫f¿ß¼ÉP̉ïOô0@9 ‚§~€Ïäß}éÜ…Ü÷₫tvè¬pæîR×d&²vøçÆa¯/̣ă‹K·—“¦o í™JêÈÂ>§x¼¿û7öïp8wĂơ°SKN@ylÇSc!ĉH°: -J=I!lpd_C]åje)‹S"q%\ Í6›̀Ÿg/ơ8b#S]™•Cíw³o~'÷̣Ü+!3+®ßf× -s'få ₫óëï¾Úz°û‹<±ß_¹~êʯ.´E¢öUă;ơtG³_…;ƒNßƠùÆ -T‰˜Ûo­í>”MxÊj,„m¬mÈQØÇư—sb;A]ˆ½­ŒÆÖ;Ñï1$À§@ØEBÄÁR0–Æ—F{¦S1{÷[Ñr¾ÿnH>ṿtga#Á̃^ îîW¹µÅ¿Ÿ¹2×»¯#æÁÿêyI<öÍ3•ï¾1Ơíd&?SV›ăÁæäúVóMöwÈ -W̉΅—Œùæö¼I:j4ºQáEÎqÖVpƒ‡w.~Ø‹¹îeJRW{zû|&åGS•f§ÏëåD0ˆ±&O¤;øñ₫Á™¶©K©Ä­cK÷ß™Ê{ïèô¥—đÎÙÓ́áÎly`°œ ï´·4½eßÙŸ]₫Æ/6ää̃jrµ¸ôj{Ă»¦BU¹+Äa w¶cé‰8(àà‹Xâqpî àp3ûqĐƒvj„~dƒä[¡v2ÊDz Ẩœ @y¯P/E›ơ „{¡Đ¢% Sd;¢ưëé{öѳÍ6×XO·†/رÈÔj*µ:‰re*rW^ÿák«÷ÖGäơ{¯­|pvÿQ¹~º§çôu¥pưTOÏ©ë…µÚÛÙ́Ûµµ×k—³ÙË5ø®̃Üë½H6.gFgă,&³•ƒ=%•r¥´ơ‘Ṇ̃ÇÉ̃œai1wVç&[nŸke›Çf¬>3;è  גּ[’»yŒ̀̉W¾*û1âQNö!GÎbtÙíFÎgÊ‘ÓåÍZ¨û tA)»iÅ‚X_Àê3ÙMF«1.ÍC‘HDË×P|È=´=´LKpÂ9Ư?àÙ+1aƒ!æmü{w¾øí÷Uâ68¥;?¹#ø ÿa¿Üƒ£¬®~¾ç&Ă4¤0°ƯƠ 5@”Ç(/My ˆ@X» ‹aƒ»ËC[‚JmÁå©Æ -©%<´̉N‡QGS:vœi6Ụ́jÇj2´̉–„íï̃ư6|¤¶¤Ó™₫ÑI¾ùí9÷ñƯ{î¹ç»÷äs}÷>sñÍç·ơ Z½V„Û¬´í]X>ú óö¶7R3f„ÍÖ±æè2́^`Äî<é7¼G)ÎÛ˜ f^ ÀvÅUFƯv›g–0©zú]|θÁ5nÚ̃Ö¸å(£™¿ûûĂÆÆĂ "¯•„»—ưEzå©c^…ßQWƒùAÑ…ơ{æ l¤˜Olª?̃rε3vç÷¥ơ'y'ơ8¾?«Åz9«[²8×H?ûÇ̉`¿*Kœ™ÈäKƒ3•̣ử`œ—3#eö1êo”w=mßă²Ä~ -ù!̣0móäköCR̀;í)H±ư’ÚGQót†@O *”=Qö9bK̀³óß í÷ĂZ hœÏ,gM× wÀ °Ưă…,̉Ă₫̃ekî­ÖíÑKI½~?ø¢3ä%=_åP>ë>̀¡|Ù”¯ưhŸûÁÿ9´½Ê‡OJƒơ‘„í1Ro-×2l]°¹ß£ 9P¦™{$m•ñ̃›̉Ư¾NÖZû%è́—bk‡ZÍ´ÖI9ó(ÂWÂEÙÓe_ÎÆ+¡í÷32ó3Ö#FKfü6ÁF4¬g}¢Öh5]Z³Ơ"´Iª‡oÑï¼ù˜Ôk\F¦Yû¤¸¿ÆW{d*ôÏùÊá^OÛ'¤̃d_”OsàÛjåß+¡|ïGïµÚöÁ̃^–°ïù¬ùq²Ù [Yk˜̣.Öÿ>öl“ö ê×H½³¶&x]"ÖQäúu£í=Ym½Êw¿†1óˆ…·˜ócøă÷–¸§8'¥—BÙÓeŸ=Wlœ¢íl– †gá)_9k¿Ö!ăưL9ü -6Â&HĂfOGJÀ₫‚Í™÷Ôº=z+©×ï_t÷\ÖWđ\»Ï:‚sh_våk?Êç~´ÿs4y>|ƒùóeœu$ÍFäxk92s°Ÿ5†̣½’4HÜXÙfuGOỈ)¸5Êiƒ¤ŸÙGâæz©0oÅæá3ßÅîJ)4OĂ'p@JƯƒ̀7±[Ü}̀»€>}Ă%éN¢₫ÙKeg:¾.eæa²Ăê>k +¼2÷^ë ‘6Û+ó׺–ûÊÜr­+e^æ°¯îEXƠ¡¼̉W̃øÊÜ­Ë|åM̃|å °~mßDîG¾c6°·-‚¯ -Ík¡¯4*Üû‘»e‰Æ«SX1©€¨Â8"eP“VÚú_’Ô!‹̀©́ĂTöÁ“œcqh—æ-r•ñ{₫Ù¸;.ÇZ+kÛm¨fŒjƯ.Wñ=¬b/r²{kdHNÚăe ñ%ƾƠK¸=¿¸{ÇơînuƯŸ½7U~¡îYÇ‘~¹Ü‚¼"¢ï̀ñZo°0–ºC×»?çl;$qçÎ;•7t'iĂ÷Ïp†ŸÏÔ»(â?HƠ:rgÜ\Æù>8Á8#9g¿ÏY²™ú‡¥Ô*1êJÍ"¾£97ÎĐg>zKJ­e´©³{ ë«à›©•iÖøïUJ©CÙù -åă™·Í2ßUßÖ’́øjl³(sÖÁ³zœẾ{ -7wªsw1cùÔ™¥êweÏuîªóÇîÏ·;s{xg }ÔÙËyÑgÅOés5:ç->fϯÇÔgxæœ:çô™}Pú¨₫èµ^̀~èÉ¥₫TqjÆ̀Q½—oăGµŸWqü9ØÏ@Ư¼|Hïçp¾ßơ:çÊæGă¼½T¹œÚ˃́áo/÷Pÿ'Iîc¬×éÿ$±8Œº&xN"$̣4sCÄXƒs–÷·2Ê—¼|OÅ{¿Såm9&—l¤ÿyo¾2ô/J¡³ƒØ¹“»Qå?¥³₫ÀßẴ¿ˆÿÉœJ|V'“í*ê€ÿỷö­ø¶9ÂËnFroêüäëzß‚¬5Œ_ƒöÓÔŸfJÚ½~“ˆÇRWik/~"¯°2́sZ+i3_cöîg₫JeƒÎ½Z¸ŸÈE°a­²Á½ŸZô8{—kT®<{ -·ó>'•_…́.Æ2œăsơoƧ¹»KÅØL|³:+—Å×)/¾̃fƯ²ñåXô&kƯ!ä…ëĐă¬ñ!$sWq—üŸEÔœVú)Ÿa}·5ó½4gs -u×ê;Ö³Á],…n -ûŒ́|övê§p—́ÂÛ%¨bÔÚ¤ăô ‘ ëùVÁu¦™·d₫¦Î4ăÛ²Ôü­,Íë)/+Ü4~úÿ—w5ß^oüué.œç±¶[`±Ç˜èq§Bß܃y[w“}X[¿ö”­V½Üe_+3¬c²8‹[µrƒÔ1áƯ`#¤à€…P•0bÄ|µơ.¹ŸHu@FQ7̃o~¸ƒñªĐ«©‚’C”Ăc°5çlrÎ2s1göăœM3¥Øü.̀B‰úée¤í%¹™~7å0¾*PxÙ» PïΣnŸïƯ}ÔÿïM›ÜU2Äư†J±»@Ệûroô–¤X‡̣œeu—Tà·åîu¼ß—»ï(1Z,‹Ơ=䜒åêŒ'̃Ë́ŲRù_ß·£ÉĂșÖ-C­AÄ­ª/æÛ™¥ûÜéö”ˆ'æ¾̀Ư8û/.åè#Đ+t¼UK1A:¯SÏÿ÷Çpyf¯üóc̃ªŸöç5º®§ëézº®çÿˆAÙ"KÅ€˜̣Y Q̣ÊÍùEb«V)àV£“ÊC%¢•¿R2$ûWj̀đtK>c¬ót}«§»è»<= ¯ĐÓ SgLs§̉Û|ÚÓM)0=Ư¢₫Gn£ótư¬§cƠMvKHJe Ï`´ñ“9’:IBµ¤¨…–ú·Z\Jh!µ‡²IºÏưçy“ùOÛôûGë<œĂo,·¯¤¦Ñ)§†’ɾdG̉‘lK¶$-IS²$Y Xœ1'—ëϹ¯ó7K;scN‹Èûw:{ûæ!KO²JƠ ›Đ×ă]öj´Ëc’Gc¥Åh9̣G_F¡¡e³i³₫‘Í^r›½x®3µG²½ưô.zT}F:̃Đ[xÊYáë8‚9ù;́1̀Focy Ë0昷LÁL̀—÷ÛC]E^D]xt›åÑŸ>¹ÏΕ »Ë°­LˆÇ¶q;=Îwôm~Ms¢OdzñAü"Z†‰& XmIQDơWtÊ•O$øË·€ơªbiqCĂûdêßµæ̀ºúÀëÇƠ!óÚĐBÅ¿e˜ờf̀~̉ª~TÍd¾føĂ ®i»1í‡ Đ¿̉s]¯«m„«a‰ÛàbbEiÁ¬®B -đƠ\b*†b ú¢#Ñ-a Sà] íƠ®Dă›@Ŕé -endstream endobj 11 0 obj <> endobj 10 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <>stream -%!PS-Adobe-3.0 -%%Creator: Adobe Illustrator(R) 14.0 -%%AI8_CreatorVersion: 14.0.0 -%%For: (jbrinley) () -%%Title: (solr_logo [Converted].eps) -%%CreationDate: 9/10/2009 1:01 PM -%%Canvassize: 16383 -%%BoundingBox: 125 342 440 501 -%%HiResBoundingBox: 125 342.9834 439.4648 500.8428 -%%DocumentProcessColors: Cyan Magenta Yellow Black -%AI5_FileFormat 10.0 -%AI12_BuildNumber: 367 -%AI3_ColorUsage: Color -%AI7_ImageSettings: 0 -%%CMYKProcessColor: 1 1 1 1 ([Registration]) -%AI3_Cropmarks: 104 342.9844 439.4648 498.8896 -%AI3_TemplateBox: 298.5 420.3896 298.5 420.3896 -%AI3_TileBox: -136.9043 135.5732 680.1865 706.1187 -%AI3_DocumentPreview: None -%AI5_ArtSize: 14400 14400 -%AI5_RulerUnits: 1 -%AI9_ColorModel: 2 -%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 -%AI5_TargetResolution: 800 -%AI5_NumLayers: 1 -%AI9_OpenToView: -40 647.8896 2 1095 674 18 0 0 136 115 0 0 0 1 1 0 1 1 0 -%AI5_OpenViewLayers: 7 -%%PageOrigin:119.4551 -380.0898 -%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 -%AI9_Flatten: 1 -%AI12_CMSettings: 00.MS -%%EndComments - -endstream endobj 26 0 obj <>stream -%%BoundingBox: 125 342 440 501 -%%HiResBoundingBox: 125 342.9834 439.4648 500.8428 -%AI7_Thumbnail: 128 64 8 -%%BeginData: 8597 Hex Bytes -%0000330000660000990000CC0033000033330033660033990033CC0033FF -%0066000066330066660066990066CC0066FF009900009933009966009999 -%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 -%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 -%3333663333993333CC3333FF3366003366333366663366993366CC3366FF -%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 -%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 -%6600666600996600CC6600FF6633006633336633666633996633CC6633FF -%6666006666336666666666996666CC6666FF669900669933669966669999 -%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 -%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF -%9933009933339933669933999933CC9933FF996600996633996666996699 -%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 -%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF -%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 -%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 -%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF -%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC -%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 -%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 -%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 -%000011111111220000002200000022222222440000004400000044444444 -%550000005500000055555555770000007700000077777777880000008800 -%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB -%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF -%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF -%524C45FDFCFFFDFCFFFD39FFA8FD4FFF59275252A8FD25FF7D28FFFFFFA8 -%27A8FD4EFF277D847E277DFD24FF7D52FD04FF27FD4EFFA827A8FFFFA827 -%FFA8A87DFFFFA8A8FFFFA8A8FFFFA8A8FFA8A8A8FFFFA8A8A87DFFAFA87D -%FD04FFA8A87D28FFFFFFA827A8A8A8FFA8A8FFFFFFA8A8FD44FF27FFFFFF -%527DA852275227FF8452FFFF277DFFFF27FFA82752277DFF5252277DA852 -%272753FFFF522752277DFD04FF275252277DFF27FFFFFF27A8FD1AFFCFCF -%C3C2CAFFFFFF9AC2A0C3C9FD1CFFA82727522752FF5252FFFF5252FF27FF -%A85252FF7D52FF277DFFA827FF7D27FFFF527DFFFF52A87D27FFFF7D28FF -%FFFFA827A8FFA827A87D52FF7D52FD18FFCAC9A0BB92BB99FFFFFFA79392 -%FD04939ACAFD1AFF27A8A8FFFFFF27A8FFFFA852FFFD057DA87D7DFF2752 -%275227FF527DFFFF27522752F8A8527DFFFF7D52FD04FF27FFFFFF52A8A8 -%52FF527DFD18FFC998BB99BB99C9FFFFFFC293BB93BB939399FD1AFFA827 -%A8FD04FF527DFFFF7D27FF7D5252FF7D5252FFA827A8FFA8FFFF5252FFFF -%527DFFA8FFFF5252FFFF7D2EFFFFFFA827A8FFFF277DFF527D27FD19FFA0 -%BB99BB99BBA8FFFFCA92BB9399939993FD04FFCA9AFD16FF20A8FD04FF7D -%277D7D27A8FFFF2752FFFF277DFFFF52527D7EA8FF527DFFFF7D527D7E7D -%FFA827527D2752FD04FF20527D5252FFFFA8207DFD14FFCAC9FFFFFFC299 -%C2BBC1C2FFFFFFC2BB99BB99BB93CAFD04FF939393CAFD14FF7DA8FD05FF -%A85252A8FFFFFF7E7DFFFF7DA8FFFFFF7D5252A8FF7E7DFFFFFF847D527D -%FFFFA85252847DFFFFFFA87D7D527DFFFFFFA827A8FD13FFC9C19FFFFFFF -%9FC199C199CAFFFFCABB99BB99BB92C9FD04FF999393938DC3FD4AFF8452 -%FD13FFC9C19FC9FFFFCAC2C1C299C8FFFFFFC9BBC199C199C2FD04FF9ABB -%9399939393C2FD49FF27A8FD12FFA7C19FC1A0FFFFCA99C29FC1A7FFFFCA -%99C199C199C1CAFFFFFFA09393999393929368A0FD47FFA8A8FD13FFC9C1 -%C8C1CFFFFFC9C2C1C2C2FFFFFFC2C2C1C299C1CAFFFFFFC3BB99BB99BB93 -%BB93BCCAFD5CFFC9C2C1C2C9FFFFC99FC29FC9FFFFCAC19FC29FC1A1FFFF -%FFA7BB99BB93BB939392C2FD5EFFCAC1C8C2CFFFFFC8C8C1C8CAFFFFC9C1 -%C29FC1C2FFFFFFCAC199C199BB99BB99C9FD5CFFCFCAFFC9C89FC8CAFFA8 -%C89FC2A0FFFFFF9FC29FC19FFFFFFFA8C299C199BB999999CAFD04FFCA9A -%99FD56FFC8FFFFCFC2C8C8FFFFFFC8C8C1CFFFFFC9C8C2C8C1CFFFFFFFC8 -%C1C299C2BBBBC2FD05FFC9998D93C9FD26FF7DFD15FFA884FD16FFC9C8CA -%FFCAC89FC9FFFFC9C8C2C8CAFFCFC89FC8C1C9FFFFFFC899C29FC199C1C3 -%FD05FF9A939393689AFD25FF532752FD14FF7D27FD15FFCFC8C8CFFFFFC8 -%C8C8FFFFCEC7C8C9FFFFCFC1C8C1C9FFFFFFC9C1C29FC2C1C2C9FD04FFCA -%999993BB939393CAFD23FFA8272827A8FFFFA87D7D277DFFFF522752A8FF -%FF525252FF522752527DFFFF7D2752A8FD0CFFCF9FC8C9FFCAC8C7C9FFFF -%C8C89FCFFFFFA0C8C1C8CAFFFFC99FC29FC299C2CAFD04FFC292BB939992 -%99939399FD23FF7D27FF2752FFFF7D27527D20A8AF7DA85252FF52527D53 -%FF7D277D5227FFA827A85252FD0CFFCFC9C8CFFFFFC8C8C9FFFFC9C8CEFF -%FFC9C8C8C8CFFFFFCFC1C8C2C8C1C9FD04FFCAC299C199BB99BB93999399 -%CFFD22FFF87DFF7DF8A8FF7D27FFFF5252FF7D7D277DFF277DFFFFFF5252 -%FFFFF8FF52275252F8FD0DFFC8C8C9FFFFCEC7CFFFFFC8C8C9FFFFC8C8C8 -%C9FFFFCFC1C89FC8C1CAFD04FFA0C199BB99BB99999399939399FD22FF7D -%27274C272752FF7D27A8FF27A87D27A85252FF27A8FFFFFF7D27FFA827A8 -%7D27FFA8FD0EFFCFC8CEFFFFC9CDCFFFCACDC8FFFFCFC8C8C9FFFFFFFD04 -%C8C2FD05FFC2C199C2BBC199BB99BB99C3CAFD22FFA82752FFA8FF5227A8 -%A8F8272152FF53F87D2727FF7DF87D52FF5227FFA8F8A8A827527D52FD0B -%FFCAFFCFC8A6FFFFCEA5CFFFCFA5CEFFFFA6C8C8FFFFFFC8C89FC8C8FD04 -%FFC99FC19FC199C199BB92C2A1FD26FF7DFD05FF7DFF7D27A8A8FFFFA87D -%52A87DFFFF7D527DFFA87DFFFF7DA8FFA87D5284FD0BFFCFFFFFC9CDCFFF -%C9CECFFFC9CDCFFFCFCDC8FFFFFFC9C8C8C8C9FFFFFFCFC9C1C8C1C2C1C2 -%BBC1C2CFFD30FFA87DFD25FFC8CFFFCFC8CFFFCFC8FFFFCEC8FFFFCEC7CF -%FFFFC9C8C8C8CAFFFFFFC9C8C1C29FC29FC19FC9CAFD07FFA1A0CAFD4FFF -%CEC8FFFFCEC9FFCFCEFFFFC8CFFFFFC8CEFFFFCEC8C8CEFD04FFC9C8C2C8 -%C2C8C1C8C9FD07FFCAC3999393FD50FFCAC8C9FFC9CEFFCFC9FFCACECFFF -%C8CEFFFFC9C8C8CEFFFFFFCFA6C8C2C89FC8C2CAFD06FFCAC999BB929992 -%99CAFD50FFCECEFFFFC9FFCFCFFFCFCEFFCFCECFFFCFCEC8CFFFFFFFCEFD -%05C8CFCFFD05FFCFCAC2C199BB99BB99BB99FD26FFA8A87D7D7DA8A8FFAF -%FD23FFC8CFFFCECFFFCEFFC9CFFFCEC9FFCFCDC8FFFFFFCACEC8C8C7C9C9 -%FD05FFCFCFC2C299C199BB99BB99999399CFFD22FFA87D2727F8272727F8 -%27275252A8FD16FF7D52525227A8FD06FFC9FFCFFFFFCFFFCFFFFFCEFFFF -%CECEFFFFFFCECDC8CEC9CFFD05FFCFC9C8C1C2C1C299C2BBC199BB999999 -%FD22FF7D27F827F827F827F827F827F82752FD16FF52F827F82752FFFFFF -%C9CFFFCFCAFFCFFFCAFFCFFFC9FFFFCEC9FFFFCFC8CDC8CECAFD04FFCFC9 -%C89FC89FC29FC29FC199C199BB929992A0FD21FF7D272027272720272727 -%20272727207DFD16FF52272727207DFD04FFCEFD09FFCFFFFFCFCFFFFFCF -%C8CECFFD05FFC9CEFD04C8C2C8C1C8C1C2BBC1BBC299C2C2C9CAFD20FFA8 -%27F827F827F852527D52522727F82752FD16FF7DF827F82759FD05FFCFFD -%0AFFCFCFFFCFCEC9CFFD04FFC9CEC8C8C7C8C8C89FC8C1C2A0C8C2C9C9CF -%CAFD25FFA8F8272727F8A8FD06FFA87D2752FD16FF5227F827277DFD12FF -%CFCFCFFFFFFFCFCEC8CDC8CDC8C8C8CEC9CFC9FFCFFD2CFF7D27F827F827 -%FD0AFFA8FD07FF7D5227522752527DA8FD06FF7DF827F8277DFFFFFFA8A8 -%7DA8A8FFA852275252A8FFFFCAFFFFFFCACEC8CDC8CEC9CEC9CFCAFD11FF -%CAFD21FFA820272727207DFD0FFFA8272720272727202727277DFD05FF53 -%272027277DFFFFFF52202727A8A8272727202727FD04FFCFCEFD04CFFD0F -%FFCACFC9C9C3C9C2C299FD22FFA827F827F827F827527D7DA8FD09FF7DF8 -%27F827F827F827F827F82752FD04FF7DF827F8277DFFFFFF2727F8275252 -%F827F827F87DFD0CFFCFFFCACFC9CFC9C9C8C8A0C8C1C19FC199C199BB92 -%C2FD23FF2727F8272727F8272727F85252A8FD05FFA82727F8FD04275227 -%27F827272752FFFFFF52272727F87DFFFFFF522727F8272727F8272727A8 -%FFFFFFCFFFCFCFFD04CEC8CEC8CDFD08C8C1C8C1C29FC2C1C199C1CAFD23 -%FFA8F827F827F827F827F827F827F87DFD04FF2727F827F852A8FFFFFF52 -%27F827F827A8FFFF7DF827F82753FFFFFF2727F827F827277D532752FD05 -%FFCAFFCFCFC9CFC9CEA6CEC8C8A6C8C7C89FC89FC29FC29FC199C199BBA0 -%FD25FFA8FD04272027272720272727207DFFFFA82727272027A8FD05FFFD -%0427207DFFFF52272727207DFFFFFF522727202752FD09FFCFFD0BFFCFFF -%CFCFC9CEC8C8C2C8C1C2C1C1BBC1C2FD28FF7D522727F827F827F827F827 -%F8A8FF7DF827F82752FD06FF7DF827F82752FFFF7DF827F82759FFFFFF27 -%27F827F8A8FD0AFFCFCFC9CEC9CFCFFD0DFFCACFC9C9A0C9A0CFFD2BFFA8 -%7D52522727F82727277DFF5327F827277DFD06FFA827F8272752FFFF5227 -%F827277DFFFFFF52F8272727A8FD09FFCFFFFFFFCFCEC8CDC8CEC9CFCFFF -%CFFD3DFF7EF827F827F87DFF7DF827F8277DFD06FFA8F827F82727FFFF7D -%F827F8277DFFFFFF2727F827F8A8FD09FFCFC9CFFD04FFC9CEC8C8A5C8C8 -%C8A6C9C9CAA8FD2EFFA8FD0AFF532027272752FF7D272027277DFD06FF7D -%2720272752FFFF53272027277DFFFFFF5220272727A8FD07FFCFCFFFFFCE -%CECFFD05FFC9CEFD06C8C1C8C2C8C8C9CAFD28FFA82752A8A8FD07FF2727 -%F827F87DFFA8F827F827F8A8FD05FF52F827F82759FFFF7DF827F8277DFF -%FFFF2727F827F8A8FD06FFCFFFC9CFFFFFCACDC8CECFFFFFFFCFCFC8C8C1 -%C8C2C89FC89FC19FCFFD29FF2727F827527D7DA87D7DFD0427F8277DFFFF -%522727F82727A8FFFFA852F8272727F8FFFFFF52272727F87DFFFFFF5227 -%27F827A8FD06FFCFCFFFCECEFFFFCFCEC8CEC9FD05FFCFCFC8C8C1C8C1C8 -%C9FD2AFFA827F827F827F827F827F827F827F82727FFFFFF7D27F827F827 -%F8272727F827F827F87DFFFFFF7DF827F82753FFFFFF2727F827F8A8FD06 -%FFCACEFFFFC8CECFFFFFCFC8C8A6CECAFD05FFC9C99FC8A7FD2CFF272720 -%272727202727272027272720A8FD04FF7D27202727272027272720272752 -%FD04FF52272727207DFFFFFF5227272027A8FD07FFCFFFFFFFCDCECFFFFF -%FFC8CDC8C8C8CFFD33FFA852F827F827F827F827F827F82727A8FD06FF7D -%27F827F827F827F827F87DFD05FF52F827F82752FFFFFF2727F827F8A8FD -%0CFFC8CDC9FFFFFFC9C8C8C8C1C9CAFD33FFA87D7D525228522752527D7D -%FD0AFFA852522752277D7DFD07FFFD05A8FD04FFA87EFD04A8FD0DFFCFCF -%CFFFFFFFCACFCFFFCFFDFCFFFDFCFFFDFCFFFDFCFFFDABFFFF -%%EndData - -endstream endobj 27 0 obj <>stream -%AI12_CompressedDataxœ́½k¯É•ù½€úg>4 aFd̃/C̣Đ–Ưêt±ƯÓ0 -ëH¢›E–yévͯŸX+̃gE¬½O©¶́î\ •*ấ̀½wfä“k­Œgç_ư¿úÍÏn¿yûûûŸÍ†›/¿ø«¿zúî₫Ň·ï~~ăƯ7¿xưúăûï¬ë'¿₫é͸<́U·¿8¾Ö+ÿăư»÷¯̃¾ù¹ÿ­₫ơ¹­ÿ“ÿúûw¯̃¼¾ÿ₫§7?ù©ơ₫öƠ‡×÷¥ÿưÛ×ï¾~ưöoo₫îéÛ7ÿpÿîĂư7ÿåÑưwïï_¶÷́Ṇ̌âóñ8<†á¼>Œ7¿ú¥¿äÅ›xñ₫ư«ÿ¯¼`Üæc¶Î'o?¾ùæƠ›?>yûßKï´̃̀Ët³,ĂÍ:Œö÷÷ê×÷ïzÑ£ó˜—›e>-Ûr”—e:lgo_~üö₫͇_½{ụ̂₫ưû§o_¿}÷₫ç7O¿ñææ—/₫X₫̣âæoï_¿~û7O^¿xù÷ư:Ïß¾ù`¯}ûî÷÷¯íÿ§₫ûgÕ¾₫¦åßÜßsÿÍ'_û‹ơëç¯^ß—ûí‹7cƯ×·¿§¯Ÿ||ơú›¿ùøíïïË~Ÿ·Ưûç¯ưó₫î}ù ¶Ựß̃¿ư‹oK×oî?|({¢¼¥²§¿üÛÿĐͲ{´üäï~}ÿÇW>Êaù/?eăï̃~÷í‹w_60‹vä̉íÈå<ǹéơ¿½ÿö»×å˜ú®ŸÊßÖ›eÍåMV(ßƠ_ü³q̃Ă2ߌóúhƯçéf;†Gă±­7û°=ǃoÜØư?¼ºÿÇŸßüÍÛ7÷Úw·ï>ü¦™2*†úoưé×_ß¿ûƯ›WvFï;ëÎûåÛoî_—Û6ñüơ ßg¾ŒíßzÅo_¼ûăư‡2̉̃¾₫øÁÏ#̃¥ ¿~ñưư»îM¾úî₫Íoß₫Gÿ¨?+cu[vßg7SÙ©çz³íËÍxÔw™·›q\ăG₫­­Û¦lC¼…í’¿úU9Đ_½{ơÇWo~>帬ëxó³¹́¼á8†ûîƠ7m0́ÓÍQÿå_ëÑÑưïäúôe_|øpÿ†¯SâÓ_vĂjxôËß؇¸{óÍÓ·ßÚayï'jMoÊP+Đ_[ĂÿV6ññ»/¿ø»/¿˜ÏÇÿíăÛ÷ïË_ßßœÛă?¾{ñ÷å̀=ß}|÷¶́”¡{Åï_¼¿ü‡rØ^½©½ßü¾ö•SôƠwï_•·üÍ‹?₫ñ₫₫¯üưñËWïʘùĂëûÿ₫ø»‚¢?½ưø₫Å›oÿæå‹woß<₫c9³|Û¯ïÿđáñWweجuÛÖQ¶çÿ]öđŸ>Ä[úKiÔ?ư₫céưđø₫Í7/̃ÿéñư·₫ÊI{ÿ¸œWßÜÛ‰T6ö₫âMëÊoïó¶ ³ïf\ÏÇûÍ«ûwå¿¿yü₫»/ËîØ–Ç/?¾{wÿæå÷¥±=₫ư»·ÿæ÷/Ê ?nÇc^ÿøåÛï¾×6ß}ó‡ûo_½yơ¦¬¾OË₫ợÅë7o?<₫Ó÷ßưé₫Íăw~̉—¿yüí‹—ö±Ên-éñwå‚PÖüø₫ñ‡|û₫cÙi¯̃¾{üáOïîï£ơâåÇ÷¿ưXïüØû¾yY¿oíåư7¯^¿~Q¶TÎÍX£| o_¼ùñµ¢ă°?₫·/ʵáưçŸ^¼₫C}u¾¿Ïéñ­‡²©Ûú·ƯѼ­û÷6¾ư­}Ç·wŸê#<¾óƠßùÊe+wƯêw±̃/ê«~QßâƯk~¯¹ûđ§ÇăoX6óU]᫺ÂWƯ -_ƠÏôU¬÷íÇ×^}÷úûÇ_½mcâw|¡ßƠ•×­ü»Xëoëû§·ïÊh¹/׃7e¨½ü¢¾ñ ¾Ï‹níơ­_ÄF^øîxqÿø%»ă¾®~_·~ßV.›ºơ^ƠW½ª¯zƠ½Å«xÍ}Ùoê¾­/ËgzÛ­đV/‰ơ¾yơ¯¬£îŒuƠơ>¦Ïó1Öù¾₫ùƒïŒïé₫̣‹ß̃U2®ÿ₫ëß¾/W…îb0í ¹{ọ́­E?¿ùº^jăêûwƠóX¨Ôưíÿû¥¯]˜Åº7¿}÷ñ₫·ßwÿg¿™_×ó5>̃ÖÛû×}î¨oöuy÷_•ăûÁOÙ¿ùÎÿv|ư«×Ëÿí»·¿ûÅ›?¼ự‹ŸÔ ï·ïÊH)›úê÷ÿơ₫凯©£ư×o>¾úpÿèÅ«ï~ú‰M•k»û›úײº7ËÿÿÁ₫ÿsÖvÿ‡ß´ Ỗ»&¾~û]·áè)Cÿæ?½x÷ƯçlüW¯_¼yñîÆÿÛ₫ëWạ̊«egµ­·¾ÏÚ́‹*`¡ûûØjm^l±v~Î&óư·¿ûúƠûocƒ}Oü÷g~ºÂË7_½©óƯÇ÷ºùíÛ·¯cËz₫Ôöü»u1o/~è Êÿånüé‹}÷§W/Ú₫7úu?k½ô½₫Đ;æ?Å›]¯ñ/ă}lGÿáƠ›oÊ*¢¶ç̃~û%u7¿ùÓ‹ï¬Û^ù¼{eÛîjǾÉø³Ÿ•øèæÉ›œÿö]!ß|(yêï̃¼yñíư77T×ÍV¶óPo •ç›'ßxÈúÿ|ùÅđ₫ç_äǼ”-Ùå“[œ₫Ü¥lñÇ_1ÿ¹KÙbîY₫IËÚ–²Åơ“ËöÙË>”4Đ₫ưÉåøär²”-?°Ü₫ẹ̀䇖²Eûÿ§?²<«Ë—_ü›¿ü9öçßÏ?ƯØøÑ#₫Àư#S¶xüÀ¾nụ̂b•åNËóºÄ)ÿüÙ?“–YËâ˪eóe÷åđåôåÖ—'¾<ơåÙXOù÷/ÏÇçÚ1uóöḮËR–Ơ—­,{Y_βܖåIYụ́l*[œîỆ|zî;Ó> mÄ₫Yʲ–e+Ë^–£,gYnẸ̈¤,Oçge¹+Ëóù¹ïøÑ̃úË/lÅeYÖ²leÙËr,gYn—'eyZ–gË]YûáưĂÎọ̈u]·²́ëQ–s½-Ë“ơé—_¬O×gëƯú|}^ í°©|(ÛøºmÛ^–c;·Û²<Ùn϶»²ú²—M–—íû±Ÿûí~ûåû“ưé₫l¿ÛŸïÏËÁË¥¼mÙÔ±Çq·Ç“ăéñ́¸+Ëó2$JF\¾₫r®çvîçqçíùä|Z–ggI†Îçe¨ŒeÏ·Kùà[y›ăö¼½½}rûôöÙíƯíó2€Æ'3çØù₫ç[¼ưó—²E₫ûÉg.Ot)£­́«‡—»—ç×K†̃—_øÿWËt±̀iỶ²öKÙâÖ-{·Ửï¨öÏ“nyÊR¶ø,–»X×¥µN÷'S,³–EËZ—/¿x²iÙµZ8ơCđÏS_i¹óåy]*Đ¾ü¢Bæ©ĐđtöÅOÖ§«/›/»/‡/ơ°Ö/Yߢ₫ó̀–²Å»²<·E°}1́8: <[}Ù|)'ä³Ău‡Ù®,[|ö́Ù]YÛâØ5:À C†”»‚‰»ÍϱÁÂÏ¿úúÉ»zIëqƯ£ź } èÂs9ƯF´/è€té†ê -k» h—LøÁ_ư@~XŸø¼ÓưP-~xv?$·~Ù®÷]>ù^}÷–[vbƯ­O}g>÷8ùÎó]w·̃e|>)GÏööƯƯsÛYÏưñ|1?wjÚ¹hă÷Ë/́Û)Ëóºë­ˆMÑ_×û+vmî¯ÅùúÛ_wŸé0>tŸÖ.J¦g9ŒqHÇ[ÿđ ’nxYèѦ{Û;>ÊƠ¤´ËK¬ú₫ơ¿âÉ{ÛĂZ₫¼×ʾ^²l»ó°ï–}ZÛ¦>çÅu«ăj÷ünÁ¨®e¡û\?̣ÛÆ“g-i#)ûñ×úĂ©Ü_¿zsï…ål£er×åKM]"÷üy/ÏÊØyRFÑYÆÓ^FÖZFØ\FÚø|(Ă鮌¿§e$̃–y”‘¹•º”±:•1;”!wWÆđÓ2–o .Ê¡,#|+#})#~*#(è¹+gÂÓrFÜ–3ă(gÈVΔ¥œ1S9s†‚¿»r.=-çÔm9·rmå\[Ê97•“Ú..Ï ¨Ÿ¾=)p? ö÷rIXË%c.—”ñv(¤»Âª§…¸·å"{”À`+ÂR…©Œ¨áx^‚‡g…•OÊ5ă<Êg,ÁÅVÂŒ¥„SC9•î -÷†ß–àä(AÊV‚•¥„-S¥Ăö¼3Ï -yŸ”«ÑY¢“½„c ö¶ø-%œÊy4”Sÿ®Pûi¹"Ư– ̣(!åVHµ~Mål -çî -ỡ–Óè('×VN¸¥œ‚S9)-!.‡³œ¸e——Sº́–r²oåä/o^ 0ZÊlÔÈ'̃C£:wÛ@[W{]êüœá₫#ƒyzh0O6˜—¾*Ñ“,S¬ăWÏ® j5b9«́Ö’XƠ.;í‚Ó.7eí³´l \^È.3‚‹œ ,w)+”ºL)7XÊ@Y#C¨9BÍjp*Øz¢lá©2†3xÖ ¼¡æe)¹Cưȳ–úϪe‹|¢æu9µÎE°d¡KÙ¢‚˜%²[­¿ùi°ú©°èư&?%lüÄxî'ÇŸ Ïôø‰rë'Ë©æĐ·ßüÄYưäYüµ×êô½iûU{¸îíºçŸøéeK¹xú±©G©±ÍO¹ƠO»ÅO½ÙO¿ÉOA[üø× C£¢#>VÊxªJCơzWG×Ăzu¬×ʺÈXëđ_o‡ç~R?÷ûÎOîº!Ôr'¯n–ƒ+3/[ÜZ¥FáÏ”ÿÇ\2°|åẬC`.£ê -ÎÎÏ= {D¤Ë=;P7T7Xƒk¶F̃ªWÇÚ 1棫Œ§:¶N§M#iÂGFÍÓ+§€¾ÇèđRÆSŒŒ¢íÏ»̉|ÆuFW™̣>ă:óđU&Z«ăùp,?qß9†GÇïâ!âb ¯Ïª“Ătu€Í÷:.ï’£ƒqqîÀ[•Hîr£ ¶8Æv>SÄ6D7Ä7D8Ä8D9%Îñ(‡8G‘±NIºˆwˆxˆyˆzˆ{ˆ|ˆ}£j‡/»–MËê ø­0tl)œæ„î‹Ç₫ÔO ¤’ÖR×·©ÿ́¾l¾ø)́ë奂ÑÎÔ²ÅQ8ñ¬¤œµw~æ>ó³÷©ŸÁOü,¾ơ3ùô³ùđ3z×Y½y}h­—…/¿p”ẂV4VH=÷åΗ°Ö”¸&âµ\PwMưà»Ó üS± -‹“av:LN[VƯrVÜƠ”¸&ƯJøki¢ị̂²3JĐW?rƯpư§^̀*S&ç-Ó—çbŒQæ™®ˆơ*y»Ü2#cú±e₫äBVNÛößiùÔ??X.[ÔơçËƯ.­~û,Jựcµ̣§ƠØ»¥́Í­ÓÿPuỵ̈úx\Ô¦/¯×Ëß­H÷A₫M+BåÊ …O:ö0L8Ôu&ñ‰×P¤R\«Ä··=£:\Fú¨êđªÊđY«ÂÔ„½"\ëÁµ\kÁOüÜ|•`êÀålŒ:đ×€/+À?\ÿ}r>ơªæƯgÖ§-訜S0₫Ù,æt¥œK´[†ơh¾ “ưkrĂUáG^¤‡ưq²ù‹ûÔªó£a>nöñÑ\ˆƯW8>ăƠu»ÓÉ;ŸSÿÎóđÈ®Ư6?ơʺ½Í^dolE^4=²±Ơ—s>ñº?3—üuù¯¯/ -#×åM₫waäF>§0̣yåÏ/Ÿü æé¡Á<]æOÜ´₫ộđÍ́¿ạ̀É-₫x0đÀR¶øg¯óă7ç Rº柘áüh~óÀåüSùỊ́ƒr]ÄËIyyÿÔEüKx/—s¿„ÿ“/àO//̃nŸqû6n;¶Û‹ífb»uØn¶„ơY»è7Ë©È-Àvë¯Ưôk·úÚM¾vƒ¯ƯÜ‹{~Koènêµzív^»™×nåµyơ¾[w¯\H>uo›xgÜÄ{7ñê?Ư-¼’×›xcÜÄû‘Ûx¶ óéñÈxÚ¥ºŒ×µ¿è÷—̣O½̣“whr(ô‰₫™4½èv<<ÿíØ®ˆú#$øŒäå6­ØâÿZFz¹¸®ü®LÚNܧư¡âä?)0*ÑĂÅÉëÀè3C#/CäÂd~(Ạ̊¨dH“¿h·¡±hˆ”¿ó+•-xßQöziîĂèíc¯ÍryơmGUöuöĂ:®«ow_ü¨Nă~xÓ¾¯µKˆë”Qè‡z¶ÓÆÚÇ1ù¡_mg”ö¶{»ŒÀn¥u÷•ÊiµÜt)—]o‚û¼´W3??K”R_´ø‹Îs¯o4”ob§¬iîñFĂà'DơÓờ[Êx̣¯T?Y·¡í†yYË–Ê°ÝÊq-ßÉÚk9x68öXÛăgƯÑ2ú*áÑéÛ˜́P•ÿÊXÚ%N™ºqzø•o?Û‹Æíô•†}óÓê<ÿû0[œ%@ñïyÓTψÅÏñ£œ -µ½z{/¡`¬T]ù%Îóu¿”u+‘½î‡2Xà‚Åz̃WRnM‰ú¼=§·×í¨ +CÙWznÿZ—rB¬´ÎiZsœK{(ákf uNơcû_ËÙ±íÖ—-1°ôÛ̉híqm ´ö¼^0pµSu Úk¦µ1ĐÛCf owo ,í -u1ĐÛë­Ó÷¡hísj ´–`73Đ¾å24ú®K{Kg!rc`i—½̉¸Úyºe®ká̀W›~84–ö2 ,Ûp6–v È×W cf`é+Qú ,í2íïÇyÁÀ̉9û@ ,íußK»ä> ,ǹ×½ÄjK0ĐûxÁ@ë<¶Æ@o—ñ -K{´/œ hÆ1 XÚ–†­= ´•Î¡A°ÛˆAĐózAëçAÑ̃ èíí‚̃97Z{í+–s2CĐöç~4úm¬óË :ưöK…`iZFô18^@Đê>6–v‰QKÛWN´!o[‚Nœ½AĐÛǽólôóhntB ‚F ólôöÑ híă|‚%£Xæ33ĐúÖm Z»\&‚Ö.‰i†`霷a Z{€`iO5––®±dóÁ@k—C ´övœ™Ë₫́Ïë9₫¼]G]àÏûÆ%đgí±’Ëñgí¡¾EĂŸeYç9₫́E5"túYs¯ad£Ÿ}½}>ƒ~¾Ï–#èWÚÛ8™~¥ój§Ó¯´Ïr1 ú-v‚S¢_é›O‹ù*₫Js«Đuú•æ¾Ơ˜«Ñ¯t˼ư–ă‘ÍÀú•öR—=đgíy[6¼×mÏø[́â·î??'†3đçíñ̀øóÎy üUø?‡Ó¼%ü9Ä*̃ÆÀŸµ§Đ üÍÇi°u¶4Ø_̉¥ÁÖ¾JƒKgJƒ­Ư§Á¥}•[_Ÿ—vJƒíïWiđ|¤4ØÖéÓ`ßÆÉ}]lí> ööel}lí> ¶w•Û·îÓ`ßu]\Ú×iđ|æ4¸´S<Ÿ×ipéë̉à̉Jiđ2<—Δ—vJƒKû* ^†œ—vJƒKû: ^Æœ—vJƒ—ñ4¸töipiöi°5¯̉`ë́Ó`kwiđ2>—Δ[»Oƒ­}•[gŸwq¤Á˘Ó`Q—{û2 öÎ. ö7ể`ÛèUl»³Oƒí¸öi° •«4ØÆT—û/̀ui° Á«4ØÇi—[»Oƒmp_¥Áó‘Ó`?#º4ØÛ—i°wvi°µû4ØÛi°ơơi°·»4ØÚ§Áóø@l]lÍ> .íë4Ø:û4xrlíË4Øf,ơi°µû4ØÚWiđ<¤4Ø^Ó§Á̃¾Hƒ}»]<9 ööel}lí> ¶^¥Áö-û4Øw]—Ïăiđ<æ4¸´S<×iđ<ơipi¥4¸´¯Óà̉™̉à̉Niđ<_§Á¥/¥Á¥̉`ûûU\:S\Ú) .íë4¸töiđ¼¤4ØWi°uöi°·»4x^Hƒ­³OƒK;¥ÁÖ¾Jƒm¥> î6b´æU ¶öU\:Slí> .íË4¸t¥4ØÚ}lí«4xÁŸư¹Oƒ½}‘{_—[»Oƒ­}•OsNƒíE]lÍ«4ؾ^Ÿû>ë̉à̉¾NƒKgJƒK;¥ÁÓr—¾> .Í> .Íë4¸t¦4xZs\Ú×ipéLipi§4¸´¯Óà̉™̉à̉Niđ´=O[Nƒ§-¥Á¥y[gŸ[»Oƒ§í4Ø:û4¸´Slí«4Ø:û4¸ÛˆñÏWi°¿{—[»OƒëJi°wvi°¿Q—Ûn¹Jƒm‡öi°Ú> .‡₫* ¶QƠ¥ÁÓ’Ó`„—i°Ô. ¶vŸÛđ¾Jƒ§9§Á~Nti°·/Ó`ḯ̉à -Ÿ–;œ.̉`‡X—{»Kƒ­ưp<®¤ÁÖÙ̉`I—[û* .) ¶vŸ—öUl}}\Ú) ¶¿_¥Áẳ`[§Oƒ}i°÷ui°µû4ØÛ—i°uöi°µû4Ø>ÜUlߺOƒ}×uipi_§Áă–Óà̉Niđ¸]§Á¥¯KƒK+¥Áă₫@\:S\Ú) .í«4xÜs\Ú) .íë4x ööEl}}́í. ¶öĂiđaYđ²g–-<́¬ ‚‡¥Áë™!xX\£NụÁÓÜ- ^3wKƒ§- ¸ûØ‚»§Ák†à̉àƯ²àÊ3gàîYđ˜h›5ÁÀƯ˜®´ØhmL»ïÂ9hgÿTO1gànYpedcàîYđ << n << 2íĂQƒ)gàaYđ°Ï‚s X¸UǼOOƒÇ€ Ư'8+ïOKƒ- xz¼6×ypéKypi§<Ø₫~•C΃Ç!çÁ¥}CʃÇ1åÁּʃ­³Ïƒ½ƯåÁăø@l}\Ú)¶öUl+ơyp·§àø@<9öuy°·/ó`ị̈́`k÷yđ8>Û₫́ó`;°öư¡àù@|x<A«ưoœÁă,ø°8v<ƒ^¨?ç`àá«.™vư›j˜ç << ‚ÇI°ơ‘Àí,§O ‡çÀ- -<<‡çÀË,Ç}öI…‰ƒgÁK+7íû,‹Xp°Døh™đ`‰°ï%qp°Lx¨È Söik$<®ht _²pƒv”Ñ:O‡‡3Đ:G;#!áP÷́(́p••…CƯ™ †Fơ¬ƒÙi8XV¼¯yñàiqE›ópđ¼xZˆƒ'Æă–‰8Xfl[‰öÆzn<æ¸Đră%˜8¦ÜØæGÍ5ÛîÂBKçFÄÉSă¥åÆ–/ûEnl©qÅfÍ}G-7¶Ôx½̀-5îâÂÉ"ƒ­åƳ§ÆăEiĐSc†VT-CJûÊkƒ–×ËXWôÔxlµAK×µƠ-Á·‹Ú eµulƠÚ ¥Æ¬µ6h©qmwµAOçVôfÚ §ÆËEmĐRă}mµABmĐSăù¢6˜SăÙ3ă–ÏdƳ'ƈF¬uÚ[\Xö·..L™±x˜÷ ¢ Áu8̀D©‹ÀéP<5¶ âà¹q-86,ÚùW–%¸hgƸ [€Ñ;́˜Đh½¦¾{,9Ïkft<{ởĂóâƯ÷7?·¾ŸøY^ÔOoÿæƒ=ăæ'Oܾ|ùñÛ_¿ưàO8úéÍÿ导í^â?.î¿̃o?/₫‹g¼æ?üàḱ‰ zƠÿmÿ²ÿư®’oÔ3ƒ˜₫^₫Ëïôơvó“Ÿ̃üçÿdÿ©‰à_ñ‚˜~9ỹÎƯËB¨ơơ…P?¿·nbøù@!Ô€µ ƯÄp»§6tĂÏ«B¨sdïæ…Ÿ¹jí«Bh=Oah=º)áçu!ÔûºB¨ó¬+„:ÿ. ¡A¿ -3%üL…Pk^Bíëơ…Pßg]!ÔñvYµ÷…Đ̣ÿ©j¨º,„:©Z!Ô¾l{ÛW…PÛ_}!ÔÓBư˜\Bí`÷…P?Ø]!ÔO²ËB¨}!ts!ÔHpU5¬D!tv~ÚGlTº*„Zg_utu…PÇÛe!Ô:±ÀÆľjí«B¨uö…PßÈ°5¯ -¡₫î{ €­ƯBëJ…PḯhïoÔBm·\á̃ÑÚBm‹}!Ô¾Úe!ÔöI‡{Û…}!Ôáe!ÔGjWµv_µcwUơ£̃B}èŒƯœđóB¨wÎ ó|êœđóºêë -¡̃î -¡Öî ¡1Ä?ùgDüçĐƯ漬>>r„¿ đùL đ˘®¡đ=t˜úƯ–eí"ü2Æz¯‹đ§u^Æv¿«t ëPo€)Â/aăPïÎw~ù£Íô'Ë<—.ĐŸ”ù¤@öºq₫vÄùÛ:\Ö< #åŒ]û8ÿ¨aBÄùåä¨iFæÏå"·]˜¿ĂYïâæo» -)̀_÷9×âoIù›-È/¿(₫Öđ¦ ̣·¡†Ä -óñ¬Oa₫yÖêˆÂüi¬Ơ^¢üsĐ­‹̣·±^¿ä¯fäöA¾Wá.ƒ|wˆñ·ơènÿ—t©åăKMă/ŒăoĂ¹\Åø̀Đ Æw·ß¿îEˆ¿ÎûÖEø[Í»[„_Óă‹ÿjW~ ,Tæđß~ëἌđmX]„_âî:đá—•ă2Â_§©‰ -ñ×Ea’B|¥ 1¾­ăOă¸̀}Œ¿Ïără—á±uA₫±©~́µß2Ư3¼ˆñkJßBüsZëåU!~¹¬Ý"Ä·„³2¿ ëµƯ³J}ˆ¿vÑ} IÁ}!ľeîÛÔ¯̣>¸?éhä/¶ưé_ú?Æ¿”›`7Œ9&7o:¸,ÎÖ>ö’[ÓÆQ’W›oT@î₫ß^ShfZƠ± Èk礂¨ä¦Á‰­¶g›qE@nÊá¦ZéZuÁÁn ‘›·hçM -É­sªçZ)ùˆ$$·öp¶OæsJçvÖx×BrÓÛ–u{Hn~Û|¶¯_Cr̉ăÔBr BkSCr”ëÚ:µs_ê^«!9]ÉËù´--À®1ùXkÆåvTej“:́- -«A¹q}˜ZP>ÖmEPn—à‹Ü|Úsl!93yÉËi7F„HH>ÖÇëFHn/̉}ÉK»÷"$/œ‡{úÜëŸ[L¾ỹ…דÛAÑ!É­½­KÄäv ­ÄÚÅäƠ^\ƒ[LîG_3,&wơui+yLn°‘2îÖ¥å›Í`)ñM”û3Íú8”Û`^b¥êU®G[ÇoÚ}₫Zgö˜|óygÄä~¦îm%ÉK§û‰ÉK»„+1ù& )ÅäÖ¹ƠäA¹mc¯'‰å¦ÏÚŒ¦”oî&EL^ËTÏ>ÉƯĂƯZ ï1¹uÎçQOá¬̣nÄè̃— ơÈ™ø¸Ï“[û¬ù«Çä§}¾Ê­ÓbH‚rQM÷<(·v‡­,ư ˜ỵ̈¸ g»§¬IëÙ(kÓ×-(kS°§å‚²fÄ×qSO³mÆ ¬EØûrY;÷±aÖ.öÇÜ0k“́j»Ă¬ñà\kMˆ?j­ỸµÖ9́ µÆÍZâéµ&Ä×s¿¢Ö(³Îµ&ÄoăkÇÙkmÇ-Scm¡ËZû;Ö–°bÑ8kMˆ_Ï`­ ñç–YëFüvkí»‰£[;çưÈ@ÀÖvÚ¡æ§J‰³‚´vvíÂæĐûÇ„´›ÏX›ƒ´>¦+yiƯ‡×w5̉ú)uhư4<¦ Z×á÷3@ë>|㬫î˘9ë>üygñăá¬+éuÏ4Îb>spÖÚơ7êå®4÷9SÖùzL²± QÖ-öcË”ơ7߶ ¬«íĂ”ơ•ê Êzß6eưÖ=(ë¿ -0fÈÚ̃\Ç1 [å` Ⱥ ¿Í™²nĂW":e7em.ëem”.Ụ̀̉û3@vŒÓAv¬J+­=kíq¾`́¨£c­]7\kCo¾@¬~Ô#kíú»±£¾ç%bƯ1­{:Ø·jJ3́sït]aŸ+¦ÓœÙg~3̃ÙgFxÄ—Ö˜ö)“ÏḌúÓ>Ø1Àçym7đ™¿×‹¹ƒÏMù£‘ÏÛsŸ÷ [€¡đYÛo:ơàs5₫˜|¶‘a|Ö®?ÓѸgßrÇàï´%°gBđ9fê™Y|4è™}¼®G@Ï x¯÷Đ3­Ù® güQ#J‡̃ª¹l zëá CÏ\i GçIđă¼÷Ü3ÇƯÏZqÏxmg‘éî÷Ö-.[Î=sàư)¾̉¶IG‰{¥¯\˜öàÉ̃vß đ¹ß¾dîYŸÍö†{̃®Ù…sỊ̈©^Ư÷¬sÇà9ïó|ø¬meD>ăëQơ“;6"̣áÄ'̣ùÛ× Í÷¡û́5ûṛƠ•ÎD>ïÛ|₫>:å |₫SĂ‘Éç?)P#6'ŸÖ --'Ÿ•s›2ùLëï 8Ö¥|Èç£p™3ù|¤Ö<ÔÇOù¿qƯ}¦ÀÏë™Ñg#~]Î`ö:́óvåvcŸwÖ_ƒqöy{ ô9™¦#³¯₫RḈ‹ŸŸû\«û\(Ư×Ä>ëóđŹ³öV³ccŸû¥ă–ÙgÂû^çîêưLßêe°ÑÏ:Ç=8₫Üw¯ˆŸ@Ö>j»áÏäô±^en£-ÇövENăߢIzđÏÚ>@üsĂ}_2ÿÜ•¯ç¤óÏơüe ₫¹¾_Á@ûN"«Đ÷ÜÜh¢đ\£È†@³‰í Â@S½L)º±\s̉Æ@W›·@ ‰đ¢›#ĐLøcÜ3Í„Wœæ4]Ú¯%B ™đëÜĐ4x{º4 ₫Ö àâóƒÆL@Óà—u ?)Ô3µ½Ôü¡!Đ4xE½@ó½m -.äG=U†„À…Kt§¼fÓ f´Ă´ö>O@×â§)#Đ:—z\±!ĐmơZ*jô·_@ ¿¨¦F@o+Ñ̃·.Á@£å ºé?L™¶C.;ư¸ÎG0ĐÆʼ®™6ªÖÊ+gàÅ@…óèCu` 5ëv6º÷uÎ4 ₫\¦@ ç\~Ôܾ!Đ;k̃íôvƯ²3Đ 5M™¾åqz{؃÷W tă}_g›v¼´ÜwÖÜe1Đ%Ơ[6Zçxb éà*ÿƯß.rß™Ÿ™]zß[îkíi¿È}M‚_–ûºŒ^ăDg ·§‹ä×·\Ó$gà́³øö` µ·ó4ºö¾·ä·ºôK0Đ₫6ºü?µäwÖ,ohRđ>\$¿®×Ng 9Èótcù¸È}Ưm® ²3ĐDømj¹ïb³Ó*#;³UÚ°³!Ц¤ÖÜ!Î}‰»ÁÀ¡áÚX₫~\"Đf ®-ù5 ~ăE†Ạ̀vÇ~‘ưl-ûuá»–*GN½nÓ̉_oO-ưu«¼^ë:–ÎehéïbkíĐLăÍÂZ£Í#¾H€ưíkUh/ª)ReàØr` ơ­-ö7ZŹêÿË₫܇–ÿúQ­đrÚPÙU Ú :×–ÿÎg•  Áù"ÿơZÓo>Ö<—–Ïö‹$ëEl#~®UG óælđ,å!!Đ;k-Âèíu -: .è[́í¡eÀ®ÎdÀ³ÏÃ3GMn€€cÍü@¿ow\P³’@»Ơw¬@+ñ¯÷Xü™5Ư4©|Ûí¦[mw´“´^m+‡ê¦­=n´-×{&€æÄ6ƒoÛ.ø7¨Àÿ́Ô¯å÷Ê¿ÁöÅzÁ?»m_ÉZù7Ö£ü+cu¨u¼^¿\ÿ́Æß27ÚzÖ|Öºeåßhs₫™¿©Îü›§ˆØœæJûÅD4 ~®U™xŸù‘ß߀³+·{pÖ±Iœưö4G{PĐg,mŸèœ8ÛPÛæ  Ù̃^Ăg²˜€3Ơp&ƯU¯GºĐ:}>hÖû:µ - µgƯ– VU~ÆFD@k×±Úèï̃̉`‰î.±Êøç}]lícƒ®ùŸK& íÏ¡†UN@?®í.Kiărq›ÅƠ\aUŒ  ÂZRí¨Ÿ§€6(jxWXÆÑZóï€6ƒ£̃1©4Ü{ µk₫ƯpÔq€£08êÎi" O×=Í“¯7*G%Ü—œ|2Å–8ñ[±" µ½Pèj/Íx7§¨"ĐZs Ơ æ‡ëJCàDmX4³üØZl¶‹<Ǿôyoypµèç@ ·Ç‹<Ø·Ü̉`×âk×èæ}-7Z§_„@×ñçv¯Ù>ƯXC†@ûÚăØ̉à©Î (¼œi°ÛÄçœÊ‘—)hƲͶH 4µy®¹²3p̉¯ÂÀÉ[öÈ 4₫h4]ú=̓×}%G yđàÚN'óà¾6¿--6~Ùç@ iÛÜ! N^*iy°‹ïµ̀àt«|º¨ZçÚb@oN-®¶z íăÙ̉`kŸÓ´ö>^ܱÎmliplD´ö°]¤Á₫î5“̣qî/ªY’#ĐÛ{Nƒ½oii0₫=tóÿ¼Hƒm.gKƒư¸V|9m¬¬ơ¦Hc ª}iiđä€ú œ.²`©KưIg?º*Úà>—‹,Øùaƒ€›£eÁ̃®ønôӦЛỡ‹ĐñtN§9îă;§YAÁ%ÇƠ¤Ë<ÍÆú<­íéưÆU‘Oø³®¹̀ƯUw„?kÖd£Ÿ)äû̉n‚X{ZÛ$k/ëÅ$óßëf~îĐ×̣¢ĂÏÛĂÅDßp½Ÿáôsi¾̃Ârú¹ _O¢F?ëÜ·6ѦÚøgĐÏÚk ứk«N¿Q¿Ê -ưL>‹™6£2÷ ŸÙÇk—›¬|î3mLk6‘ú™_Áæđ­¤£;ê¿̉I´æô+mŸưLoÇ¥§Ÿî^WừˆŸ6ÓÆ₫.6úfïµ ØøCñÁ¯´ ×.V#YÛD·Ù뜇ßH₫̉Ăo¤z#ú$A¢Ÿ äSMv₫\g?ÚD›ÑR´q üY{Ô-³ÀŸuC›h₫¬½lmüí—)đç/̉]½%VÊm¼onmÜÈŸÛDûæX'ü™m¢Øi -üÙ`9ëư‹†¿Ñ~˜ui3mF`,₫ù0œ.fÚØX=ê<AVÈŸK{˜ç‹™66è×zĂè¸ÙÛLo×ä»ĐÏœ;+ö  ói¸˜iă[ÚLkë»ĐÛĂ3müÇC÷z%u^‚€öÛ±/€f ÇElZ¼jƒ»IªR+­¯›e ‚8ñ@Đ|øµ̃-vÚ/P(Ám´ótkIđnó„—Ak] ‚¶áclw‚íD†v'‹>AĐmüs -ñ>í- ̃m²÷z‘Œ9@Đvܸµ* IÂÇvq3Ø ™kË‚ưgEç6ƯĐN̉½­AĐ&L.S»rÚ>jY°ưǿy^D€'ÓÅA›d-VÚtû1Ư -1Ă}ŸZl<Àv‘” X=½FA››¿­‚C»Ủ(8èR  k•¡RpT]3QĐ­̣–{{lYđ8ªÀ(h:ûѲ`s̃÷qm,íuX/(h¿g6´,86}2ơíƯk&U!h¯i!`]%gÁ̃7·,Ø}ü¹eÁ£~6-3Đ2s´,Øë4´: ưˆå¼_0°Œ*=Ăh́ƯÇ6 Æáx\dÁV—kƯÏÆ•ñÅZG`Ư»æû4vo°®ă4̃́{»|Tw;#Ьzl7ƒ@kC jP, ç<üÎN»|xX°\0™áÇÀ@ŸŒ¾¨ ±w~L¢ û1k̀·v;æœ#9&Đ혳ƯIv ( ;&±0́hh -Ä:ÏíÆpè1‰‡ø1đ0ü€~L"âP5â69&ü˜ˆ“˜ØüA±ù1¢bócz,†ClˆClˆ“bCbƼbȤàC†ÔX† ™1†L—Ë!1Æ!1ÆI‰±ụ̈büJƒø1©4ˆCiĐO´–#Èä̉ ™( *7¢2(A&WƠßKˆÊ  ™¨ ÊÉ•A2Qd#T¥Èä̉ )2]dˆ"µA)2©6(C&jƒ2d¢6(C&×eÈDb,C&"C292”!‘¡™ˆ ¥È$,¢È‹aÈÀÅ0dĂŒnÈ ­>‚LBăPßlÄ’aÉ$8†&C“¡É\̣ÁÉØÿ̀v›²f’T²hlÛÎ/m₫˜–i’äf·m̃Ûº·)ó®ÑØ|¹uÛB¤±öY#ä5íX°›4kIø}µ¥>Ÿ¤Đ÷ “ÆæèmÍl¯Ï­YB¤ñy}̉]L¤)íyÜ—öV¶É€›\v«¸X{ÏiVûqÄ­}0il–Ø\ư›j峫î"ͺÔÙ§¬ă"M;ö3D›¶ÏáÑđôÖ©óë–P|`ú#ôËDæѬ₫¡˜ạ̀hJ§]:ñhlbæXaÅo[{›9á×/ŸâY%÷h¬íB‰<ŸÍ¹]˜4ÖyTCÙg_ؤ±¹¸îÏXÉMŸ÷6ảØ‹ü7Èd̉Ø߇½©*n̉ø¾ÚC¤±]¹W±ÆEÛß[·‹4vṔ²€Hă“ø¦1DkÛtXÉŸ₫WăÍE;ụ́Ö]¤ñ©£c[É«Œ6®ü§¾ÚF<^B¤±öÖù-.̉¬öăSx46vÏX§>ÇëØyà^ZÎíDĂù7‘ÆÎV»™ÂJ.̉øäĐ)<›yTk5¯rn&–{4ơyHKx4¶ ữƒ{46ÿÔ.m¬.₫đ¦c©¿;á"M\å§kÖúp§}́~ïÂE›é‘¼D˜ư*‘ÆfÇꑳ¹ƒKx4öYJŒy4öçί«s¼|îm•hƯ£ñIv·ybºuÿçi2iẻiẻi¥̉d̉J¥ ̉J¦ ̉ʦɨ•M¨•M¨•M“P+™&x[eÀ­dŒ[É4[É4[É4·’i·’i‚·’i2o%Óo%ÓÀ[¹4™·ri‚·riàí&—&ñv“Ko·©±t¬ñB}|ZÜM. ÀỪ¥¶›\DÛM6 ´ƯdÓ@ÛM6M¢í&›ÚnƠ¦¶›lÛM6`»U™ÖnK 5BŸ|D>ù4€ŸđáÓ$đáÓ>|À‡O“À‡Oøđi`>MbF ́è}5‰}5°±üdÔ$öaÔˆ}5ÀOBMbB ́C¨}5‰}¸0°¡ø!Ô$ú!Ô@¿n#N?„D?„ÑŸúáÓ$úI¨~5À¡&Ñ¡Fôç~ø4‰~ø4ĐŸúáÓ$úáÓ€?„đ‡P“đ‡Pÿjº1á–Ä?„ñO> øçIüçø4đŸæ5‰5đ£₫¡Ô$₫¡ÔÀ?¤ˆU“ˆU±j@ VM@¤ˆT‘j‘j` R DªI DªH50©&A©"Ơˆ‚85 ƒ85p§âÔ$Ê©ƒ85`§&a§ âÔ€A95‰‚X5P« -bƠ$ -bƠ@A¬(ˆU“0ˆU# "Ơ€A¤ÄAI5`© "Ơ$ âĂ€A¤0ˆT“0ˆT»8‘j‘j„Aœ0ˆS“0ˆT‘jà RMâ R8ˆSqjqjà N Ä©IÄ©ƒ’jÀ RM R DªƒH5 ƒH5 N Ä©IÄ©ƒ85p§æƒH5‰ƒX5p«¢Ơ$¢ƠÀAÄ8ˆY“8ˆY1kà fMÏAÄ8ˆXkkà b D¬ID¬ƒˆ5p±&q±"Öˆƒx5‰ƒx5pP^ Ä«IÄ«ƒx5ÁAy5™ƒ̣j‚ƒƠ« Ê«É”YÚ•ƒƠ¬É”Y”Y”Y“1(³ J¬ J¬É”X”X£¤ÛqPNLpPbMpPbMæ Äà`ÛHå Ä̀A‰5pP^MppŒ̀§ă Äà Äà ‹5™‚k  ¼(ˆW“(ˆWñj  ^M¢ ^ D¬ƒˆ5 ƒˆ5`± "Ö$ "Öƒx5`P^M¢ ^ Ä«‚x5PPbM¦ ̀ àH BPjM† Ô€ ä€ ́ AÙ5AÙ5AÙ5 ‚’k‚’k‚’k2%׫\ ”\“8DâX(¹&(¹&3PrM0Pr #\ÏOí³é‰Á@¹5ÁêÖdÊ­ N oÆ@ÜÄ@܈[qk±k€ v Ä®IÄ®‚Ø5P»&Q»FD®‚È5‰‚È5P¹ -"×$ -âÅ@Aä(ˆ\“(ˆ\»8%×$J®qk` nMb r D®È5‰‚È5¢ Ü€ Ü A¹5A€ åÖdÊ­ J® J®É”\”\”\“!(¹Ê­ Ê­É”[”[#÷¾  rM¢ v Ä®ƒè5 ƒè5`Á bØ$ bØ€A 0ˆaÓcÁ "Ø€A›„A 6PÁ "Ø$ "Ø€A0ˆ`“0ˆ`l„Aü„Aü0ˆ_ñkñkà ~ įI”_ñkÀ ~M † Ä°ƒ6 ƒ6`Ă bØ$ b؃6`Á&aP‚ D°‚lqc€ ‚ D°ID°‚ƯF‚6‰‚6 ~ įID°ƒ6`Á&aÁFįƒø5‰ƒø5pP~ įIįƒl  ‚M¢ ‚ D°‚6‰‚lAü ˆ_“ ˆ_ñk€ ~Í5l1l€ † ¬MB D²²l±l – IJé (É"Ù@$›@$ˆd‘l‘l ’ D²ID²€H6" M" ı€86‰€86P ıIı€86Ç&ËbÙ@@,›D@Y6ËbÙ$bÙ€H6É&É"Ù@@$›„@üˆd‘l‘l@`·G ’MB ’ˆcql‘l@ ’ D²ID²ql@ MB ı86‰860É"Ù$"ÙA$ ˆd“ ˆd# -âØ@A›DA(ˆcql®)ˆd“(ˆe±l  Mâ D´ƒ˜6 „˜6€ÓbÚô ”hm!¢M!¢ D´„ˆ6 „ˆ6€Ñ"Ú$"ÚBDÏ&ÏâÙB<›B<@(ÏâÙ$âÙB<›¡<› B™6Â!°]A(Ó&ƒP¦MP¦Mph·O eÚ@B‰6AB‰6™„m‚„m‚„cT\;Ê‘ J´ J´É$”h$l©$¬¢M¡D@X=›àà‰OÇA‰6ÁA‰6ÁA‰6™ƒmÄA<8ˆg“8(Ï âÙ€A<›„A<0ˆhmmÀ ¢ D´ID´ñlÀ M <(ˆgñl®)¢MÂ`˜6p0L@ªM"a¨6 0dX¶M‚aØ6Đ0lp¶MÏĂm bÈ6 1d›ÄDd˜² P Ù&Q1d°² \D¶I\ Ù0†l#2†k“Đˆk1¢\›ˆåÚäQ®MĈö™ê¸x¤uĂrÓmd—³ƠAX¢ôIeçt¶§HÚ@ÏöIèëÔvƒ‹6> kjO‘´³̀‚vD›\e‘f%m|¶Ô¹…i³œơ)Z2mlÖ4†ƒ›6Ö¹¶ÖØ&ô'ml>–¹ m¤ÖßæIª.ÚX{<ÚS$m&̉Ö=ÅÊEŸ´L!ÚØt°ñfô¶ ¡¬T\ù₫v2m¬}lí)’Ʀ̉ư4VT‹B¢µ÷©=Ẻ!7/ÿ|¢M¦¬D(+Ï&(+Ï&SVMPVMPVM¬4›À¬4›À¬4›ŒYy6°¶j6Zi6 µ²lµ²lµ²l2jeÙjeÙkeÙdÖʲ Öʲ ÖJ³É¬•f¬•fkÑlzÖbÙˆµH6ÀÉ&ÁÉ&~ƒƯ$H‹d“H‹di‘l -’M"-’ ¤•d³´ßÈqÉ&ÉĐʲg‘lg‘là,’ œE²IœÅ³’lÀ¬$›DY$(ÛmB¿xæM¢, ”E²²H6‰²H6PÉÊJ²IE²²X6@Ë&QËF”E² ÊJ²É”­’M@V’M@vŒ_tï [%›`¬$›`¬$›̀XI6ÁXY6ÁØjÙ$ÄJ² ÄJ² ÄJ²¹F́ªi‰}«$±o•cûV96‰}«ØWȇcÓ“O àC±|(6 |86 ä“bÓƒĂđaØ> ›> À‡aødØ$îaØÀ½µ6`o­M¢̃Z ·J±z«›z« Ao•`ôV 6 zëÁ`›!Áî­l÷V 6poƯ¡¹—©è¸qoƯâ‰5um³ˆë‹*Ø$î­là†À'Á&qÁî!ØÀ=›Ä=ܸ‡`ølùl _·îE/ơÓ5̣á×@>ȇ`“È'Áđ!Ø>›D¾U‚ ä[eØ@¾U†M"ß*ĂFä[%Ø@¾U‚M"ß*Áô­l@ß*Á&¡o•`ûV 6°o•`“Ø·J°}k5l@ß*ægߪ‰d°o•`ûVÍF»f‚Mb‚؇_ûđkûđk`~ ôĂ¯éé‡^₫ĐkÀzMÂ~đ‡^₫Đkz₫a×À?́ø‡]“ø‡]ÿ üĂ®IÄ®€Ø5½&!½¢×À@ô²k„@äˆ\“ˆ\_Dr ”\“ˆ\‘k rM" r D®€È5 È5 »FD®I ”\‘k@ rMB ^ D®È5 È5 °ÛÈ^Oowkqk@ r D®ID®È50¹&1¹b×À@́Ä@́1¹"×$"×À@É5 ¹&!¹"×€@ä„@äˆ]±kz"×À@äˆ\sÍ@äÄ@ä1·âÖ$âÖÀ@܈[Ó3µ¢ÖÀ@ÔÄ@Ü1µ¢Öô Ĭ˜50³&1³bÖÀ@̀Ä@̀ˆYQkQk` ÔˆZÓ#³F D¬ J¬É "b« ¬bM PbMf Ä`àĐpí ¬bMF Ä@ Ä@ ÄŒ@‰5@™5 PbMF Ä` Ä` Ä̀@91Á@‰5Á@‰5™k‚m#»ô—i¿H€ñj‚k‚cËw%Ö%Ö]¬É”X”Y1k1kD@ĈX“ˆXk@ bMB b D¬ˆ5 ˆ5 ³Ê¬é ˆXk bÍ”X“ (±V¯&(¯&P^MP^MP^M ´ ´ ´ @y5PZMPZM ¬`µj‚²j2ÿdƠÿdƠÿdƠd₫ɪ ₫ɪ ₫a¦ø íàŸ´`ƠjÿdƠÀ¿)ĐæüCªIüCªH5©&©"Ơ@¤D@¤ˆT‘j‘j VˆT“ˆT‘j RM" > Dª€H5‰€H5°ÛÈ.ùeÛ3åÔÀ?¤ø‡T“ø‡TÿjàRM" R ”U”U“(«c(©&PRMPRMPRM ¤ ¤ ¤ @I5À1̉– -@Y5‰€’j‚€’j‚€cäÛDªIDªqj@ NMB N Ä©85=Qj@ J D©IÄ©Qj@ JM@5£bÔ$bÔ€@ŒˆQ“ˆQ1j@ JMB J D©(5=1jÄ@„ˆP“(¡"Ô€@„„@„ˆPjj@ B D¨ID¨5B BMB „ˆP%Ô$âÂ@„ˆP“ˆP»́̉^†í" Ƨ5 ¡&!¡"Ô€@„„@„ˆQ1j1jÄ@ 5 ¡&!¡J¨€5‰€5¡"Ô$J¨€5£¦ B D¨€5×D¨ID¨ñi`ơi₫điÀ> ø“OÓÓú¡Ó@?tD?ù4‚: đC§éá‡Mư°i 6M¢6 ôæ~Ø4‰~Ø4Đ›ú¡Ó$ú¡Ó@?tè‡NÓÓ›Fô“Lüiüi 2 ôC¦IôC¦~È4Đ™&ÑO2 đC¦~È4 ~È4À›FđC¦IđC¦~È4Đ™&áü!Ó€?d„?dđ×md—̣b.MÂ. øC¦È4 È4à™ü!Ó$ü!Ó€?lđ‡M“đ7¶’¬á™₫!Ó$₫!ÓÀ?dˆL“ˆL‘i 2M 2 Ħ€Ø4=‘i 2 D¦¹& 2M" 2ˆKqiqi` . Ä¥é!ˆJQi€ *M‚ \A•¢̉ôĤ‚˜4@“&A“b̉ALAL ˆIQiQi€ * D¥é!ˆI#J¤‚ˆ4 ‚ˆ4@‘& (‘&CP"M@phÀvJ¤É”H”HÚ­’FA‰4AA™4PP"M¦ D  D  DLA90AA‰4AA‰4™‚i‚‚m#{^ÆËy0x4Á*̉Ç–î4J¤ J¤ J¤É ”H1i` &Mb L!‘"̉$"̉€@DˆH“ˆHi@ "MB " Ĥ˜4=%̉@@DˆHsMÀiC¤Ă£‚áÑ$ -†GĂ£ƒáÑô †F -C£I, F4 †FÓó‹†EĂ¢ID ‹$†E±hĂ¢aÑ@ÅĐhÑh"6”F±¡4Ê¢!6lÈ«Á¡$J¢‰Ô¸J4‘K¢É™q•h"1–D‰±$œW‰&̣bI4Q”D“Kƒ’h¢4(‹†̉ $\”D¥A%GT%ÑäÊ ü—¨ J¢‰Ê $\”D•Á¶‘½ª.“æ$v¥A94Q”DµAI4¹6(‰&jƒ’h¢6(‰&×%ÑDb,‹&"CY492”ECd(‰&"CI4 ‹H4`1$¸McH4‰2†D“Đ lÄ¢aÑôp ‰<†DC¢¹ä¿~‰ft*;4%Üđ‡Ú"Ñ >_3ƯKy©uªCSä:6…¦4ưADRh́2<·UªCclÇæĐ UA‡¦ĐqçæĐØåaî$³äº¸D3T± ̃d­·} µ‡j¸ESÚÇ:´OæƯœ«Üb­́¾`}`†K4~_°\¢™mØOSH4vßo«OƯp‰f>.ŸR;Ñ€|XÚ]üm™C¢±¶Ư-­ñ³,̉9Û¥ÑØx›‚Fcm›³Î:~ñ²N0®ÑXÛui4>?CơĂƯ_´P ‹Æ¾Ñ‚†,ûÚÓº„Ec/²̣ư}hBŒK4Ö·Ë1‰ÆvåV¥·hfÍÚa%·h́˜øÓ1eÑ”¶Ï¤À¢±v{¨;®Ç:…Ec_O¢q‹fv¤­TKŒƒ~.‹f°»l{Óh,"èä–ªÑXn ³×)ó+ùoqZDë¸Ec·0Ï©Y4v/q?›Ec§êÑ‹S-ÑæûÏÍ¢);̃xE3ÖƒÆh¨Ư„Ÿ;f´ÏÙ4±^ÚX]|Kë̉Y4cMÿœ»°hlRĂp6‹fÔ3°h̉³RµhÊ×DÙs‹f¬ÓĂ¢“Z§^¥ÓŸ#ˆFc/:צÑؾûg”hzÊâĐ@Y$Q‡&Q‡F”E¡²(4 ³84`‡̀âĐ$̀âĐÀZI4 ‰&¡VM VM VMF­,@­,X+‰&³VM°VM°VMf­$`­$X‹D“X‹EkÑh€-M‚- °ủ¢Ñô¤Å¢´X4‹&‘‹̉Ê¢´X4=h‘h­$8‹E“8‹Eg±hà,Mâ, - œ•EfeÑ$ʆE#ʆE#ʆFÓS64Q64Q64²aш²aш²X4=dĂ¢dĂ¢dĂ¢é)( ”E£I”•F#ÈbÑY,YY40‹ÆbÑ$ÆbÑÀX,+‹f̀Z]Ơh@, ˆE£¹@lH4û¡ûB¢©́ ‡¦g_84•}RhD¾PhẓáĐ|áĐ|áĐôà ‡Fà ‰FäC¢éÁ‡Eø°hM àĂ¢ø$Ñ$î!ÑÀ=I4`OM¢$ ‡Dôhô°h€ ĐC£IĐC£zh4p¦ç ÜĂ¢{X4‰{X4p/~Uà“EÓs‰î!Ñ>Y4‰{X4p‹îaÑ$î!ÀÀ=,À‡E“ÈÈÈMO¾ĐhD¾ĐhD¾Đh:̣aÑ|aÑ|aÑôä ‹Fä ‹Fä ‹¦'_X4"_h4"_h4=ùB£©è ‹Fè ‹¦G_X4b_X4b_X4=û¢û°h„¾°hzö…F#ö…F#ö…FsÁ¾‰¦cß‚C#ö-H4•} MϾ‡¦²oA¡ư~ đ·àĐ M¿‡Fø[h„¿‰¦çß"‹₫-²hàß"‹&ño‘Eÿbâaåß"‰&p‘DI4p‘D“¸H¢‹$¸H¢I \ªE—½ÑmT:®so—=Â4¦_¸F—ªÑô\dÑ@ÀE \dÑ$.²h à"‹bÑôD¢H4 ‹&1P Ä¢X4 0 ‹bÑ$†E#†E#†FÓ#04!04!04aшaшaÑô ‹F \°hÄÀ°hz†E#†F#.h4=4ÊÀE¸`Ñô\°h„À‹f ÷µZ4=,!pÁ¢,1ëuU£41pÙ"™”h:†C#†DSMÏÀph*C¡C¡éMÏÀphÄÀhÄÀhzbÑÀ@,ˆE“ˆE±hÄ@$Ä@$ˆD‘h‘h` $ˆD“ˆEÑh‚̉h2‡ÙP`Ví G 4Ä@Y4ÁÀ!x]X-Œ@Y4@Y4@Y4 ’h’h²h2eÑeÑeÑdJ€ Ê¢ Ê¢É Ä¢X40&1¢ÑÀÀ±¥+;ø'‰&X%̀?Y4Á¿©¡Íø‡F“ø‡FÿĐh M@,ˆE±h±h  Ä¢é ˆD‘h M"  Ä¢€X4‰€0‹bÑ$†E#†E#¢ÑôD£ÿB£ÿB£éøøøMOÀ°hD@,ˆE“ˆE1D£ID£±h M  Ä¢€X4 €X4‹bÑ$¢Ñ@@48FÂ=<(Ñt ‡F ‰¦"0áĐT†B#†BÓ#0!0!0áĐ!Ñ!Ñô”E±h@ MB  Ä¢‘h‘h@  D¢ID¢H4 ‰&1‹¢ÑÀ@4Ä@i4 ¢ÑôÄ¢X4 ‹&!‹bÑ€@,H4 ‰bÑ$Ê¢€X4PM  Ä¢€X4 €aÑ€aÑ€¡Ñô F F ¦C`X4B`X4B`X4=Ă¢Ă¢Ă¢éˆF#†FÓ#04@,0,€aш€aш€aÑôÄ¢Ă¢Ă¢éÍC¢éM áĐTü…B#ü¡Đôô ‡Fô ‡Fô ‡¦§ààM?,è‡Eư°hư°h è‡D“è‡Dưh M¢ ôC¢~H4‰~X4ĐO đC£IđC£~h4Ц§ ôĂ¢~X4‰~²h€ đĂ¢éá‡Düh€M‚ ôĂ¢~X4 0à‹üaÑ$ü…E#ü…E#ü…FÓă/4á/4á/4aÑaÑaÑôø ‹Fø ‹Fø ‹¦ÇßØ~ƠĂñøMÏ¿Đh*ÿ¢Ă¢éMÀ°hDÀ°hDÀ°hz†F#†F#†FsAÀh:†C#†DS MÏÀph*C¡C¡é!  MAA0$A0$‚X4@‹bÑ$bÑA,A‰&A‰"ÑA$A$ ˆD‘h±h€ 4 ˆF“ ˆFÑh‚̉heч v… , AY4AAY4AÁ¡Ư*Ú:.Ñ%ÑeÑd -Ê¢ -Ê¢ -ʢɔ”E”E“)ˆE±h  4A4 (-ă9»/t̀- ‹bÑ$bшaшaÑô Ä¢C£C£éME`X4B`X4=Ă¢Ă¢Ă¢éM@40404 6‰¦C`shÄÀ&ÑT6‡¦§`sh*›B#6…¦ashDÂæĐ…Í¡éYØÑ°I4Âa“hzbÑÀĂ°hbX4‰ˆaрİhÄD$ÄÄh€bH4P1$„E$ˆ %ÑDl(‰&dž²h"6́çÁ¡4J£‰Ô¸j4‘K£I™qµh"1–E‰±,œW‹&̣bY4Q”E“Jƒ’h¢4(‰&Jƒ²hriPM”kn•AY4¹2(&*ƒ²h¢2(‹&W±h¨ bÑPD£I¥A4jƒh4ÔÑhúÚ  µA,jƒX4©6ˆECbŒECdˆE“"C,"C4"C4‹¡ÑT,6‹F\lMÆfшŒaшŒÍ¢éÑØ,±1,±±Y4=›F#<6F|lÍçLÆ₫×$ÑX¥̃<ªdÑXç¸/aÑX¹ßæI¢ÑX¹ßÆÚK­äƯ'8-á£vú¬̉Đh†z.…FS΄rÑRF3Ô'Œ¡ÑØơ´́̀Đh†:KŸuêƠk¨ zh4C>ÍPŸUW£lễ<;ÙÖµy4†„Đ:đhFˆZh4vsëØFSÚ†̣ºíªălÍX¯!áÑŒu:+UÆîI[óhƶ…G3ƠI0±’{4S%Ax4vôëƒfªGS>U´XÉ‹ŒÖé?Đ6âï€GSÚ€Ä;yM· Ö©i46”çXÇßÈ/_±y4v>Ø4O<;ñJö˜gü₫<¿û³„FS₫ü,i4~¯h]c4¸Fcvn ÑØ&ô€&×h́¶›#ÛX]üWïü¡zx4Ö6̃ăÑØ §µ{@•{4~SïXĂ£±›~3^Íèm—=Y©¹Ù§²¡ÑXÓB&4Û &o? U&Í«V2Æ_¤§Óø41Ư₫ɧˆưÏi2iẻiẻi¥̉d̉J¥ ̉Ê¥ ̉ʥɨ•K¨•K¨•K“P+•&x[UÀ­TŒ[Tp‹JnQinQiÀ-* ¼Å¥I¼Å¥·¸4â-*Mâ-* ¼E¥Âlà—3oC¥oC¥pC¥é*€[UÑ6T¶á̉ˆ¶á̉ˆ¶á̉ô´ —¦̉•F° •¦‡m¸4‚-.X.MÏÚpiÄÚpiÄÚpizÖ†K#Öẩµ¸4=iqi m· '.M"-.H‹JiQiiqi -. ¤•K“@‹K#Đ¢̉ZTDZTH‹J¤•J“I[U­\­\ Úểgå̉gå̉dÎÊ¥³Ri‚³U¥É˜•J˜•J˜•JsYdÄ?lø‡MÿĐiÿĐià_ơi >M¢Ÿ|à‡Oüđizø¡Ó?tè'&Á/tÁ/tÁ/t~¡Ó~¡Ó~ø4=û§ûđi*úĐiẓ¡Ó|¡Ó !6T¦_è4_è4_è4=øB§øB§ûB§éÙ>Ø>Ø>MϾđi*ûB§üĐizö…O#ö…O#øáÓố ŸF́ Ÿfl«ê®±/|±/|Á/|~ø4Đ¯ÛˆÓŸ&ÑŸFôC§~è4‰~̣i€> đçIôçưĐi :M¢: ôC§~è4‰~è4àŸüáÓ$üáÓÀ?|ø‡O“ø‡O#₫I§è4‰è4đ₫¡Ó\ó¡&ñ£₫aÔÀ?”Ä?”ø‡Sqjqj@ N Ä©éˆRQj@ JMb`(5Sü$xUj¦øÅíªÔô ¥F ¥F §¦‡`85‚`85•‚¡Ôô ¥F ¥F ¥¦ç J0J0JMÁPj„ÁPj†xê›+5=Ă©Ă©Ă©é)NM¥`(5¢`(5=Ă©Ă©Ă©é9ˆS# †S3¶ßÛjRÜ0N0N0NMAœ0Øm¤₫¼‚œ„Aœa¥ ¢Ô$ âÔÀAœ8ˆS“8ˆS#¢ÔÀA”ÄA”8ˆRQjQjà œ0ˆS“0ˆSqjÀ NM N0ˆRQjQjà J D©¹æ RMâ V Īƒh5‰ƒh5p¯âƠ$âƠÀA¼8ˆWÓs­¢ƠÀA´ÄÁĐj¦˜Ơ]µ)æLW­¦ç`h5â`h5â`x5=Ă«Ă«© ­¦ç`h5â Z͆CƠjz †V#†VÑjÑjà ´0ˆV“8ˆW‡íC®ưq‰A¼a­ ¢Ơ$ âƠ€A¼0ˆW“0ˆWñjÆînq½̃uÄ«ƒx5p¯&qP^Mp°m„Ÿ™q¯&sP^ ”V#óé8(¯&8(¯&8è^M¦ ¼((­ -¢Ơ$ -¢Ơ@A´(ˆV“(ˆVñjÀ ^M ^ Ä«ƒx5 ƒx5 Z ”V“(ˆVÑj  ZÍ”X“)(³&(8R¨”Z“!(µ& (·& (·&CPnM@PnM@PnM‚ Ô€ Ô€ ṇg¢ÖA©50µ&1µ¢ÖÀ@ÜÄ@܈[#¢Ö$¢ÖÀ@Ô!,‡s¼C­SàÍjMÏÀPk†(!Tµfˆ‚KUkzÎś#‡àŒ[#θ5=gÜJÁµFœQkz -θ5¢`¸5¢`¸5=Ă­Ă­Ă­é)n(n(nMOAÜ(Øm¤̃$¨nM‚ Ü1µ¢Ö$âÖÀ@܈[“(8Ë­¥Ö¥ÖdJ­ d (µ&CPjM@PnM@PnM† Ü€ Ü€ Ü A¹5@PjM@PjM¦ Ô  Ô à¹÷‘k±k  v D¯ID¯ƒø5`¿&a¿ â×€Aüƒè5`½ ¢×$ ¢×ˆ‚¡×ƒ¡×ô ½F ½F ¿¦Ç`ø5Â`ø5ƒ¡×ô ½F ½fÓ¡ê5=C¯C¯C¯é9ˆ^# †^# †^Óc0üa0üa0üƒá×T †^# †^Óc0üa0üa0üƒø5¢`ø5¢ ~MÁđkÁđkÁđkzâ×Án#AüDAüa½ ¢×$ â×€Aü0ˆ_“0ˆ_# ¢×ÀAôÄAô8(½ ¢×$ ¢×€Aù5P¿&Q¿ -â×@AüDAù5‚ z D¯ID¯‚è5@½æ‚6 ‚6@ĂVÅ&!ÅâØ€@96‰€86ÇâØô”bQl M`(6"`(6"`(6=C±C±ñé M%`(6=C±C±Âu¨MOÀPlD@0›€¡Øˆ€¡Øˆ€¡Øô ÇF ÇF Ǧ' M`(6`(6=ñññéMÀpl„Àpl„ÀplzâØ€Àn#@›„@!Å¢Ø$âØ€@ˆc“ˆc#¢Ø€@›„@ˆbQlQl` ıIı‚86@Ç&AÇFD±‚(6‰‚(6PÅ -¢Ø\SÉ&QË -bÙ@A4›ÄA48ˆgñlñl! ijéA(Í¢ÙB4›ÂĐlÂĐlÂĐlz†f#†f#†gÓƒ0<0<› -ÂĐlz†f#†f3`¤v¢ ùÊV̉]MM‘lĂƒ©đl¦ÂÆơ@³±:¬=ơ ÍÆ‹·ñ˜i6^đ­†Œk6^̀5ƯDW‰y Ut-F³™æD¢Ùxơ6i6öËq ÏÆ^dúŶf¿L*‚9’ñll7®gh6¶«×n×ĺxø“1¥Ù”¶94kïÇrỔ±×°ĺ¸ëA4nÙظغ‡Èx©qZôcm#ơ2îØØø[·¥½ÿỌ̈ÈQ…dccØü£ÙÖ^‘œ»•L²±᜷ĺÛ«lä’¥K§¹dcơÏyØĂ²™lNµ–úg?d ·llóÉf̉´U$›̉ö+@£‹oÈ×E²™¶zú!ÙLư3îl¬k’Ơ½ư!’lJÛ]PVªÇm­3¶±ĺE6°°ĺïͽ«s¾ŒM‡!ÙØkF=»Ææ9à–ƧƠdÂJ² ÂJ² ÂJ²É„•d„•d„•d“+Ç&+Ç&+Ç&#V’ œ­M`VMƬ›À¬›À¬›ŒY96Y96ÁY96™³rl‚³rl‚³’l2g%Ùg%ÙÀY$8‹_#΢×ZôZô@ëz ”E¯I”E¯²è5P½&Q½Êʯ²ø5 ²ø5@V~‹^“‹^cÑk`,zMÏXÔ+»ÄÊ®I„Å®°Ư&êăcÜ­I„Å­°È5¹&¹Â"×@XÉ5 °È5»Àb×$Âb׈°È5AXÉ5™°U® ÀJ® ÀJ®É€­rMđUrMđUrMæ«äàëw9+_«]“đ*¹&đ*¹&đ*¹æ¯È5‰{È5p¹î!×$î!×À½*×@=äzrk€n ĐĂ­IĐC®ôpk Ü=Ü ‡[ôpkôpk€n Đ“[“˜‡[óäÖ€<É5‰x’kr ÀC®xx5Z ÀC«IÀC«xh50­&1­æ¡ƠÀ<´Ä<´˜‡WôäƠ$æáƠÀ<¼AOZMbZ ̀C«yh5=ób`V ĐĂªIÔĂªzƯFê³cÜ©IÔĂ©zH5P©&QOR ĐCªzH5‰zH5P«êaƠ$êaƠˆzH5P©&Q©́!Ơ€=¤„=¤¸‡T÷j÷jର‡UÓs©î!ƠÀ=¤kî!Ơ$î!ƠÀ=¤¸‡T“¸‡T÷j RMO>œЇSúpjúj„>œЇS“؇Sûpj`NMbN ́‹'`‹}85 ~85À§ú!Ơ$ü!ƠÀ?¤ø‡TÿäÓè4à&áü¡Ó€?é4‰~è4Đú¡Ó$ú¡Ó@?|è‡O“đ‡O₫đi„?tÄ?é4àü¡ÓôøC„Ø4à›&á›üuÑccÍ× -üá̉€?dđ‡L“đ‡Lÿià2Mâ2 üæØ4‰Ø4â2 üC¦IüC¦’iÀ2MÂ2 øC¦È4 È4à›₫aÓôüC¦È4đ™æÈ4‰È4đ™₫!Ó$₫!ÓÀ?dø‡LÓó—₫á̉À?\Ä?dñ—₫á̉$₫á̉À?\ø‡K“ø‡Kÿpià.Mâ. üĂ¥È4‰È4Ư¯™ÛÜ-đ‡L¼—G#₫¡Ñÿ¤Ñd₫ ©¡À¬Â¡ăOMæŸ4àߘ®ü«MÆŸ4ÀŸ<ÀŸ<Œ?y4?y4àOMÆŸ4àŸ4àŸ4Ä? 0Á?Y4Á?Y4™²h‚m#ú½Gsh2ÿäĐÿ$ÑÿÆ–â4₫I¢ ₫I¢ ₫¹D“é'‰&è'‹úaÑ$úaш~H4Љ&щú!Ñ€?$„?$đ‡D₫h₫hÀ ø“EÓÓ‰ú!Ñ@?$è'‰&ÓOMĐ¯J4?I4~’h~’h~’hüäĐüäĐüäĐdøI¢~rh~rh2üäĐüªĆ“C“Ù'‡&Ø'‡&Ø'‡&³OM°OM°oŒéëé©>»›iîMÀ¯J4Á>ù3°ojXª±á³dûĐg`ú đCŸIđCŸ~è3À}&Ñ}úáÏ@?ü™D?üè‡?#ú¡Ï$ú¡Ï@?ôè‡>ÓÓñúaÏ@?́™D?́è×mDÓp¶=ĂOî ́C}È3‰}È3°yö!Ï$ú!Ï@?Ù3?Ù3~²g€ n¿ậL†Ÿä™€Ÿä™€Ÿä™ ?É3?É3?É3~’g~²g~²gư$Ïư$ÏưÆȱ/è7IIô›$Ï@¿Ị ø›æ-₫&É3ào’<₫&É3=₫&¹3ào’;₫&¹3 “äáo’;₫&¹3 Sug ß$wüMrg₫&¬áo’;₫&¹3 “ÜđÑÀß$y&áo’</–<₫&É3đo’7#₫M̉fàß$m&ñoZˆÓ0^|ø›¤Í$üM̉fÀß´¦ưl¤Í$üM̉fÀß$oüáÍ$üáÍ€?¼ám&áOÚ ôC›~̉fzø!¼?¬à‡5“à‡5üºÔGŸ3“è‡3₫fÀ̉LẨ øCH3 H3ào’5ÿ&Y3‰“¬ñoª̉ ø›$Í$üM’fÀßT¥è7IIô›$Í@¿Ỉ ô›$Í$úMU~“¬à7ÍñP¥^¦ó¹`Àoă~½Ăo’4s ?¤™?¤à‡4üª4“Ї4úf@Ÿ¤™|83g̣á̀$̣IøpfÎLÎ äĂ™|83‰|83g̣á̀$̣á̀@>œȇ4“ȇ4ùf ̉ äĂ—ù¤Ë>t™>tȇ.ùĐeùĐe º äC—Iä“.øđe¾L¾ àĂ—øĐeøĐe º äC—éчèú°e@¶LB¶ èë6Ră®LB® èC–}È2 }È2 Yô!Ë$ô!Ë€>lЇ-“Ї-#ô!ËÀ>d™Ä>d؇,üeüe€² đC–IđC–~Ø2Đ[¦§² ôC–~È2×ôC–IôC–~È2ĐY&ñY₫!ËÀ?d™€¸2WâÊ$J–qe ®L ® Ä•€¸2 €¸2WâÊ$âÊ@\ˆ,“ˆ,‘e ² Ä“¥É@4™@4ˆ&”&“(M&8¨+¥ÉdJ“ Ê“ q[¤# <™  <(M&PLPLPL" —  ,™  ,™L@Y2AÀ¶=$ær® L°J2Á¿±e8’d‚’d‚’d2ÿ$ÉÀ?,ø‡%“øw4¥üƶxV@|ŒưÛ"äćß6́âÀÇX_Ù»!c}igld|ó¹ư‘2f h́ØƯ^’mÈØ;̀e+^3b«ëB@BÆ₫3NÈØhỹ±₫Èưfô<l¤Ûº‹‘IñX̉ú%/—ü Ö4V!̀Y›đưo ‘±¢<ב±ïÎBDd́»œ×*xă©mù-æ–ˆLQ< ˆŒyº˜ 2îFˆ!䌌µâLÎȘ¯Åö4k°o©FIGd́ŸW|}ȘӦlƠ©‘1ă3= ‘1WD&2“üßp̣˜Z$™{ˆˆŒưÛÖJDd¬jk'#cF[d’‘±·¨åÁ`®sù_‘9t„Œt„ u€̀©³d¨³àc¤³àcN¡ #¡ #¡ s --©m2[2§Ø‚‘Ø‚‘Ø‚9Å„ŒÄ„ Ơ€̀©¶d¤¶d¤¶dNµ #µ Cµ% s¨-)ª-1Ê-1™Cn‰ÉPn“¡Ö“Ùµ–” µ–” µ–”̀¡µ¤d¨µ d(µ¤d©%%C©%C¥%&s(-1*-1*-1™Ci ¹PiÁÉPhÁÉ:KN†:«& ³$e%)C%)C%)³ë,Aê,Aê,@™Cf ÊPf ÊPf Ê:KP†:KRF: RæÔÙ e(³e$³eN™ PF* PF* PæTY€2RY€2RÙeN‘)#‘)#‘)ó&²evơ#'Cơ#'ơ#&s¨1¨_P2Ô>R2‡ö“¡ô“¡ô“9¤˜ ¥œ µœ̀!}äd(}äd(}ädé#'Cé#'é&s(1*0 -0™C÷€ÉPöˆÉPöˆÉ²GV†²GX†²GXæ=Â2”=Â2T>Â2»̣‘•¡̣‘•¡̣‘•9”¯®ÓW>²2”>°2‡̣‘•¡̣‘•¡ô–9”° •° •°̀¡|]¨|¤e(}¤eí#-CíS#Đ>̣2‡ö‘—¡ö‘—¡ö‘—Ùµ¸ ¥¸ ¥¸̀¡}Äe¨}Äe¨}Äeí#.Cí#/Cí#/shyˆqq™CüˆËPưˆËPưˆËêG\†ê\†âG\æP?̣2T?̣2T?̣2oêG\fW?̉2T?̉2P?Â2‡ú–ú‘•¡₫‘•9ô° ° °̀!€„e(€¤e(€¤e$-C$-C$-s( i* i( a™C ËP ËP Ë"HX†*HX†*HXæPA3A"3A"3‡™¡™¡™Ù5Ä 5Ä 5Ä̀¡$f¨$f¨$f$1C$1C$2s¨  ‘ ‘™C‰»PÉ̀PÉ̀"Hf†"¨F ‚¤f$5C$5C$5³‹ ¡ª ¡ª ¡™C ÍP ÍP Í*Hh†*Hj†*HjæPAR3PA@3AB3‡¡¡9DĐ EĐ UĐ̀¡‚¤f¨‚¤f¨‚¤f̃TĐ̀®‚df¨‚df ‚Df$2$1C$1s¨ ‘ª ‘ª ‘™C‰̀PÉ̀PÉ̀*Hf†*Hf†*HfæPA23TA23PA"3‡ -™¡ -™¡ -™9TÈ UÈ EÈ̀!‚äf¨‚g¤‚gN¼´l  pF"pæPAp3RÁk)¶«`p3§‚›‘‚›‘‚›9EÜŒDÜŒDà̀)‚g¤‚g¤‚gNô"9#9sª È© ¡ -‚9ÚŒT́ŒT0ißS÷¿ñhTA 3RAGgN :# :C $:sh Ñj Ùj Ù™CÉÎ@‰ÎP‰Î"Ht†"Ht†"HtæA¢3A¢3A 3‡’¡’¡’y×@ 3‡‚œ‘9C 8sJ ÀJ ¸I ¸™SÎHÎHÎœpFrFræ”@3’À g¤€ gN9#9C8s* À) À)`RÖ󣦇GdKÎHœ9ôŒ0KÜ¢îđ™C‰ÏP‰ÏP‰ḮHz†Hz†HzæĐ@̉3Ô@̉3Ô@̉3‡’¡’¡Ÿ94ø 5ø 5ø̀¡D_¨äg¨äg $?C T#Đ@4‡‚ ¡’ ¡’ Ù  Í¡h¨h$hN @# ¤SAĐœ‚†€F€æ”@4’@4’@4§ ‘ ‘ 95440iÛ}j ]ÉÏPÉÏ@‰Ï"H|"Hz†"HzæAâ3Aâ3Aâ3‡Ÿ¡’Ÿ¡’Ÿ9Dü 5ü Eǜ!‚äg(‚äg ‚Äg$>CÜâÂÓÅpĂù²&>C$>C$>s¨ ª !ª !CÑP ÑP Ñ́"H††"H††"H†æA24A24A24‡’¡¡’¡¡¢9D 5 5Í!`(¤h(¤h $EC T#@r4‡’£¡’£¡’£ÙE E EÍ!‚Äh(‚Äh¨‚Äh$FCGC$Gsˆ 9ˆ 0j 1C‰ÑP‰ÑP‰ÑŒ†HŒ†HŒæ@r4”@r4”@r4oHŒf—@R4”@R4À€h$D$CCCsè!ê!ê!CÿÑP₫HÑP₫HṆ̃G†úGQ ₫‘¢9ô ơ ôÍ¡„h¨„h¨„hưK€h¨  ơ/¢9ô/¤¡₫¥Fi JsÈ_êZ°¹₫% 4Ô¿”f׿’†ú—@̉PÿHCÿR4”¿’†̣—@̣̉—@̉P₫HÊ_â.f—¿D/ô/q+ưK@iLÀ`(€ , 0¥90¥¡ª`Ms`MCLcïeưÑv“ÓPÓĐ9´ `Ls`LCL€i(€ 0Í!€ 0 0Q‡NÔ¦90¦&À4”À˜æÀ˜†˜ÓP`C`j`LC L€i L i¨ 4 50Uí¹O $L³k Yj Yh QC‰̉@ỈPỈ2H”†2H”†2H”æA 4”A²4”A²4‡ ’¥¡ ’¥¡ ’¥9d, e, d(Í!ƒDi(ƒDi(ƒDi$JC$JC$JsÈ yÊ €Ê C ÔP ÔHÔ2F2x-ÍvOsÊ xé xéàµM–‚§‘‚§‘¨9u@t@t@Í©ƒ€a¤ƒ j¤ƒ jNQ#d#ÔÁ`jNS# ¦F*˜´í©ûŸX¨µT0éD:THÍ©‚@j¨‚Dj¨‚Dj«FYñ¯:˜ ™CÉÔ@‰ÔP‰Ô"H¤†"H¤†"H¤æA"5A"5A"5‡‚©¡’©¡’©yÓ@!5»¨¡ -¨ -¨9tP@ „P< •P<Í!…j¨…j(†j5PC=QCAQs("‰*¢ˆJ¢ˆCEÔPEÔ@ Ôª( †²( †º( æF5Z¨Ñú@͹>U£ơá½X «9ˆÀj´A¬Fûc`5Ç₫Ơ°¸=U£í1¨s{TvÇ jä"UsºAƠÈEªF.B`5§‹X\„Ø!ÑC¬æô‰‘‡\<„àjN!¸yÙ=„ kN!ÈùAÖÈG²æđ¬‘`|„kN!ÀmÖhu°æ\¬ÑêdV‡ ka$YaXCeXsH£Àj#Áj£ÀCÖP ÖPỌ̈(²†)²† -)²æM"ÿÛk®`«N²æ£e‘5&Î÷†Ö\6ç¯ ú@k®đƯ­±Qïbk®đÆóo‚­¹̀O3ÄÖXĐ~ Žăv¶+°f~‰y…`Í4GP)k›»VT0Àçf%Ëk›ñ7I°æù÷ưÖLÑÊḱ¨ún«èŒmÀ̀£Ê¿q²ÆFư5ÖØ‘y»ªÀñZÎ#ÅdƯzWÓ- ~.®¦‰¹'–Đàj£=6¹›M-~„\ͦÏÍéo¼[üÛ΄\Mw%[\m(T’`mM,ô`}i^`Í°î(ú£HoyM`Íđ:aI`ÍÀŒ°ÆúÊâ Ö /––ÖØÎú"™K°fx5·,°f|‰oŸ`Í´̉uyû£ uG¹k¦îT56”WÙ€5tdµ)¾!'Xc±B6‹ë—̀³k{ -[îYÅg!0öÂ8Ö<_Ă@U›ŸG̀ ¬±oµWưQ8âlù_Wó¼̉fƯ®&Ù[[uˆ‚«±%z.‹«±µóƠW“bXƒµ8ûïN5¶ -¹ëköjuklTκÀ uÖ\̣¬¹¾€Ÿ ®æ²Åu]\Íuđvˆö²¯#·ÅƠØE¨EăcÏ¿óø£ơÂ₫ÁCgIÖPgIÖPg‰Ö:K´†:K¶†:K¶æZ²5Z°5Z°5›Đ¬‘ÚX#±Xs-À‰-À‰-ÀSlÖHlÖHmAÖœj ²†j °Fj °æP[p5R[p5T[r5‡Ú’«¡Ú’«¡Ü’«9ä–\ åÖ¹j-¹Ck ÖPk ÖPk ÖZK°†Z °†RK°æZ‚5”Z€5TZ‚5‡̉¬¡̉¬¡̉¬9”–T •` …`Í¡³k¨³«‰ĐY‚5‡Î¬¡Î¬¡Î¬ÙuV` tV` t–`Í.³kBfÅƠ@fÅỚ:+®:+®†:K®æĐYp5”Y‚5”Y‚5‡̀¬¡Ê¬¡Ê¬9T–` T–\ U\Íu²vÁƠPdÉƠPdÉƠ¼‰¬À]ưDÖ@ưDÖ@ư„Ö́ê'´ê¶Ú'¶f×>²5>²5”>²5KúÖPúÖPûÖ̉G°†̉G°†̉G°æ>‚5”>‚5”>5‡̣‘¬̣¬¡đ¬Ùu\ e\ e\Í!{äj({äj({äjÙ#WCÙ#WCå#Ws(Á*Á*ÁCùª̣Ú„̣¬¡ô¬9”` •` ¥`Í¡|k¨|k¨|kå#Cå#XCé#XshÁjßj$´`Í¡}k¨}k¨}kví#XéXéX³kŸÀĐ>q5Đ>q5»ö‰«ö‰«ö‰«ÙµO\ ÄO` ÄO`Í.~k ~k ~kvơXêG®â'®fW?q5P?q5P?q5oê'°fW?‘5P?‘5P?¡5»ú ­ú‰­₫‰­ÙơOl l lÍ@‚5@‚5@‚5‡¬¡¬¡¬9` ` dÍ!$k k¨kv$WC$WC$Ws¨ ¸ ¹ ¹CÉƠPÉƠPÁƠH°†H°†H°æĐ@‚5Ô@‚5Ô@‚5‡¬¡¬¡¬9T` E` E`Í!‚db(‚k(‚k$XC\„¬9D` E` E`Í.‚k ‚k ‚kvX*(®*(®fWAq5PAq5PAq5» -« -¬ -¬ÙEP` DP` DP`Í.‚kBÅƠ@ÅƠ\'s\ TP\ TP\Í› - -¬ÙUPd TPd TPhÍ®‚Bk ‚bk ‚bkv[$[C$[³T` U` U`Í¡‚k¨‚k¨‚k$XC$XC$Ys¨ ɨ Áª Á]ÉƠPÁƠPÉƠ"H®†*H®F*®æTAp5RÁàj$‚àjNX#¼(ÙPÁkNX#X#Xs À‰ À‰ ÀSÖHÖHÖœ*&F*°F*°æTA€5RA5Xsª À© À©`̉¦g© Áª Áª`€5‡¬’««Ù5P\ 4P\ 4P\Í®âj k ‚kvXXX³‹ ÀAq5Ar5×IÜú·ưW Wó®k $YC Lô„­9$h %l %lÍ!dk(`k$`k6 X# X# XsJ ÀI`€5R@€5§¬‘¬‘‚¬9d ``Í¡€àj¤€àj$ÁƠœ -®F -˜)n¡€äj$WC$WC $WsH ÁJ ÁJ ÁC ÖP ÖP ÖH°†H°†H°æĐ@‚5Ô@‚5Ô@‚5‡’‰¡¬¡¬94` 5p5°æ@€5T@‚5T@‚5» -¬ -¬ -¬Ù5P`Mh ¹J ¹CÉƠPÉƠPÉƠH®†H°†H°æ@‚5”@‚5”@‚5‡¬’«¡’«94\ 5\ 50i×ư¢kv Y YZ³‹ Đˆ Øˆ Ø]ÅÖ@ÉÖPÉÖ,$XC$XC$Xsˆ Àj Á ÁC ÖP ÖPIÖ"H²"¸̣‡¬ÙE\ E\ E\Í¡‚äj¨‚äj¨‚äjWC$WC$Wsˆ Á Á ÁC ÖP ÖP Ö"H°†"H°†"H°æA€5Ô@‚5Ô@€5‡’‰¡¬¡¬9$` %p5H°æĐ@‚5A‚5A‚5» -¬ -¬ -¬ÙEP`Mˆ ¸¨ ¸]ÅƠ@ÉƠ@ÅỚ"(®"H°(°f×@5Đ@5Đ@5»¬ W WsÀ]DAÅƠ@ÅƠ¼I À]EÖ@EÖ@Ö́(´(¶H¶f×?±5Đ?²5Ô?²5KÿÖP₫ÖP₫Ọ̈G°†úÇhBêÁCÿÖPÿÖPÿHÖúG²úG°†úG°f×?r5Ô?r5Ô?r5‡₫‘«¡₫«¡ü‘«9ä\ ơ\ ơ\Í¡k¨k¨kưXCù#XCù#XsÈÁÊÁÊÁC₫ÖPÿÖPÿÖH&†H°†H°æ@‚5ÀƠH ÁC ÖP ÖP Ö́(°(°(°f@5!€âj €âjvWWW³+ ¸K0R€5@5» -¬ -¬ -¬Ù%P`Mh ¸h ¸]ÅƠ@ÅƠ@ÅƠ¼i À]EÖ@EÖ@…Ö́*(´*(¶*(¶f—A±5A²5”A²5KÖP ÖP Ö2H°†2H°†2H°æA‚5”A‚5”A’5‡ ’¬ ¬¡ ¬Ùe\ e\ e\Í!ƒäj(ƒàj(ƒäj$WC$W#WsÊ ÀÉàEц ¬9e`t`tđâ¡É®ƒk¤ƒk¤ƒkNX#X#Xsê ˜é Àé ÀSÖHƠt0ÀSÖH¬‘ -&íy– -¬¡ -¬¡ -¬9T`M¨ ¸¨ ¸]ÉƠ@ÅƠ@ÅỚ"(®"(°"(°fA5A5A5» -¬ WW³‹ ¹h ¸h ¸7 \`Í.‚‹¬ -.²2¸Đ]Z!\l ”p±5».¶Z(¶†b(¶f©¡Àê¡À -¢ÀC ÖPÖPÖ(°†¢(°†ªH²æPE‘5E5ÔE5»0’«Ñú\Ö‡àjÎơ!¸­%zX ‚«9ˆàj´A®FûcÈÔ¹?°FÛc€5Ú¬9·ÇÖhw °F.B€5§‹`\„kä"XsºÖÈE[$yÖœB01̣¬‘‡`Íé!X#¡‡`Íé"X#!ÀùÖ>B‚5ô¬¡`Íá#$Xƒí1¹®ÉƠ«Cr5\’«áê\Í.Œâj Œ ¬2.°f—ÆÖ@Ö@X³‹ăkBÅƠ@W³Ëăâj ‹«B.®æ¿.<û_¬ñ+çÿ{Đ&{È_>A›lż®ªX÷ç#öƒ5‚6ÙBºÇ -ª¿›Ơj²hÖ°1x+F<8›ŒÏ–µ´ÍaZ8›ls>¨¯ă₫%̃ß×ø#û÷£Ư@kl«ήÏ*}V è³Jà́ú¬ú5Đg–À yfœ]Uê¼ZuVœ] ê@ÉéPÉéêLP‡êLP‡ê Pçg‚:g’:g’:‡:“Ô¡:“Ô‘:ƒÔ9Ơ9H3@‰3@SœÔ‘6Ô‘6Ô9µ ´¤´9HSAêHAêHAêP×z›¤Î¡„$u¨„$u¨„$u%$©% P‡:HPçĐA€:”A‚:”A‚:‡ ’Ơ¡ «S$ƒ,‚³« à@U*¨"8» -ªTPEp ‚¬‚³‰ à@ÁêHëœ\G -H^G -H`çT@;—X) aC ëHIëH‰ëœ"H^G"H`G"HbgA;A;RA ;§’Ù‘ªTƠpvT5ˆ ªá@U gA²ªN¨ á́2¨b8ÁƠFÈ á́2Hf2Hd‡2HdçA0;TA2;TA2;‡ ’Ù¡ Ú¡ Ú9dĐeĐeĐÎ!ƒ„v ƒdv¨ƒdv$³C!$³C!$³s!™ -! ê ¡C íP íP í¼ !¡C íP íP íBHhBHf‡RHfçB2;ÔB2;ÔB2;‡Û¡:¦Ó×®]ơpv1T=ˆ¡êá@ UgCƠĂªÄPq65T=¨!±É!¹SIîH‰îHÉxç¶#=$·sè!¹é!Áé!ÈS‰îHÉîH ḯrHvGrHxGrHzçÔCâ;̉Cƪ0Î.ˆ,Œ=Taè¡ -ắz¨6ĐCÆ =T]œ]Uz¸Ú=T]œ]‰ï@IïPIïzH|‡‚H|‡‚H|çDâ;Ḍ;Ḍ;‡ ’ß¡ ’ß¡ ’ß9‘üøơøΡ‡Äw¨‡Äw¨‡Äw=$¾C=$¿CA$¿s"ù -"ù -"ùwA$¿s"ù -"ù -"ùCÉï@‰ïP‰ï‚H|‡‚H|‡‚H|çD<D'vü@'Q¥qvATi¢Jă@UgD•Æ ª4Qµq6ATi"  "S ñHAñH‰ñœzHçÁ³Ï)ˆ—qÄ`x–âyDPĐCđ=”C̣=‡’ï¡’ï¡’ï9ä|Ơ€Ơ€Ï¡†|¨†|¨†|̃Ơ€Ï¡†|¨†|¨†øZHÀZH¾‡Z¾çḄ=”Ḅ=”Ḅ=‡ñ¡ⓤ„ª³+¡jç@ -U;R¨Ú9»ªv¤Pµs …*³I¡jç@ -‰øH -ÉøœRHÊGRH̀GRHÎç”B‚>)!ŸC ÉøH - ùH -IùœRH̀GRHÎGRHĐg—Bp>RB‚>RB’>§ơ‘ªˆ”PEtv%TH¡è@ -UDg×BƠ¿Y¥‚[ -T ] UCZ¸Ú-T ] ‰úd•ăÂé`h!IŸC ‰úP ‰úP ‰úZHÔ‡ZHÖ‡ZHÖçĐB²>ÔB²>C²>‡’ơơ¡ơ9Ô¨Ơ¨Ơ¨Ï¡†D}(‡d}(‡d}9$ëC9$ëC9$ëó.‡d}9$ëC9$ëC9$ës"Y"Q -"QŸC‰úP‰úP‰úÚ‡hÚY¨2:»"ªŒQet ˆ*£³+¢Êè@UF¨::›"ªŒ‘´‘¸Ï©ˆ~¤ˆ$~¤ˆD~NE$ós‘ö‘"÷9‘¸‘¼ÏRD?/âg)â%‡"‚ù9ÈÏ’D0?K/¶́’êgI"ëéPYOçDÖÓ¡$²%‘ơtId)J"ëé@YNçD–Ó¡$ª H"Êéê‡Đ1qO´ "¨ "¨ "¨ŸSAưP‰ưP‰ư‚́‡zH́‡zH́çĐCb?ĐCR?ÔCR?‡’ú¡’ú¡’ú9ôÔơØơØÏ¡‡À~(‡Ä~(‡Ä~̃åPØÏ¡‡Â~(ˆŒ®§ ú9Ñ©Ÿéëw—D‡~b£_é{ -èçE‡~{KCưPưº(î‡ÊhÜ:BWA]UPÚ¸ -ê@WA]û x̣¸ -ê@UQgÓÇUP)îG -)đç”H¢?kÑög-ÿ¼,Aÿ\Ÿ’Ô«F€?çªàÏÚ@Çí¯ư3Pœ—ưs°?kû øgmŸAÿÛç€ÖîôỊ̈&ÿyñ&‚ÿQˆ¸{%ïuجÊ:‡7‘•uèMD:YYçp&¢Ú¤¾vÖ/‘uu_"ëêĐ—høOƯ|‰Ü!ÎD¯$}$ư#o"èŸÓ›üG̃Dà?̣&ÿ9½‰À´{ÿ£#øŸsÅ₫G+Fđ?Z1‚ÿ9’üRø%RøÏ¡‘Â(’ÿÄAw¨¤øŸC&Ÿa&‰\ê)öçĐH±?TI±?”I±?ÿäaâÿÂ”ĐŸ?ÿûOÿ»û?₫¹·÷÷óŸáÏ₫æÿ?éß|úWÿó_ư₫o₫Ë'üû_ÿúW/†(fé´Z€ÿ“j18ök¿[÷um÷ơß?ÿ÷ÿơXÿáSùô?~ú_ÿ·ëÓ_ÿÚ́ÿË_`³q´ûc7¢ñOßü¹Î_<üóoµéÆ_ân‚²—\]<ïU_=:úwÿ¿̃ƠßÆ]ư_¡ư¿ÿßaưôOG5+¾Ö#6ÜâÛ -°đÈu?EÖ>‹(Zq[¤̣ëMùGĐŒ»̀»nî±f»Ưæ!ÏöOÇ ăđèñÊ%₫ífüxuLfZÔA»â Üp¡b©ª}̀°v;ơØÊfÉVá-ỳ›£<ơ ’fg*7æóÿàze½OjQ¥O=iIá"ࣖG+ªÇưQyäG-Ï4֣܃?j)ưx<ªGÄ´[êAtMOZê£IN¹?éÓđ´“>kÄ™èQs‹x*>êw=jº­¶W95Ù”ÓºơÙÉæEüQ}Yèơ₫¨æ3«Wâ£Úî·Ü·Ơ¼vÄxË°êqîvd₫ôù=nÆPZÈêíaVÍÎ6j„çßÓbµêMZ,\QH‚yR,kă„â;÷f-÷̉#N.[•! -uå´öpªf£ÚVîÙ™?o»ÚK«·åaÿư¶{Ü̃\¹Ă XSP|Ï/ù~nwöüûYá®Z¿æ¿^Pßạ̀¨ÿAæ®́¸ø §wœy=́Ál̀ çÿƠ JdÏÅ:ವKQÏÙ„9ÔØsÙÏïzö\¶ơ‡ÅÀ ç²}éÏ]°ç²­i7wô\¶SkëNôœùÚ-¼—=gÇ1Í̃Ù̃sæŸ7§>{Î~,Y/:Îî9ÏË#®eÏes$ªËæÍăèºçdzEáp̀é´ÑuÔ˜Û8»Î°u mÅ3opµï)2‚ZècĂ¿’Z₫ôŧ̃®Åfíó³u¾"¢ªtº·/ç>Ûk‰àéeÿöj·c‡gmóíµŸÙW;¿à>ÿ'ûÿ.ßơ₫Ư­×±ƯM&GënÜơfw÷́Gwií<ö¿ÿ”#2êùO¦ÇÏ{ư!ûåƠWî¾i³Å$»Ñb¡Âèägíg¾̣GçP˜ÄcËY7ïaa4ŸX4’¯gÿóá½}ßn»ù̉Ñÿ“‘ʺ혗½́œ<ŒÓ²Ä>Ÿ±Ís£qO~ñ•-¸Ă4Z¶…~Η×~{éÓ‡wđ•7w›»1åí÷~È~eöÔ£®ŸÂèE`,´á} ¶¾²4/Ü[2÷0ZEââoaƯ]±°Q4â>Ô´Ẫîv'ñ¾,ÜÑ`v·W :ăe.®̃K¾gñÑÑ2~vuU³ÈÂhù™ÆŸe{e#Éû#6”ÑÈ£‘̃!Đd8nt‡q´löRđ,Å>`´jÓa´40>w cÙs|ÛeÆûYƠ…ñ‘º£ăOa4O©n!_́ KƯÄ‹»5Íz9‡ø­Äî±È¯›·Ö́ƠŒ7ö`₫sæĐc»Ô‚C ›À;»Ç†:Œ–O,0êy-½xÅ^b|ÛÚÑFø î>Œ~W+1¦T¡±̀~%AăÚÂèstíÄW4>]Ăơ9?\‹:3£ç|®g*ä‡kĐøv‚}Đ¶È‹í” ŒQ$ŒéV Î"ùo-źmtJ&ñÛƒáYĐèñ3¾ÉÇL)4ư½¥¿ö†SBĂæ™C/ÓŸ#ŒÏ›ÂØ}dPƯó̀çePÙ1Đ­Ù̃3.¯µP(»F¢œoÆ3ÇGaNKƯC’q®RƯ¶Zè‘/.¶T=xÇ…½sƠ¦=Ót¥>‹¢™zÇí¸Ø#ZÏo;ơ6·f5N/[ƒ …ù₫UPV³mIªFIƠH6Îíæ§b§̣ftîAOQº$4C÷œ8–ç;S„*¦qÍŒ}µđ*Âa_S‡‰˜-iüÛw/؃êùÙ>P ªoáñKPÍ;’ª¾—øfïÈåÇÏ8ÄÁ…ÓGuè¡Q³vFu´Pf€dñ>m#ø}ÓpO6›ùpá₫äCú̀(Ưđ cÔ¶̣{°\lh¡ø¹ [¯̃èúªơ>Ăêœ0§å‹S¸›ơæljî©æ÷b%”ÚR4dMµh×\9¼4=n= ]Hj(µ¥!²£Ócèæ꬜4­§£³_ƒƒ}§qkƯ¶•ƒ×ÛçgƱÍn¹DW$%°‰A=l#ÀÉ׃„ÍØbîˆ˪1¶N}ïha^–Äïe 6ŸeγÁă£æ₫tÓÊ}5ê-ômÚséx–¼—íœ8¡{°²ïm½×øq=ööÅ?1Ă=CÆŒ3R ¸qZÏÇhkƯÎa]{¬¿̀n‘ăU~¶fÆ `܃(fl–±€FKˆ,(—Ÿ¢of¾Ñ^ óĐh3›%'àđñè›0ú)ơ9|Ü^°"¶đÁG¹¿Ñ̃+_©ƒÑ$ƒ³§å…Ñ̉w¢Û¨jn·4”|Ï%±eKÈIAè«å»O- líÖø³U¯‘êå?ĐêX­öÈ.ö(n齜FüÜ£(Z wפ0f~;?E <çÍ)O›á^o´đ,hEÿ*« -×ô±Ó¸ôƯŹ?{ºº]| -³÷©1q\́QơèÈ\aLµsD”;¯®Á/¿Ù½}ă½M©_‹/έ«fåÏỡ¶§s¼6́"F§oÆ–aôÅY±Ư§G̣«…tÙăAÜØ;Œ¯̃ƒnó₫–Ïg«q’¸]Ñ=ŒHà-¤kkán;lÇT5„³3§±cEHØÆÆ#Û[ ăÄêkÆ2AŸƯ5¨Œ~N~üi,3פ•íJ´P´«4ÿ́5¡)¹pHL‹j£bà]´ØgÄÅ©±ß,¤,¤J·ë’êe°̃D»ªÍÉ̃i?ûÉFî0–F£ J´`̃¤„₫M›Èô³p¤0^cb4¤%9¡Â"œZÆ3äÖC¤œ;ä…½ê;¾"U<ƒ% c×]»2ăÊ”9L¯C æøb/ÓÉÏ2Æ!˜±0C –} iđÅÇö̀9Ï€=cANf¬±¢+--ÔÈ~ö™½.;Ñ͆Ôh¼¼¾g\Ù8ĐÍá8#•J t›ou»oîcÖ&“ íhªAí‚&Ï'¼ÑÂDWu cÁôëQ.ú¨nơđ% î™Ơ-Ơôd³đú̀ -¨ĐXD̃b\lä[ön±Ñ7ăuóÆ,±#Z¨6h87ø·ưŸÎ‘sÂSÜh‡&”³4µœMÉG^̃±yEơe„Ña¬obÙ4¯HÖ·ÀáöÂÏ¥—Ák ×Ưæˆ0.§€±H›–kŸÂèY|ñaƯ‰‹4­˜}·…LkØ‚Ÿ§üÔƠF§¯¿jÎ̃n/øs_fcäÅNtôÍMdá̃l [‡ˆŸ|£}-ƒ'WMa•ŒE Â8»DªÀ¶FÎ5ÔeeW#xG†…cr›lgüø¶{Œ]̃Ÿñ~_‹_¢=›eèj\?;â Ââ~gtY½7ßgºµzo[Lo·–"'&»áw¯WPïø¶ÂySÆöÁ9v-[”ƒ–;îøFû’Đg_é}ïÑu_S†Ñq¸è¡¹åîiüÀj—°O;ǵøˆƯ½…7h΢R"¯R´+¯Y±ÔeÜœù̃Í̃qöv8˹H¢{b ·X-ØÖë+[¸;¿dÇñ`¬7ßHĂ#?Ăêæ!FµƠ'ZÈÈáJUŒéæH©pzY$¦{4‹®ˆ_×4ơ:î¹-½|mÆŃ!vî›ă‡Ô–+Ëư{\s·åæ°ÓùÂOÄÇá²ß“;ÍÁo|̃ XÀc㮤{ßi~₫èè@§ -ïÎíË^?p°'í¦6c¦KÀBä_¿+û©à»4ă­Q@‡÷G₫ơ¿åç¹[®µgÀ^¹$đMŒ³r ̃±¨ư[§¬Y ëAoöÖ^¦©Ùóâ4ë/ư+å¹ù¿ư¼óÏùëó´ó‚ºÙ"[_OCÿG¡ƯÏ€·…Uó˜ ÏÏz¯—7û·Ó^ËôäßÔ»eÿÛưäó‹³K=#·ÿs½‡cÚŸä(à·ñlÅÂ{<êóĂƯ’úYÓ¶$x¾³ç≛ă{ä°{b 3¦à£e'xFw\ü4̉̉€½\¶âM›=¿¤ëüÜ₫±?Í[¸¼5ú4Ơ­*ú3…Ï#.¶ơ„Q.̃ˆ§ƒŒu†êf,ßơ‰ws´±j|̀Ô7½a<¹Í…̉`ô¬ ôW|Öµµ:e₫N? ƒ-lrF+ư‹"ÑÅ”›Z<`G¯ÙË<Ó₫“±V~Œåw5³µ§ÇSƯfCăuï+V§fL±î{ŒƠK[ü6~Í´Đjœ¸¹tïöfó¢…?¸ÑO7ü¾zïw]XĐE2yA¿5ËüáĂưFưñ>ÔŒ(±‡«‡‘ÑƯ&êŒ[vÚ/Œ~åWSüÜ{O|e ÅÖJ̃‚Óªñ NLóQ –f%ƒâ][í€Û[vWÂÅÅ6|6‚̣9ư†­£¾²?>0²÷‹MĂX—¹vƯ¨û}{â?:\G|&.{'æơ6÷µ:És´?öO.®~1”_dôËÏïŹL¯ø"ă3­ÓräèæÓÇhÛ8¶Z·8³nö'XÑA¢ÙăÄ6µ2"ƒ“_é bôR½Âeó\|Ç2ƠŒ`sû {θáÆY`E4¨å¯”&Ü[3°g=ñÑ=¿ÿé€ë~ÎÆij¶{_ö烋ºn÷“ Gaù…Ø°¸ ̣ư}\\â§W3údF[…Ù|B -­‘b[gÿH¹ö₫tjºù¶̣`Ë悬3 -·H'ïÆlI\œîđH„BùơùJ/ ¸̀ñù9aúÙ²e[Ưđ…̣¼đHâC|ƃä+ÊE¹Đ»—çĂ~ûcöç¦gLQh"ºÚÙí8Áo&ö–₫,ªÅg<½ÿ» dª€—´ú^Ă'ûØ-jËÑ/ïßxîßƯ!̀ÚÇ}ªÑI9…/ư1ÖØÿÆ}9~m>NË»ƯÙI5rØzøèûđá~®rƯ½¬·ßÛ^Àè ʴ͔ߛđ| -ch‘©F1zñĂøʬ"W4ºưÜœ\ÎFù́«Æù?ó·ú†Ơª₫ô²:ÉƯĂn~D®|âµ6»WÛ›— ßO“TWß½ÙÅ—•¡Q¤7è)£üJ#Æ0¶|A|Ưñ4ƯBĂ­?päÏ®´̃-Ac—•D7ésñÓÍFù£GÍÓëOáß-Û¹Ơ«Í" øN»åŸuí+ñÙ~Ô9¡q¿ûɈ˖+·÷ĐÄÚÔ™å£dŸ2¼<— 6e`]ơüâ'ô QưcN$[úă+¹]®mÖ´®̣ç›Ï†ÓÄ‘w»C–1`ç•1úÙÂ3ÙW‘Ÿ‚§N°yäó̃ø­K-8´v₫åL©~ỮV›X<ƠøºỢc¾Ă—9öt₫ư>#nÉ[Í™đ›¸Vƒ₫).å6sc<¡ îƯ₫-—¸Ăû²Æ wc]èÓ¡Á Ñ9®änóNP÷ÎË–›c{AØÄX&‡HI¯8ÏaVPªÏFJư¾­ÅWnéf|)ÜE¡æóÏ oö,Jö®Ïà+ưßp¾qƠoăâwdµ.üó́w$äû-5/Ô–Ÿ)=[)“qÇ°ôƒ¾Ç*}Yớ;́¶« Ê•Oé1i>ö;ây¬›‘œư16ø„­¶„ĐÛ‹¶%Ơ8ˆ!äÙr…ªD ÏjŸ/ª!˜Á>„êd­sT´ˆÏÔª'•ø"s”–ôƯV·Œơ͵³nsj‹“̃ÇøôF1Q¿|j̣Ôÿ‰ßÿɳǗˆA¼ç½UCù _ S~±£Ø±A7רÈèF;ǹØ=Ezç90Í́©Ü;Ç›o´́̉+^í§J<––†¿†©/}E6Ŕˆ̉ f6K4no¢Ô›̣@Úx–¬ƠËœV›lÈÄjƠó-=wUâTÊŒÏ:¨ùÚ»:±@9=²¾ùhđj>q±Wâ!r÷øj±ÑyŒ%̃MÈÁóaOóêùÅ>ÑbŜ”Ö÷ưÈÈđ¥‡ñZ=vÁ='äFßÁ}8v¿sôLƠ#¯5ÂÛ ₫ñFĂëÈù-W\ï£́Ăñø>x1¨ß{ 꾌¿¡Ÿ~p~~ÇvÜÏzùĂ÷ṇ̃æ₫Đ;~¿áÅn÷±Û‘¤÷äđĩ5¿N/s„É!ö`¶Q)Ô}²Ó]ïk=ØWœ%ßg?0C½ÍfÚÉ¿O}N’?›Q?œ~?œ¨6«´øp±đӕŇË,Î?›^>œˆ̃g­¯`ûÙ|˜ă$Ù[ö¢:aœ·3;añˆ°ØƯdüÍƯɹpÿXà6°úđ €épG³ọđơŸÛùÎuüă=™t³ư Á‹d.û·Ón£oM¥áÊ|7íưÜùŸ,gf‹|w¶¾åczZ/—; 6¦GwăßJ́"ưd.ne†«ë-ÆyF¼ñỪ=¤NÓ/6 Ưá¼2yÈ4úŇëGlËù¸"øqq·́%ĂăĂ›„!hß­mé¾̉¯æTªÛư0Ü€î«uÛˆ•»¡k½æÍø <ûÅ÷‹G£iµêç!q(øjímo,Çs@ñ\˜ëÍ}zéa;ơÀÜ[ÎÏœ#Ágh’Íú§qÆ™Ơw.Æ—Ưó…‘Ư`"Ú©¬^ÁrƠÂuå׋M2ưz3ja³g¬ÀñzCôî̉;Œ~ʲÉJ·s@Î(ĂÆmDbµ²l#ÖºÚ»{l”ê‹<+r¾¿Êăđ¼º/̣¸{DIƉX̃Z°ÇLl‹_ûØ{œ}V ´#•sŸU<± ö»-ñ6aTOzm—óâû|»÷è#ß×Ẩ"1sL ÓËʸ{¿Ä*Đ:s¤¼l/½]:f„¨y‹3C*öµ₫3=kÛB¦̀°G¬…ă„Â8?7¾³ÔeÓß{¸ÔË¥´iਛƒö‡phÊásÛm5…è—˟ϯoÈsÄăKµ˜ïˆW°ÊëËƯĂn‘"ç@¾oÿbộo£₫̃?u¶ -Æ{qJ ă’¼+§—‹sD8ÆùçŒăäÍØâ-́öêèTÄP´p†1w¼ƯQן§7Ö#VF pcf”Ënt¿ÚjAvÏçøÚ¬1ÜÀäÀsïÇW¶PÇ]c”^Ûó–x½^5­¿áưXvóñ·Á¼¤S\˜¢ùAûç¯̉\‹„{Ư•Ô©r{·±³Œnä†hpokFKh¹Ä¡Ä@ß>mßpd\́^‘0¦ĐV—¼ø\u\lœÜ9Ä‘Đ˨ơ'Ó²æEß̣¬W̃ŒË9êßØqqœ+<¼“Ñ^å¶SÉe§¬¹‘}†Aw6NÎí₫|Ơ=Tv¤Ÿ»‡Ñ3½Ä̉²`°§(+Vó–èUxŸy¸Åö©̣ÆB¸)ö]BZ€€«'ûÅ%6|í₫[8‰MZo›Q.^/·q^¼¾”f¹ƒç‹ŸƠ÷˜Qâ6Â~mÁüCœqÈ@û)ÑM»Ü͈¼¤/d›1=Ï|4KA¨A„EÏ×^1;×q¯·Tg^59»qª…ƒ/c±Pèï«Ú=O{j̃ÅzhXê ¿Ùl‹Z'á;0;V䶨Ê7Œ7v :LqƯC -ĩ.FzS÷Đ ·¯,ÑW‹xÊ9‘Td?c˜];±g 8/zªÜø'Û±ü9.¶…@‡· ¡Ăă îÁ³|¥×°áăHÚđ{‚5ÑƠ竱(™Ùi_[GóA_¢ª40ôdÔSÄáĂqqCˆ´5ëeÜO#\$:Xvw©Ó80Hp̃íÆÊQCÄ×Ù̉h“_sÖ";|ˆVˆÆ8»QgÅùơâRèđ Az´Ø¯µ{+ØydjÜp„ïûÊ{$F:Ü5µÍ¨wÑÊƯ΋ŸÉ­M×,#¿ zÔËÜÍ.¨{›a­VŒwËc3ê4ÀÓ6g?z«æu€û'˜½́a\εôa¼W¨Äa§sÚ0–…÷]<Ø…¯F=°\?/öTipFD^‚ˆoé̉ ̣i7HïX‹${Ÿ}ëèĐ\‘»Ñ‰íH—Ưg’0:ŒéFÚß~´#£î¡_½¾\l°[áÏ] ô5…m½µ;¿\R#₫½Óé¾ÍY’·÷°́n43*$W™öÆz¯›Q-`3y^<ÑçO³…b7›äírăÛCßéÂÅm,£Y©…ÍÎnD†}WH́¾[ïë jÙ£^.Ûu¸Í,Oó¬ûxô½—‹‡Çûy³£Œ£1$í^-,;C&¶±`ĐL§­M>XÖ©ä6F2ăn·ÛƯúvïắbÙ7¯wmÔ3D]'ϼ=¨̃ü s0LđºO?`ѧQslU‹-€-<Y₫͈¢̀ha·»â`=d–ơVÈK2ă]–-”m HÙ/^±%’¹œªø­Ñʽiö‹EÀäaÄÜÓ-ñp›Qơ ÇyÛчävA@»qƯ>°›ëÎỜSB°e\‹Å|×—‹[́Ô1̀nF•Rï͸–Y‰‚ÛǺ̃ªyâ0:Lôö.jŒÎ0öqăc£Ïç0:l¤–ưæ­E -ÙØ¿ö¼ôa®#>[èá ­K-ßüÄ·FDz?Đ*o™#y¢':ÑBŒ̉›íÛßÓl[¼„;̣Å{ lêÀ`T `•÷‹S¤†ñ_cÆf¬qº¢}Dz{¾åÏÛ¶̃‰Đ̉¸Çmé›q9 ¯<΋më'%ÇỊ̂mªª— ‡Lø³´g¬ºsd)ÁœÍ5 {²À^@¾wD7è{m–ïđÀÁØn^LƠ±Đà²ơµyª̣ób ún¤_ÊѦXÆñßW6 -å3XWĐr¬Î·cE[Êç–7ă -çœD·‹=ϹŸC1̃qv¡hÏH?êƯ&}êÿû›ă®#ó«O¤îüÁÏÈ3©Ä[Àq{¾v¡́Æ.×èaæI±p¹øÇ%¶Ó6Éi·æî̃øëÎïëóK4a×ëHêQ&ăæF«ø‡»çA›Q³R)×|¹¸`ùèï:ß8N-,;Oœ¬Ù™ár镶—7°^±…́?,ü!{“öÁùêαG>ŒYëº6ơ}qmß8Lđop…øye€đÛ\}¬WTsâF›nU‹ø‚ Œưđ!âù₫ä/ÿiB¯Ë{èơ¿ưÇÆ_[6¢›á̉¥đxàB6ßeÿöb7cC*À½ŸÙ·v~ù £>Îäce<Ȉ;a^ -÷¦ÆewÇw l??#ÈZ'羨A›±ă¶3«y*kpæG¿®¡xÙQ}Çå·Wt‘¿g?Ѽ"àÀŒH7cñ ÏăFŸŸâî̀zñ`cµăßt•pp‘«̣‡̀«´N£c j÷Ÿx̉ÑkæO³²ƒ -UÁº î××"ßh÷œ´GyIŒ^.¸ñÆFgFæT´lv}s•ïçóÍW>h́úgƒ²µP»̃t́ =Ê€÷@ï¦qp }³ƒ-/"[¦œF,‘]á©éÂè…ƒăyç5V œĂ |ßh§Gvz–q³Ư­™±4c ߣ=Ÿ~́¶³u{y<Ư‘!lwD!˜q̣Îög+QĂ!O _µ×Kgüú¦GÅê¸Ó[ÇÆ–ê ¸̣7ăÍ»ÂL“úædó"æjÀëbô^ÄyÛd¬gÿ̀èDjB đ‘ÿ§Ru£gnP„ °”=¢.´¡7{„j™ư@,¡O¿y@”b´|@NmÄß°•Ë»Üô×4l‰[?8;RñÉô†;^üDn ïơÏáÉf“Z¨|è¼Gm̀%T°`q'đñ[Û2Øs妱Ư7?ú¤È€Œ˜½ơ‹h9E₫¤Ơya¼¥Œ.̀0Æj)]—S,Ó¢á+ܬϯÍÀ₫1~r\ x£Em§|×^l«ĐV*¿¡ë"V}€±QÉsƒ¸_°Ù̃.vË8X4뱨…Û}€…½66‚åµO ±Æ{ŒSsǪeOƒ¢ 3p„wX!CKÀF[Ë¡K ÛëvoaD:4[àv¤ê²ÍÁÙíR`¡Ù;Ÿz"FÅ2/Æ ] -ö¶foÍ°Û;êĂ^c¬uP -ÑÂEc]Æ–V ﮈëđ»Ă‰©Ơ[qÂÎ cË|û·Mèj¹^©¾¼£ u´€Öçc„ïưá<Édô%Ö –ạ̀N0ú¡ñgÏ…æDŒM¯ÿÑB¬̀˜Să}]̀¦tw ¥^đ\°+cÏSËÅÓ{̀¨\ä¶^4ă˜½²&°™́1î 1©=ó§̣UW Aºù­15ÁÅYÄc‹2µ|7Ï5K1ñY.Œ½d\̀£Ïâ>l=T-n9ưAê.‡' ƯÎ}öîk”,ơkăb©u'»'jL…1sL·¥©vk`=D‰ŒàÑÖđ–ơ·V~A³lĂºç^-TvC䀉8øîµ̣Ú»|C”E!àK3́JMZ¾$V #S¬[Ïœr̀~ó{¡wµØ̀×÷ă¥h[¾½íă -›—Úq‚ưI©Èˆé­̃ -(,L"¥ưF;|ö q¥á±ß_FƯ\9èĂ^ôqỜÛđ‚̣ë%ÁØ‹XëO³Ï‰0û ÔyiŒVđ±-Ë ­ÏЇZhÜSÙ=̀â-¨]Œ­åœÖgØVÀ‘%®®üđ‰„ÏXøsÑêPе²Â‡ư¢ y"Â0^Züé+´Jl“̀“‰&gdH4cÖWˆ¤f´¤đ|’{³7zÖ:"ج¤{½we£/œ1‘eµ0¹jß¼Ê$ ”¬ăÍ+=O’Z@–Æ„ăê0Â¥i4FµFE]Ú5‹D œ-pl5Ă*?!j V>ø©«¯âvßaÔ+b»đkñµT¢¥Ñª¢´«ÿ0Ç™ Ï7Úí¤‘“Y<œU®¿o _Bo–Èg̉Ó`ÙsĐpE赘Ë,G»VA¾‚ c”¹9n®l,-é(íå,÷ -#ƒS̀±–1*ñ#̃¦S³O=ó@JÛWq́a»iÙ„X ­¨™ÊY ´Ú¨/<Ơ÷[à¶_#~ I_@æ-àŒ#]ÚΛQ3€w½ZèÇ/(ơØC‰́!ülɉôĐ’ë®Ăü <Œé"ơÆÜ¿ˆ=nÎsµĐ®[a|*fDÄß%¯F-¡( rV;ûæ´;-¸èíEëÔyăuz!u~80£vœ[jkA;”‚Ï¥ÖÀÚ°¢ĐZ9_c-˜›_+đ¤³‘Z·©Œ 7z ­áEjC©n(5ürñ7Ù5®yZW’:Åb·°åg"f UÛ/Ó§•&f »–ú6%ă·äÎtH-D8DhxçÓƯÊéá÷ ¯s“Q–Çj·Ú«s.—ăÔ„Ü×ÎÅ’[%ú&¼îŒwѼ‡—o‘̣\iz(ÎjA›Kæ­«ô>/&̉ÂäËÀ´p­±·hv̉ëL$ÁŒZ>¸ª®<ä­=¼ƠĐëØKT¤̉+— Zè½çô|Ú c!Ÿ<#lH\îO6új iza₫3j–ö™?us¡Ú|På L€3¾É®5a_Ù‘*꘾̣̃Đ&aëËåƒđQ©_œ@=7iE•:¼¥8†®r<úâoh£ÜcØá¨có, pƯu kb v-b+êÑ`¼ËKYå¥ÜæŒmeܯj'_Üø²d,̀œ}©aó™4un+:î­ê¤ÓĂwF…/Ê’œç‹ÏWoÍưFnLHçj™û2«¬d³Ơ5)|[í¢?0­mr»´}Á[qD{·Äc;-L‘Ñ©™ï‘_ƶn2»3“÷¢0iè³ö)9ÑBg­>{Îú`ƒ­°áÎ7Z‡?vÎRôÚ¥₫?±ËÛP4gX™Óû̉<̣µO¹̉ĉ[C¸̀ëçiËËú¸xF–jÏ@¿6?M ØÇ^̣ÀøOH^c9ˆ± ?ŒÜ{ ,+t œ"0T£xo´€äcÑp»Xsêе‘RY_[û”ƒg)x³­V¾·_ûFû6Fla»ÅÖ­…+<_<¾‰Ö‚ †ëÔ·`éw]m-ܘê•­̃Ú́ùD¤Ạ̀ZĂ˜{[“Â,üÁ@^u¯gSD =ÊƠ­U€o?ß…i;…ÀóyáJøA^[æ¢=E>€Ïuq¬&³ÛäÂê« -2ƃˆ×ÆA`Z5<‘¸. üBbȸµDÇ@OQcc­ó›’ Ï2Wz̀p´̀â²Ñ\¾iQ5‰T ?´p7nØ–kÁ»¦¯+aË)V Ç;ε,s^ÅàS¨®oÓJ{•x áCjóó² P£{5À5úÖ—™§†ic·²"í=‡À¢ă§HĂZî—‡Ẹ̀ëÂ妶¹é¸ g•n®=±|P…£₫hL8˵e¿8o#ŒQWËwÓ1áJDÈc`pGI5”–k£+?øû̀Z &·—`¾åpÏ–Z@¢†t­ÄŒ$í±íJY0™ƒb§+ïmæU fđæ~–ÄO«̉…7XẸ̈^n¡mJËuØoF3§ßC×N91ø¾n~qŒ½7…¸×’Ç+á1HjC’‡U^…;ñsÍwù#Y*ê4̣ ,o⟑۹f·D­x—+ÉH·Ù7Ö4exüb/ühË!y‡°£¼ºq¥sô³<Îœ8³óp¥[aƯ+£Ấí*uµÀ“¡.á3ăúä —@aÖǦº\)|Ug’K˜†B,r«Ö¾µ»W•™1Lå”ÊCÔ¿ -ÛYÇ—ÔpùoƒT¬|Û–LtsX?:Ø"₫µc6É7]¯hA­úf¬|brä=ÏÈß=Âu¹f¡É\(ie}6AÁ†̀—e8ø­ĐˆmÄ£º`L 5 rH\‡1$ #nl½Đha\̣=­w7®maäªFzññ3–Ÿ>‰ÂWÔ2ÂÇÓ–›)i5`—êÍ)mߥsđç{©•wàáñî—Æ[àè)‹ÉK¯Y麮¨Y2´7Q ÁÄjiđ†Ûr.#¢qø±§ũl·̣â|°‹“’…Àso¿¥7¢…´9p‚ăy₫é:mØÉ ́–†ej!_ZóE&ï´»ÉY’K$̀p¢n¬,bR #i(03­³:#åo)bÙaK¾ăÈ|dúà€Fêïv¶h 'ÎÇ~&đv¯Êñ©ËŒ¨_—™›?ØWr<Yç}€đƒYÑí6yeîc¾¾8Ëp£f-纖6#¶^ë÷X-³‡8áÛM乯vpƒ3¥çºYw–¹œ$™àI€/măÀÂÚ8ơ±=[œ¨í₫s]̃ JR¼wîyæ -°_yJ -Àu¶–—¾Œ¼}C[Tºk:•!™çX„±uvn]¾:³§5Cŵ%*´”‘â¯2i©=́¬l¼L–å`rÏô#ö´úXjyñm¬ÄÀ4üÀ…°–ƒ,Üa‹2¶”ÄffMZË/˜ "1Gª§gñ/k^„¿x¿ßh_¡tyå@¿2¿z&G³ê±…+đM)Í~°sÏƠ7Ú×Ètÿ̀²+»3cú(bªr®Oä81[ê÷î›ư–W¤àÄÊ Á`ËU€ÓÙ'’+}¡wÖr&9°cËú|ëí#’ØRCvQ53m-@Ê=½Ÿ¹ñ(v³À³‘–Z ¬èµœ,„ñí“wP‰C­ê×Sá¤Ư~éy™5k†îÂØ’®i-©ñSŒ’Ê,´Ê¹|1LÀŒkú¶̃“/²$zÁqÆ̀{9{Uví*Ne©o¿¶@:B9¯äớ“ü9FƒXm‡¬4TH‡`*ƒ×°[Ï¥Lă·0ƒáÛRB”ë`è+*×.ÄQXđÆŒM²Ù™pÅaßöJ$*›k¶}´đ‡{©g%Sé·­<0{½a%ˆÂ¸̃È%½¦qh˜[Ư˜_0ă¶¼êœ©¯¹Ä¯I8d¾gÄ$i.Ë]ШíÜ¿i–:Ö^Ö&_Â+t̃ZÙ[ÈȈ…¨Ö<[oÜ*Â₫#bAÂèyÀẫl;¯Åcé̃Z₫û–¤Fá·ueü^'b¶m­”Äd{ûokă_çÖ²L™Ó[ë‚ỘúyÜçè -̉Vj‚º”ÏÜ2©Ê56飷²¼ 86̣äÓÊC\gÄ*oy”mÔk|ËÇăÙnƯ~3QB·CÜ£F̀sá́K¹í²~QEy,‡ä‡|ïe¥|nІ;bo¶|­*S¯“¡ÓæN—wg4đUÚĂH@oDÇ0"Ź7[·ă,O<káa]/•yÀÛÔ!bgr̃<·CO,w½49k9zĐÊÄ¿̀"d‡·H!:”ÊÙ2uƆ5œÓ–¨úêJ¤›u -hYŒc?{#ó`̣áÊ3äîÉhyyḉÜ»²;oÎhm@\ÚYúÅOæ^4­1ëZåg̀˜ù½đ”Ôâ”"xÓ|yÈ́N”Ÿ±àˆÄ{È+jă¹Ï›V́g+BŒG©~£Y9G/Ñ8Đ2÷­D.€0®0‹̣ÀpÄă³G¬‰¹b-ü$è=ë´uẠ̈س̉Î+)z¦ßØ23î"Et_Ü­#̀ÜYXƒ§ñ-)éäXƠíRµy«Z´¤Üå]§–1Ĥt¶‹; -»rnQDØÖØoa׸B–^¸F™´«b}‰á0꧟5kW₫â·û7ÄówÍVÀRsÇH]ÙCƳ×BJñºµ(3ÜĐî°o‚ßĶ¶¨be.PXßh/`Ö»Bj¢ÚT"â-Å÷°Åg&ŒÄu¥ˆè± cº&GúÚbZÈ8æđUXÊëûr@yîgO¯åÖp̉É@Ai=¼Î{D+¾]3ö̃ù/–H±÷¸:ÂÊ Ëx¨Æ±ŸE #&](AkP™'ÖfÔWỦ†0à*çUºêưŒ€ ĂØn. ¶̉c…vTI1cYăº³–.Éܱ›‰ªÎ|ñäuj<{t"ƒ .Z÷µ¦²’7¸$EzÛ5€™Ë< ¼PùC- ¡€ ¦̃²ª· -æsÿsrdBÂĂ…Ä•,‚̣ù9̣ö&lR qZ™Îv[|;C9ö“x4fc+[’6CÑí%)÷Ëس•«âŽEç>σÉÚ< mîIzàZ£‚’K ˆÍ[ÙTºjÜ#-—Ï"ӌڬYÓpaù"(̣ê:L̀‹ÿî{Jæleå™ÔØ©Ù.v¥³1a$ Ưƒ®Qù’ô¡Â‰×Dœ®_Ùø ¯å¶µ̃i»5DÙZæŒÉQÆÍ„C÷ ;±̀ ½a¾âyqŒf±káúOP™WÚ“®Eq[aÆÁÚ-\oY ƒÖÜf™Wà°LM̉Q­ÓòBeg ¥®´'Ư6µ|ïˆâô¢Í*T´–à©„oÀí\ê$Ôɉ[fû>»ú˜Ä£̣9bmÆĂÙkå˜Z0ß?Z(̣4 ˜\–.—ªÅ$Ë[†ª±§E¿’ªz­m₫{n§ß0í“%đÜœ‰ȬrӠσ)Œ̀{Tï»"áÊ›I5¶Ø¯ư”è.¨JJå]Í⿇¯¼=KăÚn̉úCö*jQùîgVüơ?y(~n§¾VË\1Ât€//ñC3c#û1ûî„_°?=[Ăg# ÛûS¬,[¨₫TƯ3÷öiù~ĂÁ²7øwü‰G"œ\JÙºâ5Xâ~zB ˆv«Ùó ´„/ÿ¿³Ö%iB÷¼̀KLÀMS…yˆFC‰"^V ­qÅÀMUë ·Åœ¬åÙÙ—«B#Cl. ]lǵ·ŒÊQ́¬àè…Ư˜¥æeVºWÈK[Ôü^!ob0O÷À Í2K8-toOm°D̀x“Îhf™ÉaĂßç×Ñ¥i¬̀ö¬µ­H‡Ù%ëuƠD*‡9{Z\ûv£Z€»m¿øtF˜•Ùzóªµ̀—ÊB¯°v_‰2~í῭¦¿+y\×*¡h×›^†­„¢i_<”c EÏØÍàSf́±L̀©¿;)Œ -ØKm øáT…t¡¾¬bÂ#~8Âcï½øÀ`¥]Å[¯+ùU“iî% ¶NgZ^œuŒ¸ -8mF₫®Ơ¯́H́G¤\wTÈđ²ÔÓr³üw[̉|;nÜßêb;³àæ6!æa\eÑ6ó”fɼ…I ‘¼Â±̉,ä­2œöÛ¥<ÍΡîô´nƠ+ĂTuÈé5Í㸤jí©è2®nGμu±ÅxÔ«œÍZ¡×¸i›lÖ>rz½jœ“Ʊaä ´oábE f„÷`SÈỠ.®pÉá5îeLv»*H­̀—“$±VN÷ØlßuZ—X—öÁđ:«aR®öb<ªRmöU•j ³ÀØ Mij*•Ç²µ.¶31Ó•lÆ£*Ơf/{€ƯR`ó³̣ô€› o:~÷×K/øÎnåó°Y8 ÷ôă"XmEPAăøÑfF^yV“ơ£Í96£^C 1Ư.ö¼̣̀Xϯj3¢ˆÚwnKh·ùºÍ—çMª€jû®@iü¾v¢!ÛŶhªè1æHØŒm/‹¾Ù§`TYI₫¨TÖµmW÷)»³JÚv±¡(Ư8_;lD‡v;‡‚µz–Åz5újƯ‚́ dơ3W:!yúbPOÖÂpŒ½’ -’woß_XGo"úx3mEª6+KT.#S†ƠÿØQ¢Ê®ë¾_.ö¢½?v‘q*…këU¢ÊXr¬K{\Ä]Ơ[œJ—}J5̣©^ïS#ËçHËF YVYÉB W ¨–"ưu đˆER¨›ÁÙדû×{;égY!d₫UBt́øü»ˆrƒzé§óQÅ(_[tm”u;…)ÇæѬ -rƯư}µPPp‹ÇHο2Y…©Ÿ/ŒßéÑBº­ưân.~àéÆf́F¿¦ Øí|¿̣[÷Ă,/¹’'ăs#‚Û«-–~@¦¼8‚yQ2ÊóÔÙzÉó@ëb[„ÎZ,¥½˜î—AB{QÎkÔ k/‡GÇ@<â2~_ƒáƒ‹3̃滋éHÊDùŸ-›”;ẓ,~\Æ8¨÷̃ßlv₫¼ÂIÚ…wÆVûTPù™:Oaz¥Uµµ3?ù••aÍé´Æ­p›©²æ̃Jtly³ - ÓNÆ­pÛi_„een;lªh_yÓŒbL¬2ÂZlVejp³­x¼TÉÛÅ…é4¶Âm›ñ(ÜvØÅ^VÛơÂmªy‡¼p[̃¨Ë¤J¥ơåfWÙ¶Íx”mÛ́«l›…ÔÔ£´¢ZÓ0~Ơ¾TU×;Ëpjgë¾’çç~Ï‹9{4å¯Û.î )̀̀Oé§đe«ŸÊMûIh>GHN¹ö]÷ç¨zœæû¼̃¬¤đh/ ûæ9ñ2r+¡hJ Ó,vœ ±³mnơ§§êQY=̃p:^]Ơ=\Ë̀6HÀÜ“[PwE£.k7K[N,±Úªđ¶ ÛCZälaøî£#©*§CáÅØëơ¤r -%f89$#́--§Hd™³Qb/₫·zm÷ÄÅ*{–"Ăh JÖ€]F+u5Ûzñ´'VwP₫nTu ₫˨>+•µ·‹ïZ`$Éwư»₫ªà̀‹Ï̃ å÷ïçÊ_kpÉy.<ܦù'^¿†äÛ\ñÊÿƯ­Ă‡6™ áº÷0¼6´Î [ ÛƠÉ+́F›¶:|›½(5 -­'ṬÓ÷|4mR—Û?µưRĂ’£[‰Ê{§±l¦đ²sÿih´Ÿcú¤ÆYbưŒ؈ê<..ñ¯Ơ«Ñ“đ¯Elc½åÉ¢\°̀ès -~ËO*½2ó YăD>V>—–¼,GƠÖ4±VŹº̀^î¥̃˜²àÆWF}­‘3î¸8ÍV9Víû»\æ!@ÁTz°\8w¦V¼ªE9×ËD ºíấp̃‘`a6£¹ÚZfÑœ”¥ÇÓbÜ)Œ¨kàơ©J[¶åmÑaë̉ü…¥*3¢‰7“¯ƠÔ´ªÈü«ƠƒơUè9¢îb-¹Ç£Ö+ª ^«\ơ¼ƠƠØ×`uàǵ|U5î!¡B)ŒRá̃ưö‹™6¨©³¯Ăèq‰›¶•ÿ‚r+£î!·¿\Œ6ß$rè™ÜβƠ“áeÜ/¶Ù̃×ÖiL.zư­©Úí3ăTUưU ₫¥“ —QưâŒC>kµăt{3Hļ`Ÿ¿ˆi½qüR(l‚É [,ĐpˆèC¯lY˜Âqâû4¬çÖị́VFău gÅ|sß3r…÷̃ËÑ'ÊgÇ?8Ïé4i`/·Đ>ïèÔèü2êuĂ{€¥ơûÁ̃w?ơqŸ}/ñ1®Ô]±˼–­¶o­óÂ-Ển—Q{ç–îñvqÁ^´ÇIPS÷1`¿•ï ,¥#¢ÇiT#Ơ¡/”pˆ5Ê»Â(O[äÇx½øªjAå¤Ă éÆmóKY—ê®;hpmTE‚y9ê†ë•ê†tFú¯Z¸ưgßVoïá»@¢ÿ,>âĂVWÜÄ -™°ê³c0xđçÚĂ½ïµÇq,jÅîŒ}…²ÂFÛjg ËÎpW+1c&aQË›ñ;Ÿ©´.fÑo$ß{5ÖuäwØoUZ³RO₫FVGb;µ0đµ%˜Y ÷Íè4ߌÈM –]?–W÷â!…Ứ¸1­ç-›¿Å{߸–ÄƯy1×GkÍkư1GUW¥ÆK`Ÿ¶4¡W«xV:,n–‚P|ơ~1áíÓ×\‡Êx—ÆWA×½Åí?ÓävÄöZ,ÿäßsËU8E¿³~ơ÷‹ăT÷l5r_ød¹ C€̀NRÈ*CÄđp7̃¸EùβÑ°“«û¼ØRöÇJ¾=Zf¸qâo—Ñ‹ˆ„%̉—¸Y*2̃,#₫ü¢üî_q¿÷G%üî«ñ•̃ê&øA~́T•bà,.Ÿ@H»¢Ơ°éáƒå9/¢ G›ÜÔÇfTÿ³«·‹µ†}̉ë*6;:~+̀tgĂ›;c?ZăºiîJÁ©¯ŒháF½¸ăb©Á@ôûi ºAß7Í3­h5Ϊ^̃¶²ËÑnƠLæ|U©œ±ï™¤ª®Ô›(Ûfdƒ‘RĂ1êơ’®ïu¿×È›+Œ¬"­̉i¼áî”-Üñ¯×‹[lù~fÜJ«Ÿv8Ó̀x±#ï2Ù×£–ce₫¿ÁƠÖ1W~søÛbƯ÷‹í¨¾Ù{œ|vdíöd¾»ĐƠºmù6U´¬é†ë¸xûÊ6¿Œ# ¾·Öé ¥yïǛ¶˜¶?çĂˆZDŸ[NI}kè†Ưv-_ÖfHÆ»_ôy\…% "I¬ß́:V¶RCẮÇ-Œ/iFœ6®U´÷èéó×:sFX ‡ °áï-'·¯)¶Km&ëèY–rÛŒæh7nÙçL¯L¨â‹èUÇî͈&²½\(Ÿ™l æhXOliVZÔëW%ôÎr'!dÛÜÀ…ëÄ•€üÅúmY—‹iäÿ‡½7mÜȲD¿ËLÿ_dVeÖdĂá €Ö§kyư&ky¥êÍÆÆ̉(&³Ä—&³Tê_ÿüœsƯ@J•FPÑ‹¤¼é@À·»ß{~”ØN‰£Ó9ø‡?L₫² ú«ê₫Rä““s52•3Â%Ç̣·âjkVư°BÉœ.0bh•oÖÄJ._ÚÚ7:A;·9I;a0öF´AÜd#tj£kü¦LA»\Uây1IJ° ´p>l̃‰ ±×M¨Ø‘^#M=ÀÜíĂ|đµ¹¤ưV//I Wûz‡'¥ ¡̃×®æZŒ´1„6!uEE7̀z@­LÀB¬‘ßd5\à2)j{çgÄ~|.ôQ°{[k9́:q~Ôo8_‡ À$uEAĂŒf:JMÖ(ôqÊ°QSQ³Æ|1F:¤Ó»êz…Åßơi"ÙŒ(~>4cÛ‹ â˜6%ÄüZS¤{ơ9+ư˜‰á~ LѦ^n8ÍBJâyq»….ºÍÁC©Ù~˜ÈVUơOècé8º!ơ²Nº(F-¦÷¢½¡1T½éà̃:ÔhÛJî`i#_₫˜ñÙ+ÙY‡­4 E r3%*xQ́fs°ï‹ĂÛE•‚h>áAyP§å ­¢`P¼+ 9.:;{1vMJí„X—]­¤7GUÁêv”ŒÏ(C]g³z;³ÚUfa¶5˜CUÊ™.¾•X}•&ó67Km¾¶û́â‘̃™‰éÄ.혵ÊĐj«Y_Đa7¨½©¡×—"¡™U~©c=ZúêúQ§4Ç^eŒÙ¶½̉Ơ˜µºbï¡Dó$”Lµñp ÚºÚÏ–ŸÑZbCŸ\é]²Œ£̉LâD] -ØẵàBUaTªœ¯ëK‰văçDëéUß`tÖÇÅÂhÓPp Jg}2â~J¬ËÚ[rÑ8KƠ¨½̉gy$¢‚ ½ăºVz ?C4û¦´̣µ©âJ#Œ4nñ Éơ[ƒC¨mJjPKö‘XOi„6<%Ö Sâ4ÓsJoRS A,¯§;5_kĺ)•F;e0i_º£EĂ7±zÍŒ>i ZyüÛ}á®b‘X%a¬}¨ëà4éIWÀŸ7ˆLU®o(ôÉ¢Ơ×6†'âĐ•@¼ôU—R₫¤vPUà…äºu›;ßNOu¥$AƠF©¶nD&}SË\­¶¨ÁÆ`Wđœ«¬ß$º7ŒôfŒ£¡zÜÝ‘rÂ4D^·JœËăé`$C;Kj&<Đ”ˆßưḌ-ÑĐ2( ®pƠ;+±ª5®m̉lp0₫È× 'M‰„NªJI¡7Ơ,EĂ_k 1 -µªB1yk¨x|́̀^d訜!Ưflá7Ù¾Îb¤—̃Bư˜S5ÔTû~L¨Ú<Ôc2̀dp[Ød3i×;%RÑ«¶A¡O¢”¡våcóà¢ÅRq;î%nq -ĂÆPkÖªyĂ”ÈEƯDˆzWÛßB.Ç̃b¥–ªÊĐOˆ£•J=̀~¶%Á½Ú`Äú ¥Ëàdp¯‚amœµ­ü´~m >)(EÓƠÎJo{73]»’„43]‘ÆÇ+})₫(HT#ÈÀH,æa ÎzÔéÑ uPôcœÈƠu%W×¥z&;¨l¨•Œ:¶È\4“­-pFb/e°Îb¤°R‚+,àÁk?%Ö¨çÈÆ`¼«ÔÀ„­#‰Ăv„±Đ'ÑO'ô%¶Sâ$úéü ñá/Û₫®d}ƒ6&—¬áưÆ2Dëå5_3ÀÊ´ik°ùˆ';1¥Mwm¤O¶8M½êa›'g4û&Ǭ«Ưz7d!ÎO¯Ñ§G½¨J—¢€OoPSO×ûôbƾ²Ù[\“¦W>F ßN™CdÜ°ÅIb,¨2ÓÁ¡ÂcŒ jBÜàfôÂúbT(kƒIn'uBÙoŒV¾0eÔøÚ‡¸zMǾ-„LơĂêăjêN%Ú|mö¹1¸tä’?fKç-iíí mA˜rE¸«‹|LæȈ®BÔÑ̃{£t$ fÔ†ĐÜ ̣Ô7z3¶zv­ûc»?făº¥áW{û£ëuSyëáżscư{ô£ V< äbu}ñ¼4ʈ¾ sbƯͶúưêà)±ÏMˆză„>*™±SW© u4v“̃ÉƯ5v¥Çätp±¦*ñq¢?oĐÛi[̣‚ÈQỘÉomèđ±ÂĂO₫ÉEMƒJœÙz19nd¶rµLĐÿ󭽈*±‹C¿éÛ q·¾a¤×÷÷Ứ®3uv-­³æthèk1KuuMhTqÊqœ’K±OèUµ1ơYnĐF÷æ„<ºB±]¦ÎTû%?ø ±¾Abuc0´·XºÆ+&Ä(¨b{Ă”^jtB¬Å4£û9Ä’f¾é«FƠHWü6uđÈ¢p°È -dí Á¢7ÅH•NnFÜT:£=Ö®¯­½̃,đ¦†̉£ˆMuh¬PªYß­Rg$n‹&ô†pBÆAºTJ’ yùqBªŒMkÓ¡Y¥éEÙ7ˆô…Ô7Œt¶²×R=b!EÑÂ`-küé´̃…ÁÛXÓ F½F£æÄndI#™-ï g6©ÛV\ÔZWP‰6¨]?œ׌ ¢ơjªGYt2æ Ë Püo18¬Aú qÜEïăæ`賡XH¥£i%2‚6Qư¤SGA¨­åÓlMߟÅo‘î?¸0ÜYÓ¢iXxƒ8‰!oĐKÀ¹ÖhLCÓatlıC_[Aođ ¡4¡,áñ q#́¸A/÷ñµ+-Ä̉sÓ¤«%Hƒ¹¼¥/¼Å±Gb1›́ #ƯxÇ“’&Xë± ©m'ÄÊÚºÚw°NjCÑ  ›Ñz -×9’ Ù –öQø%×ơbư‚¦=\>·W¿+GÀ·I,0ƯXĐ«êà€¾3ĂvB4ôú£³œéú “ä™*7f™6¡› R×Áu÷&đŒÄÍlŸ }L `̀PBJQ₫Zkm¼™q„ë°2¦'…Ötđi"ÓXVY²N‹•xFEjYëN©°h*§˜¤\m^³HƠYI{² Ñơ~=§„lŒĐÙú¥±uˆ®B4'¥³²¹Á¾öj‹‰ ±S #{ĂH_w¥Rg@èj;`´ÜÏrW*x¦¼±ó¥A;Ú•®H6)ŒEß][¸PÑÑBº•mè«7~B4u ®B¡;5iÑk½ơ®¢{2¯©C³-u¼ µMĐ˜K1!mXơzRCP½ú˜[rÀØɦ$†́ï´|B -~míxÔ‰vĂ™¿Aœ~Ă½4W™¾¶8ñºê *Ä: LC¬”©U7øHôÊ×#bFùxGErØTEÑë̉l³8­-ÇaPădđä ATçfÄ1ơvÓ+kơ(×Rç4ĂC4sb;%N|È₫¡Á‡ qË¿½ùmư¤WCí÷1˜ú«æD}ñ½÷±7b_Ÿ•Ï¹^ŒFí© •bnöB)É8$Ú~4&¨6·µqT́'D?MDŸĐÛª|¥Ñâ‡E¿YøÄ$änîaGHÈU‚`A*Vm«\5µ%뽬eû„¸ñUú8à?[ü”5ÅÚX™j»‚Éà’6]p@4¶1mmÎXr1ÙȤº—Í-ŸÜÓÊÑ[‹¥z¯çôñ̉Ff*¹Íëˆ~¶xè}q́N—Æg#‹Ù$ühƒ^˜×Æk ›Ñ½gÂăqÆ@k%̃”ƠNˆ|yBo‘Ê:Øà1°ä|%§\¨‘h³pµqæd°¯}n²¿6‰­<ªö†)½|CƒÍđù¤rV“å6†©̉¦]₫*b¦“AI©[:ùgbl\ñ8wJ ³«É£–n_́Œ²›#ÑJ ê:úˆeÈ̀ØKiïă ™ÑêmI5­» MÍĐƯ'Ä4… }"à(é{UZ\‹™h‰x:xrSF‰µ}­Î5‰ ¾₫ĂíÍŸî.oî/o₫z|lt6#™₫ÍçŸưáÿÎ5ú˯ÿưw¿½¼Ê¯úü³®ÿ}ô/ùOÿùûW¸}sÁÿ₫ơåùưåíÍÙƯù«/~ñ÷ë«›ü—Çùï.¿ùpñ₫—Gÿ”₫êîîl>æüÛË«7w7Ñưó¿̃܉Üÿđî‚ù ×4_ụ̈蟿Î/½ùëæ ¿]}°Qß^\₫ơÛûGÆ¡ ‡å/(ÿi§§óưå›ûòÆÆ=×d–Lå‡Óøa·§đ÷SøûóMáö›ÿ}q~ÿƠ퇛7ù¿º}́s'3zËë₫o7—÷ï̀mcôsỊ́WÿúúWWï¾={í>>½Ë7 f…Aÿ´́‹w'̃¸ûæĂƠÅÍùÅÇ×BO}t5êËŸçf±÷ñ©|sö₫â·wÿçCù¶2ÿ\“k?>³›×<¿?ûÛÅ’ë8ü\sº¹ưú₫̣₫ü15™Ú{ûËåƠ¢¹mŒ~®Éáz}|bwï?\-Q9ÊÀ…̀æ±ov?öÍçøùÓ˜|ó¯ü³lÊåÍÇ·äöƯÅƯÙưíƯ‚M‡îö)»¼yŒ}lÈ2Œz®‰|}ûáîüâwwgï¾½<_2£Ezöûrz{ưîöưåư²ẹ̈}5¯À?ÿúâíÑ—Sn§s0åvi -Sn—L¹p0å̃̃e]₫ê·—ï_1·ÀDßS[nÁ‰=Ør[î`Ël¹ƒ-w°å^°-^–-·h:{bË·/À[6‰·ç²¥óƠÅß.®¾₫ö́Íí÷‡ĐU04ÿ>­xy–Ù|sơá1₫j—»jƯ¼¿óë‹¿]á{ÙÓáϬüî́Ăû÷—g7_=¾k»­7/ØŸ7K8â›gd‰Kæ°D4½yFÙ´́Úï<÷º}ûöưÅư܆=¶ÿȹíå-¿‚*Œl¾óÛ«Û»ù₫ÛÇí– ÆüĂƠß ;\hïß]œÿñĂcçkeÿ‚ü›÷î̃_|}~¶́¤m 6Ë®Y0±¼›®Îî~ó÷w·77K¶kû‘g›ạ̀ù̃̃¼¿?{̉üÆGöÔà8vM₫Ÿư7‡ă¢Ý¸=œúEÓøïÓøïg×1₫t{ysÿj™‡ëŸ~ºÏøÚ®ê+ÓöRéyq!'::v>2°XăùyënûåØ5pvwyÿíơÅư’¸Ó^q‚̣î»Ç¼"Ó™`Đ.Ïá1Etcn§åïü’9øƯĂQ̣Ư~H’—?a¼ü™|O÷:»|ÔB<Çư4‚ŸâéƯ—œß_Üươ+¹—ÚỊ̂‹ö2÷ă'ưC–Ï!ËçÁ鸗”å³hoṽ­¹`;îÔüÙmœ̃̃^}uwqñß Â‘/=éÍåƠÙ’°́^y*ÜÉ‚Zơ»³7—–Û2đym³—’j¶dO¸s»:‡}3+{÷îÛÛ«Û¿.´;h†ØØγ±—À¼–Ḉ:ózed{μ_D.éK¸̃Ÿ¸°ag'±o|³`_daü½dO 㤠-ămn;Q¿ˆgï¼àYp¡v_đ,±VöLđ́yƒ‚Ç̉@¦W~iüó¦¿/º!»Ÿgñ2¸Ơ‚cµûÜjÁ$ö¦èåW h§ßƯÜ\\}}quq¾̀¶ưÈsÍïÏ ‚kOŸßö#Ï,m~}ù₫ƯƠÙùÅơÅÍưïÏ̃í¥È¹>˯[:ÜKç¨üïÑÖºÿüø„ùŸKÔê2p—9ăî˨OÜ“og'±oơ)ÿcLb·ùÛ¢ƒµó·c›c÷oÇ‚Í·¤úơÿBË’]»mo¦é¼½¼ºZ–tơŒÚÄc̉ê6¨Ñw©Æíđq}{w{½dK8́¹æ±€…Üß.QSoŸq Ơ›%‚4깦qvơưÙ ¦’9ơưÙƯB®‘Ï5¥{2±úăà>:½ÙøçäƠåÍÅÙ‚zŒó³«óßß.I«Ÿ }®IƯÜ̃,:gçç®?üHæÑÆvM?×´î.è_X0³7o.ï/ÿ¶h^uè³Zg7—׋6âŸ~ª/xÙûC&ÊNhç/!eÉ$öÍ}qÈDÙ)ÿ́KÆÛ[̉ñë‹R½;‹²ˆkï¼èy ¹(K&±o¢gÏsQ¨–{’‹²è†́~.ÊËàV/!eÉ$ö&’qÈE9ä¢́ˆÈyÙ¹(îg‹²ˆ½ï¼Œz ¹(K&±oơ¾ç¢,:X;;^B.Ê’Í·#¹(û¥ÙÇœEî \ă9÷r¿›ç-JĂ;́Ä₫5§ØŸøI¿â9¿àEơ/üƠ¿¾₫5;ܼ₫Äî®}”_/²×Ïà¾túy¡àËÓ2sàkOákéÀ×^$_[°¯¾vàk/¯ưæ.SêÚg¿¸ÀB¼0®vĐÖ\íg̀ƠÊÚ‹äj]íÀƠ~–\mzư‰ƒơûÈÜ,ÁÏ*x¸FO¿FƯá-X‚Ă5:\£3ó³áùÓåß/®₫tuöĂë—Pꛣ¸ ïR‹ÿçE©—“±{Êÿî.®o-!?4oy¶©¸%s9t y–­9r_¶Í‘‹ùŸÍQ₫ÿ/óç™ÿâèåu_̃¼¹x{yó8ŒíÆö½»8»ÿơ¢ó8û\Sûßÿ/ïß¡çËǧwèørèø̣Z Oéø²kªÑËÂôûf ÷®+ñ˦±7iü÷Ănó€G¯ơ¾ÆsÙùù…Œm×ùóùíơ»Û÷YÙ₫ă‡Ç×^WñÙ́ö’ œƯ]̃{}q¿äǼ7Xàûî±@Ót&´Ësx̀}¹1÷Œ.…sx,Ÿic~·ç°D˜|÷Œ²ä‰*ÿÎËƧ •½’‘/°oá'’ûˆxẫîM2Đ¡̉ïÿƯÿøïKè7xˆÿ>°(‡øï#39Ä?éụ̈â¿ä#ˆ·Í— yÊ!æ{ˆù₫ô3< |b¾‡˜ï!æ{ˆùb¾ÏçñØû˜ï (¾¹|ûöĂ‘¿ÿ‹g²7,`ƒÓ&}z{“Í›%“Úzb‡g§ øz!–ÀÆèçƠWW·ß|jW—ưö>8>GçÚ³›?đlví¹ôáîmÖZ—îÛæđ…ri×ÄÁÁC¶£²¦ê®zÈÄ*¿üëƯÅÅÍ—Yºø̣̣æÍå_o¿üÛåíƠÅư—wo¾¼½;»y4&upí”ël‘H;øÏ₫³ƒÿ́˜Đû́?sư‚hÈÙ_^¸ ^pcGÊÈç:fiÁt.®̣Ÿzj&cŸÙMóëKZ‰¯ÀÚŸ57 -endstream endobj 28 0 obj <>stream -å×2[_™ŒÙËcÿB\Fïß]œg¾~÷“¥ ?“¨x̀Áơ¬v÷ÁlßƯ‰ÙưÍßße5g‘—lû‘g›àÙR¾ö nÀíG—ƒăåàxù98^̀Í"Ç‹yaè98^—ƒăåàx98^—½p¼\ưˆ_bŸr¤ze~ªùÚ ½ö ½ĐF/§”öI¾±ƒ§ï99Ó¡ÙÄNr„—ĐlbI“ƒ]o6±dv½ÙÄ’9́z³‰å º»/B(g2?îÿtvù¨¿ư Âñ ×̀a×…ă¡ÓnÇ'[;/ — ”½‘‡L/«ÓöơĐ}éĐ}é¹éϦû̉×ß½¹ư₫SCḯ#gú9VåïMzå‚Ö`‡rög¼< €"ß<ÆC7rµ₫₫|glÉ ­rĐn_ûç^·oß¾¿¸Çm¸»x³%ï›đGÎñçh¬ÙƯ—i ́ÚμT°́ƒ…öâ,4ç›/>~B¿½X–9T>—ÄsaÉt¾¿|³(³ÎÆ=×d]\0™%ºÔ3ªRË&±D©}FöçæÇøÄ-¤÷Q»:ø1vØ‘~ŒV€ûàÇX2‡ƒcg-Ưƒc_$íÁ±«lüàÇ8ø1öÄñó±ĐîÏeT¾tû́íƯÙùưÙƠn/—¤eë¹®G}ưóäd,h£̣ÍÙû‹ß̃]üŸ7çKÔÏÙøçÛ’*7®ÿ˜÷ôo‹:L?פnn¿¾¿¼?̀•¸abÜ_.¯ÍmcôsMîüç–=Ï$^Ú’IüTæή±ư—Ơhh Ă{w{½d&ö|3Y€”q»Dß>ß,Í’gÏ̃,é¥ö Z¢ĐÚ=0³CÛ ŸLÆÚưtn—‹¿|¸ûæĂU¾íûéô_•̣ñỤ̀?—0‹2pnÖγ‰ßå§ßÿHÖÓs Ö*ư₫ÇîÚn³‰—%~úZ«]í_pè‘ñŒ—èø%4É8^`&í|—ŒŸÎËô,ÓùăÏ ‚t›L¼¸Üº5çmb$OĐ?w% ”€ úË‹́¼ßIOÛ›½:j Ê÷FvK0à -ơ3^¤—Æ°ÿóíÅƯo/ïÑÿ±k;|öÍ’ƯƯ ïd{´ Ă ÿûR÷ăÆègæ&¿ưps₫ç½d#/étGÍÑK?f¿;³ç>fîå3³¯59„&Jÿrwvó₫í$…<ó/ÎơñäBƒ÷zS\8 ®uGíIÛ >ÿ9¿ mº£óÏ?K')„üªăŸ×Æ#Oú&v™OBèüQ₫‹àÛ” î$9çñ”NđÚüϾ\₫ÅØàô -ƒ~gpĂÀ'̉I“́·‡¡iñP*ƒ\́IèR—' mSỴ̈m›iñÄwƯQHj\âŒ"^̉ÄÀ?5CÛó‰öÄá#Asê|̉÷™àOzŸWÿî[#´­æ“—ªçS₫¤~ÇE‡7·'][:Ç?úfH|Äç¹&L:ï¤wù½y7\Ÿ¿m8Éơé¨;éC—HèúÎ9û¼¶óùÅỷ]́ú<ÈåMÊ„¼Ú}Ê[ó†üº¼{Ăwâèô¡ ơùgoq¢@Êëÿó4Ÿ¢cç̣’å)cºq<X7.v^¨ÄJ\8œL»ßä%Æ”¾ơ¸Åjăüôö)Çm̃ày´úØÄácZ°ùÔÅÎt'y‹=Î[Ó½–ÑççzÎ?Ü·!ol>Aø>”_íB^û|̀R₫ẁ;‘̣Æaͱơ ÷C~¢ăR`1ó"¶ùxq óçåI´MÀỢy£óÏäëS£›ĐEÏ#•§ÀïÊץ밦˜¾_¿eÇ*R”—½Ç«óéẹ́̀æÚ`W²Í‹5 : Çoµ'MßF{M~(CăZ,×u­…póOaÈ'6_oΫkó1̀ë˜NÂb¤ÁéybÂai™G₫!¯óÑq“—Îç¯ç”û!ÆñDåù[̣¶/^óÍÑŸù¯¼lü-? ₫¢#ËÇü`̃”†üm8Û™9œMóåvº¶È]`Ă úÆăâå­Íg!̣rùÔµøøüd^ô6…Ä•iº€ƒœC“¿"¿kh±ä8¡=ø.Xj´x4³”·Ë¿©iú¼‚ùÉü‚!NüTèƒïó¡ =بË×¾ióÇâº÷ùä%ôLĐđ÷pÿ„§ê*ñ:O(`Uó„vȇ9’¬rç{ü|₫JÇ¥ÉkÁÁç̣yqC^Lđt.}›iï[Mp³|X[Çù.ÙCùĂÉwpspÔó[†|€¸“àD8«ÎñÔåe Ѧ–©©mlĂ»GzpäGÇCïµÅ$ Ư`Ïe–ëûÔ>o:Î'¸^”·9ï^ M9Àa¨‹̉y›¯P>äÇ`NàÔù=¾ç^fÉ{=æO -Æ ̣_·”C™Ù_A.D±rĐNñä×øÇ¿áĂÑ/~yôŸÿ1û«w&¦K߇NñÚ3¼ú¯;¿kNﺳ»âä®;·ëNíº3»êÄ®=¯ØP"©?>Î_)ùÛ¬¹uI?„º1K¥AÊ`GÅ¿¢‰Å¡̉6–†ÁẴD -ji×Fó*J çí~hø—R>øFt8/‚₫[>ë©lưÂiưáĐ:~xCQi¿́x…̣ûZj§’çfó”.e]Ñ©)ETḌsù&%>˜µpÍ×7A’}óơ!™̀ĂˆĂM}ø´¼pÜI;¼z`œ0‹á*@Vwó‡ö|<á¤Đ¤Ë¼2Mëßå ¥Rp„x´đÅzkf>+–¾BïAË:LÖ9ñ¦|NÀÈă¥ËÅ®#AzR>]^̀4Äaàå¯Æ§oÊă|ÆxîcÖ ²®ËÍĂ—gö1äEäS>öÔ ̣}êSë7ÆP¡€ ’¯^"Ư:|ơLæ‡]Ûem¤möĐ¹̀)óát¶Ëynüà6¯§o4ÆǼ“ùPgûJg-k<]&$è+$4±å‚eƠ0^~c&Æ6àîA¹̣4[Åml.M9€}Ùl¨äùdH«‡<_Ÿ$ Ú§YRƒĐø, ÉHZÊ] Ó‡™K¦éƒ³Å¡`¸b7ä‹ÕÓ—Ú}/Lå|̣¹ …÷/à°ÂÎ<‚1‘-˜Q>«yÂB»¥ê:—x`èHv -=Œ‡KÔDx Z,„Ib*&Z¸T ø^Z‹^Î-ç  a“¿jđƒ”“ÏG&ø6T7¡néçóΉɂâƯ:h™Đw- 8®ÏpẠˆ̃.dK/Ơ6HP®¢8B>#đX`íómt£ßIkŸ/i!Ï‹§¬Ăèx<%_óyịÁÅEó©Çy…f™7§˜y -Ïk~k - ‘Z= l₫ç[™̣ödå2˜³¤¬ B…íÁL_á̉å ‘hèØ>˶̀á‡._ ¸n\¾y¯û2₫œ¤}A,d†óư:¨™yÍóŸûØFsaôù̀åß¡OÊxõßüs}O]̉ŸPæg™x`₫ú!æ=È7´·â3Y>7y­¼Ô8Oøù£dÔÿĐPwÊS·k +!Ë/z5ÎédÎ<đM¼ÎyyôkùăÚc#Å]^á¡MäÇ.:‡à”̀»×BĂ׃ɶ?œùñ+Ñœ«LrTÀ -rX`p³J̣Œ»–l\vÊ©Œ¥¼ d8̀ư•|T´Q­–D,t£dN½üñ̉¯£o$» :X}2#z%b 8ËçÅk3ø AÉJ—,¦”Z₫b^ñ">ñ Øo€Ăblz½ÊáÀ:J:9í†l‘•CÛw:ß4Ṛ¹Î?ơ}ê¤dM÷Oü©täÔöTÈ\¢Œ¡H}D¡rm†¤xđj’ƠƠí|¬ø`Hå @("MÔQ¾HbëÄh¥Må>(K*âµ±èøâ×yä k& ‚.Já”̣Ăy®öưù–Ëläj̉Ăâņt&²ÆÉ5̀¬*Ö“…‡¥³ oa’a—èYå¾¹$ñ'û>8ªĐ²lß²”&±N úT ©¨hĐ^{¹×¤%âp†–ú¹o!Ñ́`ËúÏ~÷繧₫ß âƠê?U}‘t î Ưâ ×b¨ËÚÙ9Ϝǔm’×ÑÔ¬|Áu·Ó yåƯÄç›̀±Pç¢4h%Ù ÜKxºÊB²8bX K¡è$×Ú¢D‘•æÅï§ÜLu~N&dnÜóM!³R=ëT¾A–5Ÿ^\ º¿(y?›5o¸Èh ̃È´ "đÀÏ@:€«c€&x:ŲÄ6Á»œT+Y¦̉˜/4?ßÏ93&ÈO ă:Xñ0Nè\‡/gh©_6]¶nơ #45Wúüü£ôäYàC¥i Må•oé´Đ”=-Æ.@ rr'Ùyú¡…®},tpøăP} 6p08°$GÑ· z'^˜·Ơe“:̣ZGÛøàQÉ‚¿„“”Í ˆ*r!F€Rí©²†r”›!³oLQ†uÖ₫,“ÅEÈ[Ö1;ÔXKV¦ ÈÛÔj‘À”2g̀$Ÿt%²vä¨êh9¼'ë¦Ơj‚R¤ơBfF ô¾³‡ÀœúÉ'YضEÄáÏæµÈwªî”ÿíLă?ơ÷¼9ïéÁÁà̀üU©ă¯Ï«œ·?Ï KwD 8¯M₫́ŒÙDv!hpÿ¿"É5pÂeÉî¢y`É41ÀLȆHdÈ$ñƯ - ¦IÍÉJCE -æD×( -CW[Ö‘Ë»‰Û̃pæ ª–\ˆ¾ÑVuDäío‘p0ø!Sp/áaṛ̀̀Aơvà×b©‹ÙÓ Ê„%̀ÿÂ7f«G Ưr°ø¼=‘Q¢4€Ä…é&ŒÛ&ƒ¼îÙÓ´ 5“dÇă”QùÑßă‘̀ĐR2# -úôºLº‘£Z×ÊU+ÂAiÍZ‘i¥ù,eauäRkUm"ă,„0<ÔÏƯȦAK YÀØ{eá!/烩•3oIgN±nT3 ÇĂÏ -C"+ûÑ5ä Í·+ܯ4ư¾ubĐtÉv&ôgÂÉëÑơæ„%Ë€‡»—øf”ºi2í#yyª2F˜‹^Ag<•Vgx¬ MgÅËäZp¦Êʳ́g6’“=„µđN>oxƯ[{\u”Û^ê–¬éÂÔùx±è7oúĐƒA†bÔ†ÍAÑÔXº{;o’”ÅÏ› Ê, #èËA&"Í\¨€d̃ëüïaTK±ù‘F^ {Å•A?Nicß S5i‘`·ÑùuÍKí ä1?FHOÂÏR#rüi‰: Æ¡7œÅƒÔçW<ËÙ€K² ! q̃©ơ–q°åơ”/uîú»&)_aù"©`Đï¨ 5´X(£°¢|&½\ˆo¥¶‘Đ€£ëº(‹S ̣ƠC#å¬ư·Ï?«‘¡%ÿư‹_½¹ưæâèWw÷¼ùÓÙư·G_Ư}xÿíÑ_no¯~ÉxR–%ÿù§Ï?û¢ Mæ•M¶ọ́Moüo›Íÿqú›̀ẹ̈)J¿I¸=§mÓ₫ºmbÈgv=¤ß4 BWÿœm3…ê‹×c ?è5?ăâÍk|ó`¾x˜×›Ó`°ëMfèc0́ăY*PÆmñè_“Fkçt„tzl&FÓ̉ '³a|ˆaŸ¼u™›QAú ü…¸€0́qƒ`£´̉jÎyaècÆ郘ËÇ̃¥Hg5@pXØbø7¢Fç:ăPÑ|®‰WŒ -â8ü ä+Èœ=̉Â9áÄË™¦ ~Dơó3˜’,wÄ‚R -øù$Fû,WÁy:.¨)Œäü9`;´ôóè(8•™$ȧÁ̀Zæ„Ø(9ËáäëK¦d¹đ\ÄÎC¦cœ+§¥¥æ#ÑWnOʅåwCN[›«kñ@¤ÿñ3̣?4 ÿơ÷Ÿ+B¯´ˆ]B₫5Ç"Z¾É•‚̣m;ÆÊ®>aÜö¡OyđCüŒÂq?~Q$‚ÈÆx>¯g4„re àv(úgf[‰Ó§-‡/ë‚Â4…Ă1ˤ¡+ˆAẸ̀8£|¼p=½ÙưàÛ¯kÿÀéø»}’WƯA¥(?<ỗ¼Py[Ă2œe y/ïF¬á8|̃z)oơ!̀'ÚúẠ̊%= ¹Kxi¯ØËăTúđÖÈWơ %$: ;}RWc™˜9 'ØËøí¡‚[ “†2%X¸qtP€Ø+”X„4h–̣QEàkîÍ7C}”®Ơ¦O4tx/­“û镈Խø Ybæ@zœi›å„DêRP «k ¥µt°́w³ÙÑnư½ \sP6{?ùQ́œBlá¤Ä -H£±,Ä`ç1?¢©Æ¸bC䶌u0“ơ:f1vMS/L6{½Œ‘àêïÇ£C̉á–¿ æg}UHÑȃlïÊv®6•É¢(<¨-ñSé) ̀iËÛ¤TJ³ú6E°@”G×í́[8 -è…HÎQ›ê5é`Nđ¾–ÛÄ”~mpz°3̣êjJÓs!؃ƒNV‰n%åfÖ¯²=wÔí™qÂÍ́`¸)Ïè̀g$gí¹¼^Úrj̣q/–Œ]ÇN‰]ñ>tGyU¢xĐ 7f¦x/•Đü“ ôIT:ʽ‹)®ÔWåfË=́,Ï sÊƠ¡Ë9S"Ơê,ơéºçÛ ¡D¦ - t™Uå8˜'a P²%èFo1±±Èă…¯D́½\×7e„Èëm–…çª{;2WơTªñ}̉0¨ ¯Dô|§Ê…N¨´̣£C´Ñƒ_S´·…^˜K“)®•S7%%k4Ê“Êl8̉zË* ‰csöi -)q…Ún2Œ«È@ù.[Zå2D¹2A1̉›ÚÆ[°IA ̣Ú+VˆC,-µAF2çèŒG÷Jđồ'*ç¬o:9yå/́àøä«ü  è̀)]«àw×NvËÓƒØhyÑP 9ç¿ëcªºÎ…tÊßĐÉYY²´M -ä¡ă/Ö…¦£1ôr![¨—Û–¿PX6üʸ©d¿FÉö_6HôrHÓ!öJĐ0Xj £(:ªFr¥ØËGºê¥?Ä”ïR}môNẹ́»¦^̣¤ƒ=? ÑiÑlÿúé=ô&²»__ËF­æi)SxA₫z…ÉAilÚ4ʃ­oE”§‘̣!ʬăᥒ{¸Fè@$sN7$4Jûê,:‚ñ}”åẸ̀–‹Q[­Nœ•è¥™:¦ăé[[Í\LˆaVÜt)ï* ÆTg ÏZÓ¨è¬K¡Nh€ bƒäƒW›Ä¶…Ci ÉRxN”ĐÂTOER°ẉ¶LB§°7Ù® -€â»œi»äëÛBä½´¸¹hÑk5˜HyÜ´:°º½I¾Œ))£Øî̀eÈkÙÛIW¹n¯³ÓCĂLNŸæ˜ËaŸA…ÉÄj¢|-&…dvË÷·̀0,‚¼µƒ• K¼B¬tĂPË<ú‹Á›É½4̀ơÚ–ñXu ¬é†Q/í,́ĐYˆ•”Á.a,!ïNÔA®)±+^lpEæí$ä\æ¢x¬â6UÓBÑ ¥×•DΖ %º8]Ư¤ºa¨6eÍơ1Åœ’%Q–ü$A®êüѸ§ímÛÓe˜•ÚE¹ª%₫Í'Ñ)C!Tt -âÄ-y&É×ó+”•ôʈp:' ]¢ñg œBdVX2ß<åc+ÉRmHaø'¼·Pl;X †ơ±-o+‰Ü'«ûi›fÄ©J²7óFÑB^o¹2æzÖÛÜ™姰†¤œ’Aé&´‚lƯŒÆŒô‹Å̉8g™ÉöSœŒ–œTúBLɼ*ˆ“ÛkĂƠR\0sOŵåæ ¯ø1ÆÛ¢ÁºVƠ0g¯‹3Uu^=4̣àéÿĐ£ '±ù8è)3®%Q" ˜i°W¥B«³¼‰ÀÂOêÑ}àSü?ăé]„\«X©+ĐAI.ÙÜ›ê!–ïÇĂ!kÛW1ʵLg²O rÇ•,†qßëÛBƯúAÅÅ +Ê(d’ö¶|ưTâ•m†ô!ûÚ.˜Öz«iÓ§Ü?9É<ç^r̉:¤±é\1}T©.¨ÆûÓ©FÏjGêhụ̉aÿ%{+Û¬ ºgB˜ ‡›cå8LHp÷ɸpcÎH)†̃\®EÇñvâC¹ÚY]rƒîíP”£,Øơh×HỆÅÛG ỜñV›¢yÛËäsđ“®w:hIÖ9Ê -\ÓcªÓU5Ô$Í)±Ô|Ñ”HÚO¥ ɘ̉ =µ@*Ư)ª0(*ơncˆJÔ“ƒrϪÄ1ürÑ©™ùÚ@)Î̀{ă´IÆjĐ¼µÓÎë/ ZB\¦÷Í¢ü¤looE‹C–¯ s›iưw½">µPD¯Lù³ƒAá¹Pwß$E™\ëá́[%]³IAQH}k™'%f'qÉẩ^7)ó0 ¡¦T_V‰̀ú-'Ư‚¼J[Ⱔȓ9ÆèÓ”ºZTÖZ₫³iẤ-2o .’4KÅ%)Ï{ Í¡GD0yS’e0ae] †Âµ¬µP…ZPóù“Úd‘^$k•-iÉÎàI¿ÙcÜI§I¢´º«4¡@_),Û LT3JRĐŒÙ’åûn’w_¤^×[Å£T†ÖÍfЇ8Ơ!N05Ê*ŹetÉvQÀw$¬X‡¢ -β†wem+'½3«)8=‹ —‰9Vß́Ô.®4c¼ ñ|{™<‹HTiÊ)ë̀ªkÛÖ.@kÈ}Yí…mR±Äơôhè=̣}»i„Zä¸Fúd­Cä₫ík̃‚Ư^”Á59—DEØkRĂ®±JoÑÅ̃¢ä–ÑÜ[Zb@¢[¬NĂŹ̀ïÆ•_°Ü”0Lr'́A?Xœ•w¢ä´:-ºû•Ø̀ØR—”ăÖË¡Bçâ ÄD`û‰|›u-, -Đ«¤ùÈ"ePÔvGÆ“øerđÙg  •h%£mêÆ´ÆzesgÁ7T¦NÉôÁ¬Đ¨ĐD•HƠI&5 ™½î…ZÁ‡aë¼î1+î'> ïËû}Rwj(}tv×ưPÓ¬Rj°Ù€°lQLATw†¶WS̃&S%?.DÓHèü.´AºÇ`f°°H©''eĐ½hª°€ün$6<’ ²øÄ \mq -+H)́­ê åMö£%JD™wưc/WeUw¬T•ж›ƒ̃Rï6£el)—§¸;ƒiA½9OHŒ&ùK₫íèßô–ƯRC̃äÉ¥p¶¹ø¬­{)%ÊEù›÷cE¢~,RQ -¼c9s!Ö2oP_§z^ùÏ@‘îÖ¶ú³æÅUö\•NÖ©•Ex”Y¨“̣@ÿ#ÿ\«₫°ÍÖ]@:8K\øgg×9ƠF•ÚI­Dfi¯Ÿô·b ªWà¨Æ^V4Qj/ởÊâÛ¸{®Ú¿°# _0sF…₫R·d£ºXôke5ø±¤Ó"\ -_|y$º ơ±µx|Ö19©–9̣¤$…¡d^“dm„¦*W½å-W­¸&Rù‰̉ç;i§aôx"Ä4[̣đ""¢¾¸ -)m4[ÊBoĩ4Ạ́6ç̀•€#YrRêA“–Ô[0L]‘DÈ=.F¢ÁMü &‘‹ơeṾdmÁƠ…B[¶Ë†BZ!Q="ÅIhx¢9ḥ ‰r3ñ‘Œ9…^†ëBsF+),¸#37ZªåJqænè:¯ypÆ₫ÇܸxQ1JF¯EŒª¸=biÛfÀq‚kû7 héP;åŵƄ£•₫±‚̀©æµCeü¹K ÈuW@ë>Åæn?`SÆ2Ă—Áª‚=Ù­³o]JU ¶Ûe^ˆ Ígwγñd7uV8ó “§úùG—LÍÁÖ,üè²aØ¢…³÷­Yº/^¿ÿD>öŸƯ©»yr\Ê€J ·%b=‰Mq”y–×Ơàû«‡.lƠ×”5EøË[i!!±%˜̣µñS]¬4+ͶÜhp£t”®¶ơ §ă/ÓÚOÖ÷§üro ́ˆđê¡‘åÛQÚ­>*D¿.Dut‘è~ơĐÈñ 2¸P“´AôBUyÛÖ2]RMpn%ß“aA×öD³úZ|¬úùd>>{[Mûèưø‘·ÏG#v­Ơ¬̣ñÁo}aI3+¾²¶æ§éYQ”‚¡f=ö…L=àélëÆ*¿‘øákzAt5ïØ[`*®=P˜p•¬'¤6P @¡/ù8^ Mëe*/£•}f -€H{vóà5}É/©»́Á·êK‹°DL5Ơ«µL¤4f(LiÙ ®‹*§dªN¼nC­V›¼•·ÊRsU[T%ơcQ‘ÄH ¾̃JÊ0ø1Yåô‘“Ê3dYê¾[ˆQW%2+¬ÓV§ß́„tüưÂ!X¥íơ¶Z8J̃ÔSÀ›³‰½Ê9*í[]euµH•D¯kĐg®̉'”ưF‚.•µ¥S¢j;¦†;\ £3Í̃¦V# v½UFœâ^ắ‹ak©x`T†×«B‹ƒú"ALj2„‘₫̉êæ´¼`  ÖiÀKŒ¨@½ÛJ± lŸ%¤— h«&‚ÇÑÚ°•Ó‘$¦úXxG>ăj,ae->F¯â–CÚ[ZQa¯u³°ŒlÖî[.4;ëÚuË ª)¥e'‘vL=¢˜2×åù₫DÜ«µ4 ñ–0âU¢ -­B{Œạ̊$ÖØ4>BéY&Œ—7z½µáËŒyŒ @41Z_ÙBküNMo&7‚̉ÉÆR 7¾­3₫>æÁø‘éo“i½¦^ÍøN×Ú¯tÊ›j€̀1ÖV¬­9§&§²MI툧ßƠ¬¼uU(¥;Ó>JƠ~KX_]ZoèÄÂu*¥ßHñƠyë’ªbKr(h@ξZ>¾ËÓ‹¾ØiU”uŸdÙƠă%UC}ÈêÇjk‹êå-Å6©/>´ •pZôE 2¶n(¥=Œë¨Ô¤Úvẹs­…h7̃T{ñSµ®ÖiĂ’dŸªú5EḰz̃X·Zª~o½Ä¢9Ö ±Qf×`‘}Ø0ÈŸíOäP‰ÖÙ içLÉEFv9Î -,Rµ®áă0¢y¼…9‘¼"ß)ª3§àBçeçå­ª&Zm’io*"™̀¾‰êƯƠ\½ÖjP9PƠ2°ô]EiJ‰”Ù]Å‹}—ؼ7—h¬ơĐøûIÅKUrÄM7•+çĂ8ơJTŸÙ±́‹̉ÜJrJ+)2q›mè¶êEÀV^zA(o°äM ÿDI* ëH́ÔÆ) \Đ}W›•jV*G6~³R’È©.§´è1 M´Dk- µtZDŸ~WY7EHF,mZETºă䌡À ÉÙV^"ǯLJÖ=K÷×Ư•¼ đÁ«¸²g‹SÔuÚt ¢~PnæĐÔREwÂCi¦h Éd¸z"n fEăˆbçÍÈk"´NWVièäj¥Cå{C'­O<ªª/Ê<Ÿ8Hñ³« Ë÷¾bz%&PôJ¹4ÑĂB7Ô¡À~Ai¢‰V±¼¬ëM$éˆAwk6ê•Ü‰Je“ù‹̉ç,Ï>Q{Yỏ̀+8́ÔùgăËä¸N“¬_ƒÊŒ+Â6Îdž¢MŒR¹¯b\(ç”N†n+v¶să­ÉlÏêkh,Ÿ|×NLµBlLÔÅZ>̉ZO‹ 1bÛÍÄçÜP ÈMḰºû~Û<›Ûlo‹ 0¡H±~ưc"|cäÁ¯ùÓ~ôƯæœưƒ̃÷~G¯ÚC*™¼Bå‹×ë@T¾xưt•ǜ* =÷4(•ǜ*0•üÜÓáṬC«Uø‘+ U:¯@~«´¬"ø-h×€«Àµ»^Eîă'¬à±'C¬äå]²Bä0+_¼^´Âß[µBđ§ƒ­pUVÀ­|ñz5àʯ;çuĐ•èÊÑ>‚®àđ"UỌ4…??ư?ÿ|8чưÜ'ú?¦y₫(D é -"ÏaZDµ<ôQoy®Å¨sÀ½h ²Eä5 -“OØny t‹©]kÀ[́ƒp!u Âå±ẳ>ä"êÊÅtí'ƒ¹P̃‚sÉÔU€._¼~̉Ô .P\낧́’9Á*hjíkÀ]̣[ ï’] đ‚ƒ½âE®y¡» óBchĐ ]ơ’Ÿ\ ö’Ÿ\ ÷½^ø’Ÿ\ ụ̀Åëu /±£uÀ/´Ê· _È«V€¿äKº -₫%?· -†jÅ“!`øÔÓA`̣c«``h½nÁ:‡‚ÉÄU`0|Ù •µ'Âä§VAÂpyV€Âp7¶`a2u0 ¿'BĂP–ÎÁap=Ÿ CgÊÓbĹŸ -ƒ§Ö€Ä¨X L ~q%P Ư†u XŒ »-¸ùƯVÆàÑ cD^S]£G×ÇHS_£GWÇkb|ŒƯÑǬ‚1i ˆŒƯ‚‘Ñv¬’Ñ£3(#®“ÑÁ_'ƒG×ʈƠ¬‚”ÏØ•Z +Ṣ¤Ÿ,#Ç̀*h9æWËXëđ2åÉ3r_mÀh+Af”$ÿt˜-û“ftD5£c₫t°1¢ÜŒˆOœ‘@:ä ±'ƒÎđ©§ĂÎĐF[üInÁ?y ”Ư‚€2̣(=º -ª¬ÿ - (mú((=¹ - êÑ[½Êîèªđ‚ Pxt%,Ô¯†y4”$â8”È«à¡ôè€(›Ư"JäU QZê50QÚßU@QE଀2F¹,ÊøĂ -¸¨8˜F™~°2JnFI)[eoœGÙW@GÙ“+À£ôä*ø({t €”)´+ ¤́Gg RFĂHy”=¹Jªlâ Lj“ü$8);-s@)#¯”2y>•*Ô°REAX,eÊÏZʤüp){t ¼”)s€)₫3ˆ)coO™2½r̀”)³s ©¢ă>jJO®›2±0‡›*ËÓ§L3Y9%¹¸:eä9́”‘çÀSF^=¥G·À§Œ¼~JOnPyAeä5 Tzt •é1s *#Ï¡¨ -yF%̣*8*Óç€T"¯‚¤2åqJ%̣*X*=º -˜JΡ©L]NeÚèÊÈs€*Û®Uzr ¤ÊÈk`ªôèP•Ùk ª́ÔÏÁªŒ¼®Ê4ó`•Q×@V0­2–̣tØ*=¸\Uy!íÎ -]eä9xƠC¤©“é+#ËS@¬ơÔ¼µê”UQŸf`V>Äû–Åû>™Gy Ú¤íV;ƒ¦ơ;à?Ágưcø°¶ -y†¶eä9̃ÖC/Ù¼w[¨[…<ĂƯ2̣y«˜u+°·&1Çß*&ß -®©‰ú4 ® SÛÂá*ä×C£7Ùă«Pgˆ\vÚ¤åù"L.=·…Êeßơt\.³†æÈ\FcsyÖÛŒ±5ø\fκ̀5´£ËøØ¥Ë|Ckpº́€Í‘ºªW¥ÓM{V—‰´9Z—‘×àu•Ggˆ]æ?XƒÙe ¶µË¶`Ûe„5È]ztv×£7v5~—™ös¯b ?ĂK*×*/Sùf8^¦A®@̣2•tå%̣*4/3>æx^3̣Xö»Óˬ¨^f¬Àơ²å_ƒ́e›¾ÛËü/kĐ½̀4Ă÷2]} ÂWÑưg_vV |•Îp¾́…+¾́É5X_&£× }ñ¾ï«xŸøe?:Çü2̣ơ«(&+p¿Êö®@₫²ưc•m:ú—é9₫—‘W €ézma€y -˜]Ø58`öè Lä5X`zr˜ưèLä-D°B^ f>ˆ5¨`zt ̀ÈkÁWe6˜Éá9:˜ Ù5ø`zt B˜¹̉æa"o¡„y†fÔ5Ha’Î[XaF^ƒf.Ă9^Xñ$VÄ0#̀1ĂD^…f9n˜‘çÈaFc‡}ñúAô°/^¯Æ3•j VÈ3 1#¯AÓ£[8b"¯@³Cđt,1;s41‘Wà‰•«>C+䦘ñ”5¨bÆàæ¸bF^,f̀v-fJæt1cös|1#¯A+2k†1fiʘIä9Θ$ÿ¤±¢3̀°ÆŒVT«₫ØC£₫ó…₫óOå~̀¾Ü3ü³Fơ:@¢}ZH´8P̣³^Đ›ºpkB<À¦yœfätÚCï˜J†ÔŒ<‡P›ÿl'Ô50jÓo˜C©•O›©=4z:› Ơ -yªöĐèÍ7mA«̣ \ÍÈkàƠôèÀÈ[kFƒ¬y³V^2kÚ_F¯€ZÓ£s°5mÉÜ‘ç€kF^¹f[?]³Îa׌<^+Ôô‘çàkFĂ¯[°Í¨‚ÍÈk@،鬀a{ôl?Åfä9›ë5plâ««Ùôè$›‘×€²éÑ-X6#¯fÓẦ¡Ù -uÎ&̣x¶ÂV´ÙÉYÑVñ -6]—U0mztP›)s¨¶̣)Ok+ÚÊ ®Í^¸°Í̃8‡l3̣´ÍÈsØ6ûư5ÀmöèºÍÈsđ¶̣û3ø¶²Î3·2·M7ÛÍ9Œ‘ç0nF^äfΡÜLµl7ß÷Q0·rgpn峟 èV4ÜMH7£êüÓƠ]ëV>úéÀnö!sh·º¦Tº+¸›‘×À»Ù!™¼̣ âÍÎå7{t̀›Iú9Đ[!¯€z3]i Ø[Q³fpo¦¸Íß -ỵ­êVp76„|öͨk€ßLÓ˜C¿y₫VFÏàßL̉­€+âugÂxWÈ38©[@p…̀±Ó0è*08{pgÍÎÈk áL±™ƒÂy,\Q fÀpFCĂÙ—¯‡Ó“[đpF^gjè"ÎÈ+@âlç0qÓó3³€€³CøT°¸r̉gpq"oÆygä9h\!Ï`ă2ƒ7Mä-đ¸BÁÇ=j‹VÀˆÜ+s ¹ƒ{z‰{úà†ëƯÇlåߌ¿ÑWl¾‡·xøů₫Ơ5¯sóæ뮿¹½!¼₫ê⯗7…ô‹¯®>\ưöêöû‹»_~₫áóÏ>ùïùYi*xlY Ưß[ñ?đSnqà6wm8qåÂ%º*¸}§ˆq>8èµÖd–‹HÉ{DQyæ„ÿ+y¶‰YŸG[¯ù¯Ï?ûæ±µùzm!©£ùåx¾úêWçç®ÿ|{†Á›×` -‡ˆE9IĘÁ?¢–Ă}ÿy,9Tù£²H#¯I@À€”èÑ̃9Ÿ¥Ơëx°2¯P}÷P‚hdôɃ‰Û[Ø€đ^ôN”hÚ/PÉ*¡3#vPơÑQö)œ\Uïç?CåÍg×;ƒ‰z½‡™lĐá°ÏWb{OÈÀü3,Äz•º¾vŒ́|ƒ.œh”đ_‚´dǛ¾N˜†×°q;;Mz%…¡=;}GT=çä§êƠTí2™5Ú{ơßoKˆg¤0 [! ¥p¡ 4µáu‚lH9 Å"h¦Qµŧ^‘w'4–5­ZĐüzâ%°½z¤˜CÛ‚[ï¤8`’I½ü†.fë–é¥ÅÖu*ÇÇ/†¢á÷(xc©U¤‡)¿ªSCøc\6YhNJ"̣q̀ÊŸaưË£ZuçCçä¸eẬ½¨‚düb„‘Í!f Ø!½µdêPỒ„»ptzpúÅ(Êcªe&!–íË·ô“q¼¹Yq„̣ẃ}C@yëÍ₫d́Èä̀r‹í­ï„Ă—,!; æøcdf:SÜZơ¹̀ÅA` Ç]ƒó'´Uc ˆ†}é)ÑbæÍÑ?ÿëÍ}f»™}ß^¿»ưpóæèư·gï.®oß\ḷ‰̀~ñ§Û«ïÎ~}{”ÅéưÅ]f%`†®²EüïÿtF̀l¨~c₫ÇÀnÿëèáeĂçvÑ¥ÍøÜ–†b´¸ÇZXÇcOIÄ?î\]~@ƒFÖ±ơâ¤.60ùˆu²́k20Á…L!X#‘ ïÏư£Lˈ:sèx( »Øe,¯¥̉uÖµ©̣̃NŒ̣¦Zo¡Ø¨_Ĺđ{UªÍÙKDË[ƒU@Œ³-w„)Ø5î·røw™Ç-eđÿLưGfä¥4XÿQ&[F3GT)Ă.₫êî —!ù )À C­g cxĐ€;3¯P0a[@_}oúˆrPÄ-ÊÍÄór³²̃˜¡‹ÆƠXDß›áéCalÊCÉY¡­ (¾ »ôeß;­¾16µvÀº3¿¼/̀5'e(±œUHj̣íheđ?£¹³À̀Ô¯Û"z>¨T7ÿZb¦¾À©ỚPÎw4¶t:¢bbª ó‚aw'§Î»8ÓƯ„‚ºeQPñæùDgeh}ĐYÁ£NƠ !x4`l÷â}₫t„̣̃¼7ª›¹&-8Z‰hS‹£=Q7¦Ñ4˜-j Đ%%ÆçƠ Épº»d¬£-¼¤.8/#¼;ÿ¤g#ä.ùO~ah1–úÎĐ8R–Vͽ`ô₫si‚*̉ÁöĐùC§<=*̉Á%¢À†¡|\„ô–R\1TÖ9+b0E”§ÍÙ•Øœ¾# -àÔƠN•EÊáÏÛ]ºº:‚Ư^!²j>“¼ê^¥^äĐÈƠ(ư×·$åß~ÄUûd%nt‚¥Ëâ-u‚Q ÚpÙ›<µlO©ÄIQ‰µdÀ ơÉ ¹À$iÜA5ç©4Ü™¢Ÿ -¤0ưª¨R棕µåµf'Y-¨¥dĐ́Î0!“ơƒñ–Jz»§n& ÊöE8.Pë8¿}´txÃäS%÷\o?̀@œÂfTĂ’¦ïTOw4É• "@¶ÖzĂ™ -ÚZ¯ÍS -fO6jBE±¤v²V¶æº“0^SPvB+©7ø -BÎbC+ bGPà˽ĐS,mlX© 8”d±gÉ́:s"Áà¦F÷IB£.`­Â…?z`,±¤—J!ÊßÙ -Æ RC!(º¹%ß WQĐ’ ĐØ‘úFŒvĂ.“yƠ£đ)k˜é i° @[:T ß‡O“Ü:©­2¿[Û₫†Ỹ`n'RUku?¥B$䃆YÑỆR¢¹ÙCQ ¾£Œ[H3§L']½67́Wâ!¯ä=øu¼ñá‹J‚kE}@r–K˜¶qëApQ­ ÀbÇH½h&j)ªg×ƯÁrGá*bÿØFVCé¤uÇ+ùLå¹̀Z£úeư­€iKJĂL^a 9º/: «é,+¼µ‰)à´­ºOºdÑ›¥x_ƒµB„&TÇÏ’MQ7ÂQ²¯2ª‰^Dxa¡Á†eœá"¤ñS+ß"CxN}›ppđm(Ǜ‹   ‚Ẹ̈¶LȦbØ4z¼B5èX™î¡ërc{(åŸv₫m30ưï‡Á³·èçŸ/₫zù₫₫oÿËï.FÓú«ÛÛ«¡¿¹9ûæêâw.ß\¼·aíö ó÷w·w÷“W­Ju9½ºưđ†I.fª7…f¿{,…Å=¾‚ ²°;´ß 4î“zDåípÈ[z¡ĐE„B;© 0g3ƒIÅ{ÑÂ.„ÜŸ°µCÛ^”•0’¯èSæ¤ÊÑ„£̃C·Æ‰@v[‘Â₫”<á-hK<=†”é;ĂïJ–w.d¸̉k¬>OÁ…>â ûµĐ ë ‡éÏF…öL -AgÏÁ‡Ÿœl3t¿± ơ/y(dƠ­Øµ'l_¢sÓ“ă2YM;”m­½>'h¾O4¡‹;3, °ơ†9â:(Ü^!7 ¥æÛ $ÅåØ÷ÀE(ĺMdˆQXPa?1"d÷1t̉óáædj*'M•<à`iĐ}å‡ÆÄ l­Gbà É%L~µcᣲp•vúT$—°Cx,LÅ:A₫X3z빂eó–;Ă~́GåûÅ!R©ÂŬ86‚ë5ëâr̀Á#ÑH¹¤eÂ_lOØóÜ´;é˜GAk.Zá BÁ„Fd[́•‡1ôÖ -ùƒÔ¢ÇÜ' ~’·V6́^tÔu’êLiée£ơÉÊs:AçEoÙê]RkM v­ù±;Yôˆj±̣ ©Yp©â8²(Ê9É 2,9đ˜%k´ / Ô /:3dđéÊŒFôJ×!ơ¥è«cGf êU•ÄÿSSʳÚÚN‚YL2´.&ùôåS(·9m]vfé,0ä³Çgï„n ”£óp–‘X&Çú\«ªÈ²z”¶>$ç)UBú ơÙs¨é/R¦>®“Px„§«“ÉÏÉʘâß%gت6LQ¢L1J†›Ø¨ăcAÅéL/O­\ŒƠ먄XAk[Ô÷^y ƒps‚µÂˆBçæå×ki‰æ)ÆZ°üµhAÆP´@ƒ|½«özg}đº’uŒÿèp"{đÈi‰Æ8‚pFú̉52Áă†ÅPÄ<×₫¤`ZfU¼)½â“¥MƒÄp£C½0NC -5%6S¤ffa)_ƠÛ‘ơØ,W°ëjÔ*BÇgĐQ #X\똪Ă^^¤l4¶ -»dŸ hF5ÄB´ˆqpXUl0‡¡Ø˜+î­|áƠ³đAP±Çå#Gs„ÈÆ ¸8î́ƒAp3(úÀƯ È¤’D ’"<ù´}gE¤̃̉öôl?™Tp:§¤%e¢åÍó ¢ æ³É[¥dF!˜ÈĐaÜđfy\u˜ÍcúLËÁR¥¸pká&ô¨’`°ÑÙlăwÈuƒ³9z\BÓ]´ÓJ¡…mcđÎn ëeK»|á~Ô°{È0–vVö íTáä„„‹S!>‚ÜJ³ -BlëD=ªŒƠîèÏơ„¢Îj´+ßÀä°^ZPÙ§âª#l3Ià>c`nM‹¾ơ‘ÉÀÉ2Ï$ äcÑợọ̈Ô!A±C¢kT^#“yΕx Ûư™s ̃‡®±·°™s¾E14ö…ù£3K§¦ÖÙ&±}fÁ»T'V %p›ˆåÓ?FƠ”-D|`虃@Z9ˆÙMY¬G'/G8E/Ü¿8^¨ÁÆÁĂ₫3ψøȈÆd)`L#× ôJɉ†7¬A—B8©zyÖåù - ơh¦Ü3³È—,1—Ÿ@̉!X­Ø`†k,"zkôÀÉơrlăPJKAËMh\€|ø­(>¬¢ wMéåt‚ÀD•†³…£G#a0¶Ẓ$ D«sñ'LˆY餈œ+ÆrG¨"²Æú¦¤ D…D·¦Ef…Ö÷"«×…º4c>ơñ́<=îJéơ¡÷Îz˜w½4H¨ÿ‡¬oX@‘?L=Ö²Ld;"ö¡Q6$bpRטyE*i½³|¥ §VÊcŒ†Đ’’n&,å±ôªÆÅS̃ -2[hQ =[ˆ0VÇXbÊ.°K¤“0—?©2-g‘¼BØ“̉áŒ+ đỌ̈Ô„â€j­Ơpí ‹‰7n#]₫Ç·&́àäưrf*])-̀œseíz*© ü°ªßG8Ôh­ñªôW,« àhL¡‚IÂÍX2â#d¡Ô2ç¹Ç møÜ’ù†F'G¡H₫‡å²©4Æ`_›Â9ß›Á¬›K‡ oéØP]ÍX-8ecj8a\: ÓBûB+¶dóî¤*E=dr§ó¢ÈÈ…ÑâKB© K²1¦µø~S̉Ô1Αjq¹±j#Œ»(̀OÔ´½ép®äâ`k¨D:;̣®ArÇ₫ơÜ$ô¥LE³AbIÛú­nF»úwºAYÀbÊ.R‘“̣.Ơ÷ŸL-p±¨ -j£Ú],b¯à—V‘‘— ({Îđ -̣:É„ÁÇEbñ`”Åî¨ÛY›Æq)>èJà“ ư9́rÜŒª œ"2CB²¢xNÔƒ½.JyJ¶ˆ23¤+ơˆEKI² -0+î30Sˆ[³'K×7¿Ù•öŒªäc68®Ch僲NÖc«­Í`áW Yb²tÑâ],×̃&«¿î -₫UvdeÀumífà1c&y08'M–Ưˆá'“‚ŸGä»a’A« hE¿Öä3x«¡í­"3?´ÉlË”¨Tơ£Ơ„,<$*NÚ@ç³nẠ̀ÿ¡“×C¨''É(t\R°!dÍ¢2„¦/|IY¶ƒ·­Ö¦9™Œ Tư@ñ5h‚₫ÇËZL¸4£µ9‚jüV ê—ưˆÇj˜! Æ@üà’p̀ÏSŃÂê¨GŸ³T%ïµg\§7ÀÓ@·‡o,Ađ¼‹¦vœ3s„9,(Ya±@#+Ơăh®ĐÊÿ|ä¿»;{ÿ¾‚Öÿû‘Đ¥^ócGD÷Ä´ -¦¤_“)·B:€mIèLơ æè5Ro­é7凹•é£¢ÍȬưj^>Ä0Ô{'—Ûs„TD+@G™ïÄ~èÈ₫Ẁt`9eƒúÛߣ1O’G Q¼‡®d¨o´{²Ë†»±“₫Œ*ü<Ÿ2xá6Za ä‚xâ-å·íqU~ȹ -²ƠĂ·VùỠÈé -bÊvR ]¤SóáÉ«Pv,U*¶¡ÓÉXj0Ư2aS&5hè bl2̀Z©‚0:•æ ‰cź ±ï`ĶL؉ÔK› 9́ºaĐn -3*ȼ—Ḷ²â$ë­(à$<Û$ÆÄ€d:"(Fà“´]<œ̉=SṾÎ & -ª‰ËdÇ+đ:%™ ¯üđâû?ˆ<t”Ă¡ha'2ïƠ»˜G"Xé21;e₫À·rë†ói@–‡³Â8iä”’ÓEËØ$^¢*‘G̀B"¤~afÉ|LH;ƒ₫5Ư+ưŒÉăˆP6–¢5(¼88Ëór†i‚ü\°Zø̣© ›ŸyPåơA1-ºó> ^Ñ!Êß)0` ₫&ê¦f½•YwPº&Q[,'̀̉¤;\eg{çµÔ+zMSŒvZTUS´˜‘çk-cè ưĂ®âfB•Cƒ4̀ÿv É"zjlu¨Ë~ú›zÅô£‡ -ªfâó±Û F_¼†/ßY_XçW?s!Ô?1:4}´æR,åñ(2ƒZôæ±Pó“Anj©“^mÏä’f/è$³ư”â¤1ѦâÑw©†Èf_4í./9‹TÎơ« «•ƠH×G #ÇPl¬‚j[‘J™Ô—ü$ª«a¸3½ ̀o°”Î6X¢äÀ†GmP}́®ƒ'´bü së‹X<Àê1Iua–ñ¸X7Ä´WtO´„²œ,Fư  ú‰Q (TÔª̉Í3XÑÄ„j[dKå7,ÿ´'́_ơ…4V[H̃ÏÄ京)ê°ƠíÀ”†¤„ƠØ[_ ÆÀú:‹v¢q`ºB>à:¾³6øởcsvoíÜƠm&¯Ô@¶_À4säÖ:›O6ß÷& -“ÖY×ygZ§&GÊ &JÖZ¨2SÛä[Ñó¶e)8؃,ƠlÈ)ufù“®A]Ü Î;|@¢Qj́ RÓô¼ pÑÜhĂœÄY»Eâ^…ÎÖÍ)…›×ÎZ»ÑP¾Ÿ£œµi ‚…g³d1đÓ†ïxÄ´p­–ˆŸ ñ` Ơ•¬Áµl0ˆÇÔX/(_ül¸¯¡9ë*‡l¬?Ä5Z"«Í÷46Ÿ)S‹Vùâ‚D>ü6ËZåC)—’¹Ôܱap…:¥n”᤺{@ -%vá»tè¢YƯ XjŸ3¦Ë裼ö%œ…µz2y°=‚€dlK[áРõÄ…2₫×æH¾C/«~RüµÁb½½"™l‰̃C£x nCă­K â}Öº0Äü‘.Î̉µ Fùa]'à\ŸhqÜqúĐ›AơHæ†ơ_ßItS»ẾU9DƒpoL´Ak0•SÏ)?¥¤tFZ¡̣ßmP—Xê®°Wçí 4×óßA°<­^à~O3=àA¬V5 ÿđ4æy½LZ¯–Ơ«%ơZ9½NJ¯“Ñ+%ôjù¼R:/’Í—̀«ạ̊j©¼R&¯”È åñ"i¼D¯Ä«äđ)üq¼J¯’¿O—¾+dï"É»@kdî:‰»B̃®‘¶‹dí:I»Nή“²ØöÎ́_¢¤<zRˆy-S1aw[»Iü@ß ó¼W2éD…Bm$˜T>NäÁ†ÉÆAH 'À¿éóŸ?¥iï®93;́zu£´“­ƯKG<¼c"#!4l–Æ]:ƠS<|`‹È ÅR&~%̣¼Ẫt¾Ù·ôÙÈkËo€ '2CîjœEárÍ?pülV·NEoơ»=Ëö‚¢Àƒù!CIĐ_IGÀ0̀ñ”ƒ®VrZ Ií–K±ơ[ú„d!-̀ -©P×̀~dñ€wj[ơ£zD|™ È,è ÄE‚[r₫{7E˦–u„פ±0¯a6³Z™'óK—*æ0U—’©-­0BÑJLSFH•œ¿Ñ &÷(ßĂªÜ“øÈö‚µ^O½¤óÀ)¶ÊÜvä”Ê¡7¸Å2̉+{ª¹;•´̉g©å`ÆEkeôc[V(é óÀík£Ú¹‚i ‚ͲwÎ^¤¬ÓDđWº “W̃™J'ÂK>fÛKfĂy˜k$Å@´ÇëƒcŸE‚¬ø̉°c«':Đxz»¨X_kSêpp15(ÜHדjÜĂK©ÀS}4{ 0Åɱ™œ¡Î¡–,ËTưàä‹fơ­µØ@BAöK̉ó«[k“ï^A¦¬Î×VƯÈ;ê­Y ³+o­s'Œ^Ă - ợêËSÂ*¦|4°FœaBT( –yÍ̉«AkǦ­pÚ¡³Œ3\D,nÓ­ó%A“̃’ưÀ‘GMHHD8¸hAơĂ¥á7¤NKĐ?ˆèH̀2!!²\PVĂüR&Œ0iU7ZDơ>uLnçƒA•Wj÷Å–ÎzĐ÷Ö¿&XªÑµăƶ¥{@»¥@Œe» - „,ø¥ô鈂ùDĂó¤0LơR ä‰f“ åq‘',jđb$M²Gz°mFTóæ¡©ôKp—ÁjøÊ¡lÑ ZdE ,æè‘øV l M°tœÂT`2yo` ,zB2 ¼ÍØ9˜…$æ°p‚S%¸1–å>H¸o¬ó ̀f¼5¦.#¬58UI6IÇÙỴ—…ÀfJVƒø~Ăâ*&$œJÍPôÄÉDº Ầ%kz™¤å₫œZƠ -Ï‹TÎ+Ñ4Çîêø C̃ ù¦̉Tæ jäṆ?A3Yß|/‚J,̣Dó˜® º¨¢̣íR)|jª Cy­2^‰ÖGhWPåsª1 JTb¡Ey“Ùà1—!²ƒ—‰\¡£ê¤Ä¡̉ÎSAaë ®Ë ¨tkÙ§Z( ƒÊ[0¢A†$+…hÂAÀăEîŒaº–\Ç -C§9ö[L¥—eÄ,óéCêH`SR6%èÆyå¶Ö+©жcÇ44ñqGy¢‚Đđ¼῭ÊsT qẠ̀±K<«PW°Ÿ*¦£ß@¹´IRưHjú‚j–©ª|„mNUoB¼ùzØ Ë€Çú‹åm×2cSU*|*©…ƒ™“B1:(ú3À!,†î‚0+¥3­gç…%Ù¾ ôÿÏ̃—¶7q¤]¿Ÿçº̣D–›®̃ª;„$`0˜%˜0Yà`[IN&óëß:çÜƠƯ’%/Y&É<<ó ­ê®êꪻîơQö•¡!›‰•¿™êfX6‚$aD•Ïăhè/XS¬Êe%zfÊà5˜Dâ2́J)¬½` ¢î˜Ê”‡2QQQs*\v…pï®–  3Ÿ–#²ø<, Â æâ;›¤<ˆú`K«åÖI‘vë©Í²´ˆ)2A×'©RÆÅÂü/¤gxgrâ¡Æ Á…¼§MË×QAˆË̀ ³NĂX˜¬O@×ê)ù cO!Q ¬£ÙJ¹§ªQHÀÖb/A!* zi"!I2EAPV7RÂç\½¬'¡HÈ*Æ…^ă,å rÑXëK‡dsNñ)´z˜É ¢E5»®‡°{Oÿªø/ J<öSEɉ1Y£¡ygP¹/̣ÂœáÚ]ªÙO…Ùg¢Ï'%å*¡Eí¹ O+±{đZY à¥L³öZÁ{+ xr´ cµơX©b»¢§¤BÚ/Fh(:’^xDđñ´(jÂÇ6OGÁès ’‘¤ ot -AäC¡đ²c̣Læ]*äœ&³:Qd‰ÄT ĂPOó¶j2•Lê(Og®Ê°ñĂ†ßŒû:́¿Z€¥t}Ǿ -<:]u¢¥œÑÏ€’¾úDiăr†\înm§Ñ̉r†ßS°¬dI£ÏĂÉ -Ă!­´,¡O-× ÀĐ\QIµ-Ä‚:W>µ«%JêR©ëΠaÂä½:%ÈJ–WÑë‘°đ$!ǻ;(!©´>e‡KS @C¨Î‰S ¨uD ‰è`IÑÈ»ñÁRơWĂˆ$¢C%¨Ï ]Ă!D”?C‰\ù$,9Ö \éM&‘T.v ₫8gBü>â¢iƒñaà21œMF—€̃̉b#t­̉¡ÄÖ”¹`( obD‹' .Ä¿Ä"LØưä®$đ'ÏGâ3ñ/…¢ḉ6¯“iÄÓWMbF,b-ÛŒD<„r±£KoE1C™¡̣O*bĂǴZ%ƒĂ°€†'Û…*øµ0-Ö‡r«2³YUdI*̉đ× -b|ø’|j,ĪÂy "oÈ₫’~óR ‹ g9‹ÔiÊÊ@T­F†´8Шéc’%Œ -3ïJz¿hĂIcUEc-¿µ -l„…q;è=D\C‡D¬¤%R똽MH)ᆰˆî“‘ƒŸLèĂDà$vo­¥¶¦| á:¨T¬ T»‹Èå©àĐ ¯¼¦âÅJ[\µC ¦“[‘3Ï<û˜S¥º\á!g«-j!̉ȼEE>åcÑ%\»€²Âj·„ǹ¡ ☧çÄ ¼ÄrÅ|iµó°gÍöæó½Di&w.̣€À*••¼d…NaĐ(IWsH’đKô‰¯rFD́*æ®5ű´+ ̣»ª €…í…(̀XË KđøÔq1ÚI†J±³,® ^4Ë€Đsó bó_Ă'5•Ènŧ™ JÂäÜéiN{ë&'p3‚§¨`„BLÇ̉©‚úPDkçÅ /ߘñ³4††*P§v¯¢x ¡Ø„L´I’ø°ÚÜ© -Jô$ñEcŒE”TÔ  gº’£^›ób"ä—Jx<ôá,ÓKèeWZ^^j₫á̀ʾY¸©ª/HdƠ[nXañÙÆàéÎÊÎr±mÁP’;pÖĐ9‘;Wf†¾ÉØ>̃»2ÊÔz®7•NLzɬ¢.j ¸ÎÖ7FŸ?!v‚¦Ê×dXŒăg̉ñ0st‹8öÈú¢ø⌠ú‹˜•Êl數ÇI­Ë :@ ä*-{(¼0~¨5M£`q™­6Á J¡‚­YíkømD~~Tûx¦^Uµˆä -áßÓæ¢æl.Í̀ ªmĂªÀ Äåà•@è甄4₫y…p;Đ8ö$W̃6‘%2f~saG&Â2å´˜ „OB­C•îá Đ¼1Nq¤ä$̉3²„( …ÿ%·6%€À€rùÄ‘U\å©@üMƯÂ%*NôÔˆA1ŒIdP ·î̃G -,¾eoâM{Jå"“WTÁ¤qå k^ú< -3‰ß„$2x̣!ƒ…&ÍîU -U¢_™7"7˜¢-ắ•fă#Â|fäñ¢ åŒÂXB«ëèÅ̀.–«F“+7ăeä”Ä1´Z ¶¥m—«11‚ÓRt$M·µª¶y v]$/°Å5—èQơ74¢pƒ`¨½K+WÚ]ÀW3°Á‰‘˜/G¹Û.×±b¡A\K¶f,­ẀỈ2A*éyÔ[oTºƒOŸ¥­§® º“2CÎd2.†đ4zxœ€úk„{̃ˆ²¹È% ¶ˆ5‡J.aqÔđ®Ë%¡$÷êFHYó =Bçà°®Kñ@"Æ(¢ ¨đ.oܵkV« Fâ´v u’¸K¾¹¦d,báÚ5bÖ¯_{;*=đTJ^ ªéØ£2®™£›×‘ªH~Ñ\Bœ)&Úµ „óùtóÚ X̀ ǻE+ćȺAF~Z[ù}my]Đû›qeæ³O@‡úêúA,ô„  &‰,ÆÂŒ¿ Ñ›aa0ôÀŒp’‘P°À­á˜`À!bå2G#µHض ư‹.nd°zAÎ#OD=Đ»ÙÔ5tc€ZI2tf®R,ô’ư™ Qíë˜BÛ+b¸â”@{ -  Ëó\a`Íƺ1:Œ¡-xĂg´ -́aú|‰(™Éœ̀MJ A»ọ̈jú¸°X$Kza@”JĂ*ÑqÆ’ÇL»B˜ q!…Äè–È -\¡nQ;kmlø;ˆ²ƒ̀‰‹çŒY'ÑÎY¾C™–ºU6SXV÷_à)„ÀÎÄQot̀ÍË,(µa)p±”3“Só@°Ú‚‰›àu‘‰H?§yM§„!V¬G‘un‚ù-qÛ|C}^ëbâª2^̀‘XFôCØg¼£ßˆ'…ô–¥4/Œ'…´·§Q„eªåà§IECœúLp/Êt -ó̀@ ¢*Á•gf'4Ă U‘0)₫ #D̀•¹ÁåƠZûRfS̉˜ŒDcL#M0#Qu‰̣¢y0mà¼ơfĂĐ2Mj1jl²‹B‘§ÝiÎr§RĶXxOúŒ+óHùT º`Ưˆ× ¡àêæ̉Hq­Î{¥â&VåHđp¤ñŒĂVºzƠÙäªó§D°ÇH²ÖÊ׋×J³-i×°­p­P^‡(Đ̃)7¾{HÆ x ÷ÅÈyu I«™ßéLP˜ó²™ezX3gX“¸@ôÇy<… -nÂ3— -‡B¨çÊp(¼M‚¬* 39¦È£G…ö#jà3… jr=i¤ÈÄ’Z*̣ -!|°2E<Íă¡XG¦‹›CKaœX@.NMœ;/U\¡6́[X?4Ás‚gïÆë/ÍÙ̃+•¦Đ¥¤•3 IƯS—Ö"­æû„ÂÛŒà‰†BnFåb:ÉăÚ ¸¶e›Y*®äF$,W}iaÉfV!Mđ”A0\º4„ÍÔµ´öÂÜÜt₫%âWɈ-ơª±CapB¹\É̃‘bÍU*jjạ̈Ȫ*µc5Hm:đY¨¯²éWæ§E‰€â¼ÈÍhÀÔ$-·Œm¸5-ñׄX„¢;›’89,Yf•Áy5Âs0’éơ¢q\„¥ôÔW5/ñ†weéˬh…Y1UÑ@.Đ [j³ÅÊ[(JØÆgóJ,çv!^ Jđ˜kb› «ºĐ5¢ü̉jDHÚ’ÄyÈW³h’臅³:«”M+ûYY‰µq:à“¢ -3ÄSUUǜ©2­‰k‘S+眀ˆ©z -Ï‚çu gÁêh%s6§\L4rɨ/ỲDAgF}SĂKU€™è¬ÿU₫] ©áÖ£””$+^dfJ9(¬l–Ñlåk%2›Ø?Ï(é,H'°Ú,5t©B‰ÿ•’.á̀‡CΔ ˆoŒĂœÙÄÆ’›ÖBßOzºÆ´"­£§̉¢\&ß':uPÚͺ9#ñY#&1ăû³¸mDsHc,ÇÀ’Ơó¦”ø0J>ØØŸ¾îƯN^÷F»—ˆ̃·ßùÇáà«2(Aáÿ‚ƹ̀₫ŸÓ/IĐí‚Fw« «¤\K“ôfĐ̣p{>Ry+I–B¶dLĐ c¸ưcZ߸́-Ø[±·(boQÄ̃¢ˆ½E{‹"öÇ¢ˆƯT8®÷Gû½°{—ŸÉK®ưƯ¡ ·{뻣Ÿ‡ăˆ½æåô˜jĐơÖJKÛă5’;Âïz’qHô¦8âá!fb¼&¦c+”œ¤*,ª­"¤ö´k"1t^œ@ú—rÄ₫ÉD*H$}IIR¨„,Éy»‚Ơ=Ç2ÂĐLŒ/̀¾¢[¤`)ÅF¥:‘˜_¡Û”û‡¾’2Ô‹!´ôh‘LJÑû¬°›²„đëă™£í y´8aR±”ƠrËÉƠX‘² s¹ô„3œJ)£ADI -s=­TÑÁ¬ê2¢mƒaB"勼W¹´MÊơŒx̉¸Í›¿°Ä™/ ›+É)u¦úÂôu–WAŒ…̀øŸĐi•*Sñ;¶u&™0€RdÖw]•¹˜å†ă>ôT•ÅF åŒèYB Ơ¦”‚ßY`ø£°6«Lé<ë\øôY#P ¬ŸÔùÈ¿%+wxJC#’6:£Z̃¹ü|¢:4$¨)\`xÙî̀¥ôPjåpă@ñ³´=Ë1A­~_j¯àÁ5c°l…Ïáœo ?{†,ÀX-ÈX tÀ²´-À¼hJEọ́ÈÛo±ÄT¡Ê*¸̉à̃k¥iáh00¸¦+ÉøD|sa•²^¸–ëE6LæjQÏSV£2K²P‚^Iî 5†é‰NIoȺ„ φtƠơ2œ&œáp""G”F!®ÜY,^JBtĐ?ƒàDǹ?tơ®-†£³Åœœ)v„L -ED1IdRv˜_ƒ®’R1đ̀T²ë¶LJýU_IÈ‚̀àTP†Ç[¼æbÀÅ̀\IUeÅ{,Ù!Z=´;¨C0+¡øë 0ÑQ““›Ûb@ªÖ‰6N]…HsĂTÑ’DRRׄAOØlOQpÀ½²¼on—„UT‚'r -%cg¢̣ƒó¦ÔÑ@s"óZí`€dAÎ? c –‰}™…́¹q*#s“^¨bAĂe„HÙHƶÇơE‹%¬èEB1Ú„¤º,úÂØPfçÅ Í4U†k™Đđ ₫?5æƠ"Ù4Ü‚̣·Jz³¸Œœü›\N12ĐĐæ•Ñ’)ỗ•”ç¤À7 -á"_‡Tëđájp][*æ¥×Y`B çkkÙờ×/kª?’´ #¦æY—JV ¶Mß 3§KYé¢$`s$p×dRªX‚t†*‹Œ{Ú-É-áî®xúd:±Wb‘$ù°¢Ỷ5Ă‘dÄ׉ˆFx¥Êqc®;¨¬l·•ÓB(è/rñ*Ñ€ïí+ Äăó¢bFz!Ê­ºR†}~êy5Ó—U¡êmôYrù׫̣°¾ ¡b0œ/âĐ$'Z ªØ¿3öôÄD• +‚ Ááal3̃"‹¸(¾2ˆFHgÁ8._hgĐ “†¢(3Đ#ºâ?W:«« @®æÎø‘ŒĐßÉÊËTar¤¤Ñ¸)U°"½¬§Ät•` -´‰̃)$[¦†F%²pƠ³QqIă‘»]£{0ѳèW‚"D&ÔRÏ ¼Bï)\—xÄ)Ê;`‘&<\cµAiyí.Băet¡¦|0Ư¼qíe!Ă%ÏméDz¿ªˆ‚ñ8z£8ƒ&— -i,r óăb*ađ¥–¶É×#¼,ôˆ*5hL….¬ äÖˆ ¹'7€NÙ¡đ`îIC–’™V¨ -œm¨,29–̉@¹ËóƠX‚\“ôŇ…P™xÎs×C¹À|sHĐTÆGg(wf)77=¦Ưå‡ăFŒ„4A §̉¶*ˆX„,­.fX̉ -¥i“* Dqpè.&›.5t›x%™ˆô«"æVjLrkSÀ¤ñàÏX…t²ỔÑ hƒˆy™Xz -…áJB0κ4+8áYê”ô!—ËTÊ'\ X̉‰uX+ƒ§\́¹ÆÑJ»x—™ừAÅK¤Ô÷2»‹eÍa'3ö§F»“HR©;p/K ¾K™0E’;z]¦0j¿‰’jrûJ‡ö¡ ê‘Ƀ=åñ‰í@ l)#!¥ù ˜‘”qÚb̃HăïNU^åbE'×r뉗¶fzá ŒïSU@ª£M3cÀª SD`Ù5…}‚̀Rñ$Ø%Y[c´!-SiëñtIتøvβ2++Ù§“V´‰²·È3¨ÂMô<¾­{E4¹†$J-g­£tà¸0)N2ß -Né_‡ƒ’<#»D¼áeÛUb[rHNXë“hNÀI•YN·ĐîEÊZ¥ê«Â˜YUÑà`A -!~rs“³¡́L₫Ê`ơs̃MáAK³ïLE³:"®É‰gn›’L¬Í ¥à÷₫IÆIØ ‰tW“*ke"»‘K Ôä'Zlº–°©L”₫ªkY*4X ofEƯ8É‘ÿĂ˜y„§±D+Ig”$%¢jXY2gˆ}b?̀;aÙđï’§4,¹ -= €AV+öx-‰L̀càƠ–6C•5t ¢å¬¢ ßa4Ë’̀Rí]X<¥P¥2¡o±U!Àj®;„käÎ,˜Á±qmó¼†3îJ¹\¡ÆKq¤c«́ɦ#v)«@YYĂ°eªÔ¶Jy¬UbjtH±GíOH&đ:«†@W£b}¬¤X6™˜*,"˜¦Kd©°†€i>Đä))NQl_40j¦ -À–§±Æ‚^æV˜‘ËYS(—x K0’ «.'–)¥œ/!O*¼₫–ôTw U3,PÜJ,Kf'ªÎV•ªA»Rå#†ëbZ*™ÜyU„{0kÅä‰ø,2EVT5ê’¥JÁCÊƠ/bM1!MQè/:öQÎZ]-óQTêj%:b -H ÇS»Rđ„yVY%²ARÉ/4 ÜGmÛwå®ÏÉÎk´Ñ£tå¤'–Q́8¼SEgmöo´F Đ–q’° T£S“!’¬ä–¹·„Hd+{'́)öØ­‚ËÍŒÇfM!‰¯l,$ËZf=†SY“é,ÖÀ¢v†!°\dɶ\~¼AS ­ƯơTFơ=ѧø Ô °’Éá5Ÿ ÀñƠể¬‚¬µDö—̣yAˆs_+j·A5™½‘+Óu9‹ơX0ͯ_+¯ơÀ´cÖ›*Ó4+橶0.q®)"êÂj¤QA‰¤¾î…Øu!Ï̃<ÄíG“lÄL°5+$˦’*aRk¦fQ¿0gQY1¢€«,>B;%Àe&PlUˆ­« -PåôˆQƯ2P‰ AN—ê¸F$’qŒ‰ÇÆ”>\be—3ˆY@©w$ưĂQơ ¤+Ê¿•Œ2#µe¨ââA–X•˜.&dåè™èâP›„H à¼$ØÍèëøS+†ä%eÖ`•§ñ^x¶ÍƒöpÑ‘×Lƒ”üJ|+Ơ-E2É*JÎZI“ÄÅNqŒơóŸ_ÓÑ’s‡0P6ˆÙ̀¾°rNˆt„”rÛ!¢s/,ÍJȹ’›Ñ«O'Ÿ”7qÂÜ LBTtîµ’Hƒ -l7VQñK"Y“™ÎGÈבÊœsơ¦1¢K‚cE€:McÇ%29đ’IÔè&ƯKvt y½Ó̉®p3k/©[»µ½HÈŸÎĂʈÓÙöj—l5Äáu¶o–Û1=û®Ë,TßÄy§Ư·75)2ĂÄ÷­Gl–U,×׶Kƒè"ĐVi–²¿}\÷ûµWi¯j‡¶OË”ăĐíÖ.Y·q|†í[ħͽmœâPÛ1Ѹ7{±0³*“ô˜7ĂEÖw¼d2ÚêüÛ†9¼~Å̀ăÚKêÖnm/Jܶ‹8^í’mÑ8¼¦a÷Íô´ùwsÙYSתÜ3ŸÏ&÷RÏÄ!ÂÔ©:¦¹è ¨YL_‚˜3º¯Ôë³đR†âCˆî¥Cçí}ª-;•̀vVú 2g2…Œ>ŸGI£ü®Lx̉êĂ›:9ûzÿÅ©áyA±»Ôf{̃¸q}08Ü{42¿c6!cù!B" –Á7‰ÑÔ$Â_€’€ơ„ôqô‰¬1~4‚úèX3ErAcL‡Êziû4ªÏ™«›ƯW5ÎPÂ~ó*µ4öQháåR¶2\ÇVäñ6eKYO×Ytä=Ă“ßs†—ItOH¾Lo@çÆ̀çE¥ÓWLu/º/ª€:üH¢VÓ®{…üaýB!Ô"·ƒ–é‰ñÖ̉›ôuMƠ{M4 $…`p¡z%œ#Ô đ ܲRl³ U^~ #1̃Œ’!¼›dA·KÛ1˜ë^v D5:ÿYh•+ $Ôg[ú¦Îñœ³XỢ³íQưªE[ÇËàêq -£¬a>â -PƯ -JÑ[ -umft6ær -:Ô|ª̃’Å86ylŸ×:ŸWˆ=íåßä1‰Û2¤r²̀ @JbÏZth£ơà·x‰VÊèî&½x̉q1]ùÏ!zcÍ¢§%ñx €&¸–°Kù́:±₫£ÑPƠ’0dˆ›Ä›Ă-WxáÓhẳfJ˜3¬½Ä́H,—n*ï5´ª¥…‡VN)ff0™9Íká5Øåññ,ÆL¤`ÆĂ ₫ZA2É$·ÜeÇB4^¤«›Ơv‰]a¥04̀éjWÈ3‹¤:cPÄN/½E…`É-ăƒÊ“{Y›úÄ-RÀœ)DÄçPD -ö¼†¶¢]MĂ$ú5ØœB¸Œ¶nÅâ²2sós;æ£ÄJW&\××¹ÉêƱVw#èw¥ơ`~Wº«äqåæ¢iĂg.B+¹è*o'áœ(ö=Đ̀Xk-lÍĂ×pˆ₫üÎo«tùuE'£Ép:¶'R)I+l˜âpê“.qm’đ7{̉…ˆ:¯Ăt$S ‡b6:Ç™a5Ả}à HSƒåHi€×*PGÏl×¼ơ÷Æ:˜DÎl ÉơŒby3H×v†¯"—v%¹¶‹i,’ï_!@‡Ä[RS“ä̀1àD´œă 9„`BLRÙ(KyÈ îŒ¼j+™¸™IË Nz æ£ ‘‘ă PQSYÁ§A6£ØƯ©”&QY/̉ËUeÑ$ #ó$7:Jơ>̀a9y‰Â8/b?¯¼Ôwuá ¦w3u"Lb|^x¢BQ€âÅ•£¬ù –Ká–#îm¥4î( ̣/,@&₫•̉qˆ§@̃¨\º`‚6Ôé¤ ³đ8LwyneŒ8½ ¯)Ä„udw ôeS©D‹d²ñLö¨ -zC²2XM ñYcÖßÈru Ï@èµUå&¥q%˜ a±zC}…̉²Xÿ -Ÿ"N -g˜jªñă÷Qa ¹éj‹µøZäVbî"WrÚæ¤.*cúMà…ÉQơ'ëB¼ˆ†½dä~B˜jĂ®¦®™É´® \ƒá1ưLêy"́yrj&ơ‹íÏô!¡K{³¡«"ï$è„& .?\I,MlXNÏG ‹ åĐÀ Z(øNÆ¡Ö /Jº«â§9QHáƯŒ®Y|(¡•j ˜ÊḌ˜êD2MxñDü#Lê!vjÚ< ‘¶Äâ8LÊ“VV -ê.M¢“Û©¹~JÉ+”…ßæÄ₫ ¢R+aeBnR+Ç>Ap8^†â7zYIJ‹Ï²Íö¸í™%”)uNh{)÷ #̃Á͘¥”y `i€N?’M‰́OT¨IO•Ó)ỵ̈L»Û¤̀•­Ü²=ƠVùgµ€½_0¹GØÖÄPBÀ’ǘëé,+„©¥â,5ÉUjdv¯ÙM"Q˜“Û‹ơ^%p.©êS 22₫!û˜Ú$’’jå{T ·U¹GÙƒ: -ÀuVZ´d¾KÑ¥^;cÊÀc•Ç“ª†0Ö†Ù’‰(0ªP§#3f°©P/Äs$O]BàƠ´PYz-I"ñ’R,ª£™Z]Ê‹K%O {‘tÎÈ'pZ°Y¬4 x”ˆûÖà-@a’“>‰ ̀DƠ«ÅU­ÔUf-øLT¨(‹QÜ„µÈ>ÈNmU¢ĐĐ °øÓZC²¾ -w&È2'É„‘EƒWb"‘é Ï‹ơ€²HRߌf[[Z¡FÈ"ÁyC̃03©P-¿ôƠ¬0°mâNU¦ïP>úY©Đ9˜JKN­f4N)ËR+£óÀY¿yZ)ú+Û:BxΟtY#èÎJi‰Z-Ø8v́YdŸ”BL,®„…4nkO3³‘AF~ V3VéŒZDºf¶”2Ï Ù#ŒœªA³UM‚Np%K[&µâ ˆiëEŒôÄ¿J‹¦Ü‘•¯ÄÊ•Dz{$!7~xíœF¦Já!/$À>ˆö—ç–GÎeEçÆ ¾iqgç$L”! Ä’— ÙÊT]”ôCÜ\¨4’U1#lH«6"dn­¼qùwZø½RI‹+ùj›ö“u7™¿j`¡vy…lUJ¢›DhÔHÆq¦IÎJËOÍâU¦Kß™Ûiöë^µ´l¨B:$ -Ëv -Rgit"É5̃L¼2sƒ&…¡R•êC˜Éw¶­Ggع©üîó2?3®r9¹mÁÄD|ƒJ ¦HÎ,DEû› ®çd̉MÁ¼àDüU&, @¿…ΤÀ‚èDØ#j¸Øà”¸ƒ́}¸(2ĂGÅ7ÉkSz3‰Z¹’|¡=J–sr7RpP3AF¬È›ˆ÷gÍ›œw½æ꼈WW±¤đU<ö -–ä¨v2gaÚ/̣hWÍ­OE=QZ 3\qnç¾¼e!›J¢3è^€99{”.I§• jK¿ .à¤XÁ—Iáæ>• e2 -Íà©@¢QA|5:9œ)ÑT˜jó¦-¾xwj²\®#̃LØ/*±çAWFéB¥tJ,y‹l¸ü×ĐîJ)8_•Bñ8a°É̃–å:«‘åN¨T{‰8µ•ô°ÔjƠ9µ…ØP˜}êL‡ˆ},̣1ầÔø1lÑzH®đ¨ Û”‘7¾@+TÅVIs©̣¥‡1ơ£ät¥Ă6R‰¦ƒăfë)Cô®ƒVKÆ«©ˆªrŒĂÈ„¸åW‹6–^¯+“Ú6 àKǾ^U@¤P:gAe~Rl€dUæ -¨ô@œÈ8vi†€â­ñ GK¦D™Œg K‡iâ[Ù(ă…(¥®"Q «ÁÚøqnb¥Œg'f#}Ëb¼¬2½¦ Y·̉“¯àŸ’Æ,¸<¾¦˜ƒ[JàtÅ™|T.ăœSÊ/ZÄ8Y)Gé„p…Lư.gî³ÂLÄnr5Î “*=/¯D'Çoĸl2l’IOP 9Ù¨±I³fßEâ•¿À‚e™å†ÎXû°₫éØΛ³(SV6áµ²ƒ†¦í€!“ÔBºyœ) œˆ‘ÑE(‰2#Ä%^‰5ù@$( ÍŒ‚„°Ë³#ư¯UWâUôX²^¤ÀôïF-6`Ö6̃ÂÜF¾6§ ?tªiÀzV^"€UăĂ,—ÙÓ¨ -reÔëaÄ%IJͷǔ/Íw"m+Û-ó”Ï"à€+r‘‘’ç²,©‚ùt¥¬/ó¥åÙzcDá~ª ©‡ELcè,…ÍrqÑù̃*ƒU—Q₫̉ÁÂí”›s|̃ÊKÉ­R Ǩ,$gÄ~0?Ê¢Yt@±`*MÜ-{¶Û«TXtâÉ¢lâYJ3&n³Ù̃œÜÀ¥I -×A>̃2na‰³vB$’Lb°}ä*Ư4’́g€oP”LÑI¹O•5PF$‡:-ç5„Ị̂çM¼ÊÂü5¬ßÆüq“PuƠjJñ`eÁ7É8ö%ø¦X7^&‚µ†o•úaYÈƯIL›2kœH@¢áVFùh(ćXÇæˆQÅMâ4’‹Ty{{¬âó$}€Tf WWa—.»Æ’ÂáP´’ø…-’™¿›ƠKD.h ĐÈ ïjEQÅ̉̀AJê(‹̃®¼2sBsJÅIhÂ'Ré¯ Äü ·*ÉWy>KЧ¤#Nƺ5NqT‰Ă¢Xµ³ª@E6±Ḷø‹ª @M¥;Ïe3Q¥²¸2é|°äÑ©\9Åbù`ilœ;ı2̀ |êY¥­2'1‘½tÆơʨ4Jn¬æÄÀ²«M)¦fF%Ÿ!Sa¼Ü×Â!*–nÔĐ JsẺ*OùƠ«hmÔB¶Ç†zƯÑÇûn@Ú±j¯µ^@ûG8‚5§\!F Ïø¬ÈŨRÉm6ЉŸ×‰eÆ̉₫,™Í2L§ …#ĂU¼Re®q́bµ¯·ñ>rƠªñ₫”FåuB¤5©!%I¿JeNæí{a À´–»†OKÄMÇ8ˆ“=DgèßÉD˜¤.•ª‹Œs ^ ĂV¼H#01˜ˆçOZƒƯ ù¶x@¡du47¢NˆY®áª<7,˜œỲ ÿÔB$46`æÆ&Ë䤨|D3²›vt2íÉ‹ÀB&~WYÙ̀,\ ³NAWƒjÎ̀7́Ô›̣ŒqƒÚ Vàį}IxfFpÂû̀˜æ9UÑÇ*Ă/^!ÔHX¸F`7ââéœS‘v©Ü₫2_”“]sŸ¦6 ‹ăú›G‘NWcYGº KP-êÅ¢ QE ¿=̣7å89|-‹N?(A>ô{Œm€‚Ă52 Đ'ÁZYë¸ú\đĐđJ“Ÿ(|ˆ&ïe đSÖÿ ªñØĂ îù Ú”NNb€Éebó"ÚeQkñG·˜=Èxv-P¼W¥t W ‡•ơ/ơ ¶8Î"içq唦¸¶ -°UA±«:gB×q :#tóˆµîÚ8ÓÇöĐÊh:BB ¯V­z߀V¹q•ºê[Á^&zë„©)V€æ -¼p”^´>(â ÿD!CQ1S*9(WN”¾—zÛ…UbԜĵ ïnõ‘œ¬Ju%5?.đŒĂ -¬ƒ2gÁDˆư‡ăÄ,FhHcÖTeÁ̀tÂû6‚Í ăAÅk̀#ñ‘Ŧ¬áöp½1ó£ăBô©9‰‘H -8hAiFy8«iâiA¯nÇ™Èä¤`pÎ Bº§QÔøR…-ŸI‹…E­°rà@ÇêOå$ñ‰Đô4˧Hîó–Ÿ:QJ:4¸¹4 ŚAîz¥xyc††OYÆ^öEăô(ë†k^¦*4ăÊ}¨ê9€1CØ̉£o·¨#­§—.i†&#l°PàªÅ Àx:ă2I®µ*:1¸¬ÉªœvØ|° 0‰)̣0KȆđ{æP̉i¨‚Oí]¡Ç¥’¦ÄY¢åc. (ƒAO)˜±Ẻ“æôj̀LD„ªG·£. F¨ ¸i*Öªµn²ª´a’îúWâ6 åuU103`¦Ù¤>¾BXcŒ¯‚ơjyÔNEœBàî±ü -̉̀ä‰jsoC>kMÍZ '›L„8T[#AƒOˆod‹>•÷uïĂ ̉ÖW/ ÿ'ó*Ö,ZÙ…7P°²UĂ+@É@>päơ(-C—ªg)k¯Î́¢Ó¨#{¥g´e8£í– ƹëv£fă\‡̉(D̉`ÖBXĂ ‰ư1]$-ë¶2t7‘u@¤%́+¥ˆ'1|q_60ù…-aoÇô<4)\|đ+`3] Q^ €XG›̃Må°1áÙâ“F¤„z@Ưºg½ÉÄ…[µ(<üoHÀú;À&,ªđß`ïQE¦R. 9×1[·0«¶*µëç4†`$‰Ư#%gBf¼"kY6Y¡*H"&ñ®\ˆYX˜Y‰1¬Ó"†ÎáOĉB̀aà¿×²úhef̉œ3äV‚¯@wÙl4Û´EƯÓÀUX– $¬Ô¾n“ hĐ>—”~¥²1uQ™/đ#¼§ó)ˆ4*,Ö|•‰%¸0ÚÖB".-X’ uà3ä† Ù(øNY—Ȇôú,Ü{mv„£X¾+ iéü”Ç=ƒJv_YÙ©ĐĂj%PEÈèÊéÇ5ïM¥‚ FkHMzŸç&43e°1)*BkKACđ!Ṣ¼Ên½iµŒ‚¤ĂóµÄ4ª"PH}qÀ{o‰3FÜṆ̃œ·Q 2aĐ¡“ËÈÀà9Q‘µ˃®+¨)•µJd•ÓăI&€Q3¶†€$eiºÜ\~B´‚ª́¥·³̃„jF¡Ø[$Fu7¦¢È,í©*”n0ç,¥÷µBÄ&̀w2Ë‹ËP6I“St{†t̀đÆJå9CÏ¡̀ÆQÆ#Ê*y†c®4½Âæ3†kÀ(ª”ùN•0œb'H²̣*®tt¦e,!b‚áeES”[PÚỸ\ Y7̃Ue Íû¦lÄG5´+•aBÄ„›:F¥} †îc=V »sÀLuR}¢đœtœ\Ø• Åÿ5;‚&ç¨.èùƠ)ŸÖ îË•JX"Í´óÂrG¾_Đ€abnb·ÁO æ4Â×heËñ½‘ª!$˜–ÎlSÄ\)vÁX*®*3#\ˆäĐ›̀¹–ÏẸ¹̣„[ iAæè ·b{–pB?"Wv»ÊWó;¼‚†WY„Yå«x>ÉB1;¦Ît¢Ư¥â¹çT;œ:”`J&¨°öi¾ Â0ÿ¥ïnqªJ°@ôîq³*E §À bôn&CïNđÆïd233¤̉«ÎgràC>!êq¥G38p™î]³[«Jj.6}XuDI¤6–BA`)‘xø -™±„ú¬Ó6Qy¬•å©2UË0†+"́æÖÈçâ– -îQỄ\ÆSTƯbđRƠ@••¯*KcÄäVµkÅܾ°x©y Wo¥Z.eñŒJD³”ø-cI•(Đ­½Ä…Ê° ú°)pJĂ™X—‘.¦PPPêl7¦öĂ°2öùH‚"Ze¨)ñeṬX–ËT:œq5̣́ªĂD£ØÔö*²t¸2;°ôªv!‹ –{¦©«–¤ç‰n*c„Ei*‰)̣¹!=Ô¦9DW¸ék‰J:;á2r yZ) ÚФ0M*`àAˆctxù\U«È B̃dU™Ó¾iOư«‘lcżzĂ~̉î²,Hoaid\A—«,ªk2LÊĐÇ~/D.̃H$iyƒQÀá…Œ;o¹Æq~­o;ăè̀­£>AiƯ‰ôXT/ ơp>4$|9Á…áó`Ùâ…n 5‚‚3W «Ô÷F½Á*­£Ê“ÉH'i«œ :ị́Ÿv…™4È…5 s(#¢9d Ü’0ÅC"Œí¢Ñ¢]Ûm%3¸¢J¥˜q`Åa(%y¼Å,̉\ѪªVÔAM–¬ œ{„»æ?´Ñ -‡9Xgb:HV–OeuI -|R‹÷ Ë -(.q{cÁ«Y† L3X` 6†ß14ˆ3Ÿ¨H+ Œ‡Vmj©j6*–¸+Æ¢2^¦)HSG†+lƒX3BGr¤…¦À:B²Ef ¶2Z´¸@Qjû˜°ă )°{`ed ׳#'ư:˜bjJV÷íónNNÔŸ`:5ejyJ¤2vªI¡• „8áK̀àWz_fé¾¹q7Z“|F¤MüØ1"Á_¹&®ŸèkÔ©e{̀ñ¦g”˜rđYÁ®Ai¾ü½¢^Ư<¨ …U¦[j_ÂV™¡Dx~fÈ3ư€ä0$k꘷@l_V±×VùN.–éåôad=AƒYAÜ6Æ·ÌzÈEÓ‹¤̀uôxߘê1¢*ũĐut.BC”ø3U…),»¸…¢¯6Â¥$ÈảƒŒ—Àaŕyy(gS£'q*ĐñŸS£D)YùÏ”<¯F*¯!_ Y°Jău¬TáCr‡ÂØt­Áׄ-Ƕ@Ѥ¤ÊO/÷#P_«ç àb鳋4×™°0ø®^|kuôbn'ç&p•a`O&ÚpÔ½¢ÖJ}*׋eœ3Ÿb!5̀ÔXâœÂuƒ°±o±FTÿ”SHƾ—ëQ"•zX–5 à̀°®QqWV Ơ0+›¬U¨>¼¢Ô”¶ÖÄÆ0EÙ´§rEÈkó!ÂâÓÅ;ƒ ]‘SyŒUÊyfb$• -xn'¦E ”ëDơ Œ<­WyËÿ¢ÔKècL÷«)$„μt„‘cN.œd¬hĆÜBbV€Î5¸Íó:’!æă`,#Œ/ -7G̀Ï1&w×$çÙ6e6-!8Ÿ‘EĂ0¾Ơ…r7ÂÉ‹öktæUgÈ€J“yéiIǶ°`—Ăc#8ñY¹,nïåFU™V&:ºF -¦©‚›Ä'ƒO!¡tNTƯ­n1Q8tAä x‰à?C9Ëb.ü Â\OuV2°üÁ2è¹ÅµïWú‚̃$F¿à6¢×r0z^̉JÉr¼%GµøJ”ª,íÄêTăØÔÄZ ûp„]̀31ÏTNôP¨“$hB%JæRzWÇ̀/H Äǿ‰'´á\Ă -ˆ ĂT¨8q`¥öôEĂ₫cä˜p²Î ¯ 0ZjgpÜ×–hd;¨è‹(@¤wÑsw8NíöYđ¬Cª|-ĐÁ -)ßĂk€¨‰|bBAÍh¶à—¡…ÈÂ&¡T-:9>S%‹1G˜;ô*ÑWçåBÛ`â•̉r”pÂ1à´"h“Ê>ëÂ<@x½Ë¼E-à%&Áp•hlɪLwB•Äbˆ©ÈEù­:Ê ; 0X-Èp×ôÂÛÉk,1ª"`L$Æ“G)¦XÂđi” 0FˆÎÍMø7èöæ×ả|)‰-›HŸ­ïDÂSöĂLU¢;¤Æ£™M$G@Ôc&z3¬*bipiyp0V ßJJđ\oI€=p è6j'ÂÈO `̃à(T¥(²÷ôÎ3é$—Wn…ñ̉42¹0? -éÂImÀiĂô“FD-éĐuæj ơE© fùáăj’Úœ¾Ê -¬Rà­Œ¥z%{‡fäi¢C"‚OđD©•<ñ" Ä]Á,W̃Ø袦Å%ÔV¸´`8pg»Pbœ¡†Ê«̀ÎDp›U¬ơ$;k.ÍÊx("<¢à¡q0"­‘yDƒˆu‹ùÙ±s0RrCă5È>½¾È#3ôdoKª…ˆC\ÁüM@ësÚĐDQE„á>V|aX€­Ôóöí£œ5á×$̃́If -I#OÅ}Kgyê„­!2ñ:ߦ$RÜ'U@ILlô:{LÎÙ™Ă½wFRFX,ó£î鳂˜Üæ€ùlôÚF - ù!XÎoZ‚R®Tpˆ9¥˜³lZ -:è’´•ƒQđCâÚJY“¶§sŒ`9ׂ`¦GÛ{g§Æc5Ä2œ²qʵxïăU”sÄ©6 v”¢3A>¦àĐm -À÷t:{Ÿê¢sm8:XzLđgœ•Pâ¦0ÆX¬X]T̉Ă1§+UAûæàJ^µÑ”'d ¿VM)å½—>̉ÔáØ8K¼ m˜̃µª¡…*j"¶†JפMë d/\!²Çá“å)Gù‘êµV*À½' ~Ç´‘AoTPˆá9&6fBÄ} \ÏâIÔNÖ₫L±/à½Ï‚rx j… eÖƹ;‡±‡~}7×j *zJQo%ṆøđÜ•öQ€ăZÓ£ÎådÊ39d›o‰™G -q’Q$rGÂco-ª&W„ˆç–|Nç"f„́ˆ/Ñd@ª.¤?ä~’:gÙ ±íâ ‚<8Ó° Ë"ÈÄÂk´†¬,æi¡‡ưîÈÂ₫ưâ₫hÿa8O§áH]Y±ëî₫̣Î?îđ·J¿=Ü= >Øúq8˜¾ó‹×·G[Ẵñáäuï^¿ÿj8î=oÇ—Nú±§_×ú»»;¯Æưƒ×;kúx4Ú½̉K{ÓỠƒŸúáïG›^ê­àƯco˜i±©±¾ÛŸxƒ½ ©±¬‡G£Ăưí®gø£w„¯û`ÿaúz¦ùĂá₫`g··̉{üzg?6Ơ3ß°öº?ŒÚiYÜêF_ÏØßcËĐh:ï/hư`¼ßßîOû»½#|̉+—z«¶NÂÚ]%À[2™XXíÂÁ¿‚´Ààˆè1 ½gèØÇ÷Wö5ëD}[³W÷ê¦çäî™Ë§í:₫·óŸ?¤ób^¶æÿÿñ=·=;öº‚$T}m÷uîççÜ÷üÙçü÷̃‹¶7†w£­­r¥÷ơ>6đ¶µyÁÿd¿:˜édw÷9ØgÀÅëi˜DqÅMÿû›¾î²ñF±µtĽ¼3Æq¥Ço4“?rX3âw~léâv<¿÷RX&¿1́#|ù<ûE‹£µ2¦q°‚ 3‘Y¬dÖ‡…Tjfΰ€~Í̀Ôú-|ÈÍé/»ĂÉ;ÿ¸̣å₫èç}₫ ỂÅëû£ư^Pk/ơ®Üo7•ëAúiØ´¹²6Ú;À_ßÙ sÉÛÂÊÜÙ爫._â;\±6—ÁqåÉÎdgkW]đÍiđæL¹ÑŸ́ :O8frR—>©ƠVû;áå5Ư¡ù0NèáçÇ;SƯ˜̣™»Æ›ÓñèFƠƠ?Ă4¦†¬§müñhVØÅ̓₫ |›K6»8èïÆ‹Wn‡¯ưhø2gZô'-/}±¯'Ă[? ÷lok¶ÑM;קeCIÎfó åÓuü ßØîoÿ~o|…Ï;û«u^©}±ïî¹ơïáàcÑ|ÄR!Z¼¢o…èŸ!D+ Ö§$Ë&r,-r&*¤·¢ô­(ư‹ˆ̣̉­(}+Jÿ Qê°”`₫ÎDpKE)˜AßJÓ·̉ô¯+Mư[iúV₫m¤i -zí·ơW -ÔÇÏû›³hùVè₫J¡[ư=„îï2’ùlâó 8³ä="p~ƒ&1/FN')₫ +9ưCṾ#ư₫…ô?¨“₫/®£?ưÿd‘³?y=Ä+Bü=ä₫ÿÅögK¿^öಹp‚aµÿ9›RüGë½ÿç7°¦z›¯ûÛ£Ÿÿ[ÿÿ„¶ö?a×)P¹÷áÚŸǹÉq1³ẁt.wÍ̀n¨Æ9sü4V+f»?~Ă*â•­ƯĂqt^h®ú;º’Å6? ÇÓÇ`2È©°ZÆF£ƒ₫`ö¶×£ñfnÛÚƯ_úưøÜo₫l“Áîø¯¥_₫ÉøöîßUô₫i¢Ä>ÚA‚ü1†¡$σ—/'Ă)’ñ^Ÿ$yîîîN¦ă₫t4^₫{¸X̣̀<ï´’'²`owgó6—F/'’*©‰‡÷§gØ»̃ú8뢸±Ơ®ª¯ñÂcrä«q{g̃¾*/ơ¢tö¿úóÆ+Ü}ăÆoVñµ.ÖÿóŸ_z÷ú“7¿Ë²X†¹?ơ’h£GáƠ'¿ö|}«™½µ<9÷6_ïŒG/¯w₫§ßÿạ̈₫ßñƒ₫ù¡¤%âơ&½µÑx8ü.~•óM¤w|&1ÿ¿®¹£—«#ZQ̣7ÓVYÈ‘¤f…ÿ÷ đÿ{+'_-₫₫Ë&ơX5ÿ%›ëïå¨IW«ê¾´] -·&9ê­É#îÎĂ&ù«8l̃jÇŒvü,\ïîN¿ë*Ç›;{»­r|Ộ¨ứgL·Ï₫0ˆăé̀ÀXˆwk»S†wr=ßĂ₫îp:j4·Ô₫sñÙ£đ;Åo˜ïđƠ¾àug/¿ó‡ƒ™W»øÍëépæ:Wv˜÷ Ç7×e™ăúÚ½§_ö ·;·̀üôt¸ ¿›~uó¿̃‡û“îk¿ôÛßǗo7v‡Ñ̀üv¯ÿ -ơñçU•Kª®ê]Ö®…K÷®¡úøéµ:é}₫ÚŸ «">ñZÖh®}y­ÛJn=Û°â#çí[³UVè~¶Ê°ç{×æ{æAÑ¡!̃¦y—¶]Úîkib­æ:^-fËùÙv¾X̉/°ÊÖLw¥én·ÓÍÉÎlĂ_Ô¾æ›7S™Í Â₫gn~v’*ö·¦i•Äaä³mqèî€=”-1ơ ̃­ÛoÆÏÙmèâ %mçxó{xŸ§×™¦hœú£ÛTË3Eă´èN€ë´]ÈóµÛ¬H–4Ë´7²¢óµ²ö™Y1ûµ4ÚîK5M‹ùỜ Hg·̃>[¤E|ÿ¼]ªAÆùlăÜ>èªM®çxv^ÄAƯ•Íÿ–ƯÙ-X&³s[Ķ¥†cí1˜’í1ùÜĐskßz̃´î̀´ÅÏLu3?7ÓYKƠ{ÖŒ¥{†6Ồ̀”E Ùl{o_³ÓÖ·›´˜‘Ü#ÿ·3¾­áâM{7vö‡ưqđæƠu';uÜÓ>rÎk iPÔÎôø£÷;âôWŒ8=ưˆOóø£÷èñ¿éÿ4œDØ‚K3Ñuù]ïàÈ-öœ‡£Ư7ữÍÑôlÏ:z[çÓZéa\ßÉ‘#¬këØ!æœbI\Ơ¡_Ä3ÙÙd³grO³Ù3™;KnÛ–ÜŒGÛê¡îè3]rä™nVè—͉sôpJ™§V±÷Ù§öÂj¸ÉĐÂI4fưPwÄQ¸́R»^uÆö™]÷aëµ]/»»]—: ́vÙ‘€¾Ûe瘮º]¦Ó¾ÛeW (:ωBêáVnƯ³aYl]wFÉqà#éxs†ê¬ßü¨v®ÿJÀ —èG!áQÿöá°wcgLŒœµƯÑ!ÿTë̃z±pGئưg “ [&˜KG`̀́áÇ Ôf́æhpˆ:›ưiŸiñ‚Đ̉f́:^øö̃ÆưÑöpÉÏW{ÿ½·»¬ 1̃Ù:œ'†?zåúxÜÿ¯>æwê¥Ó.¬Ưíñ0BªÆÀmüL9ÚÏÏïO^üÔO®vªfºmêñÆøa²¤!b¥Ƶ[^₫_ªưÑ₫đ4³„ª`vfbÓˉÜÚÙßv§yÉ°f6‡Óû|›S¼h·ùï¹(~û¤¸ă'åtS±ÓßÚjgœæKÿé á́BáăŸN/Đö¯±àñƒĂÉt´÷èưx̉‡ăgh؇§_©ÿ}ôWÏÿÖV¼üù/°¾ÿ’|²»3øßă®đBq¶ĐÑ̉×~=ÜyơúT{°iú—ß™3¾“^đçmFqO~?kù×x=07Tú¤×ûåT¯öË_äµ@ßs̉ưûtRè¯̣F1Ô»ô}¶FÓ dl _NŒw^Í@,½£7ưeô -ÊÍÑáx0¼|¿†Bι¿Ä8ö†Ó₫vĐ´~‡ÁÔ¿y0ïm›»äTk®Ó—d‡»Ăá6Öâ“ÎĂ¼ñèàúxØWê悵x±ơÙ‚ ÿG̣6ûk^‹v!ˆ̣œzy•_â}yOR0Ï|Ѿ@mÓ/zƯÎoÿ¬)9èoo·#×–Ưc’ớ6Œ¦óíú»;q“eI|ïíƒU»XÄD‘åJp€×ïö®NG½GưÉt8̃ùÏp₫±×ïÖ½ƒ₫ÁpÜ›́́îvÉp¤’Ф?núăí̃ (±A †Ø(zÅÖúû?ơ'›m_ŸƯnïîơ '£ƯC‹º7káú]çzK-辇ÄŸ÷è‡È‚êîeª’eCë¹̃Ö̀úˆ¯:‰K«÷f4x3:œö^G‡Ç·Ư cîO‡á©C:'Ư¦Y™Ľáäu3ÉœÄÎÛÆ/”æiµ|àioÜxbNlúÙåĐåÁáô ¼Ú ƒI;OŸûû“ƒ~Øôƒ_“w¶ĂZh¾̃‰CéL÷‰Ÿ¦;́·Óáf> —Äø§aïñđßÓ̃­íikgwgúËœPX´°6úû¯û¯†½‡£ƒæË7êÀíƯÑV÷Ñđàpw̉NƠQ½zöÛî̃éOCGw™s÷ædF,i»1ôw±ªgZw[<†)ˆ®ă6 -Êf²tvΛëw×wwă+>1êđë‚iKz/›=V₫îÎ₫°7¡rBëƒ8Ư£Ÿ†ăxÈăyçëÙŸ¾:́ăkô6†? wl®EC™†OiM}»(f¹ W~¦ay„ïÔßQ€æî́î„ơÏĂ¿Ăzz¦g̉¨s°<‚U1w²t~~<:è₫%cøûăđĂÀ&ß¼̃¼~8½ÜÙ~9œ_Œ3Moím ·4uí>i½y¸Dàú(Lü#́æÎñµgÚ?¼¹4×Đtf[,nŒ–áw–û́›ß Ø}ù«ø½M\›ÛGÛf¾ưfÿ§á½ĂƯéNø ×m¿/Zi/\lmlÎ,ÓdY³ÇíjO.k2¿rgeư­‡›'<ÅÚ̀?æoÎ'\iơä+?¶Vƒ„Ă™2×Ï|+vƠß[¨Ư÷G‚'¶“8y³s°¶Í›KÇo6îx2¤ÿéÆØŸû˜ƒ˜úzgô»e£·tG~µñÈüb´uwÿå¨×‚®«·́Z™ÔÛÙçFMv¦‹¤×ÁÄÁ‰ƒG3âàd%…O8N1é -.6Ö|å‰$ÖY‰ƠƠÔ¼Éeèïo›B1jÏơº y€¾ă]<Üu ëû¸—sGv̀1“y´ñâ½<; jyYèN´n:n¦gçLíO1i¿foŒWåÎ?f۠Ѷ‚îQÿ‹{Ôq˜ó­»>¡*w«eVæËÚÎøÇ.̃¾ oºƯÛú¥ws,£ññÛè˜cÉ¢Nó#Z̃jf,‹æ‹­¨}^l̀ïè#wÇ«{áöc7̃^_­ÿÖ(Æ:§ó|³IPå†ÍÓNløÓqĂg«ƒƯÁ/Ǭ 5́OÛĐhº³ÛZ, ́«½7«“&eü˜F[PNj4æđ1ă~¹?]Ữư: M‚Æß-[ÔÙdu·£\.Üe“Ơ­l̃ăƼL§Üi:ă¹Ơ:Ÿö47…Ï7EucÎj‚;̉6́˜ -s*æâáẶƯ &âIíÆ̉®•¸·úă#¢Å:QØc]IrÖ­9Eăñ¬ -}Ró R.m¿×¿™̀ü­Û‘Ÿ¢qwä§h>3̣…Ừ‹N†7Gº¨ÛƯR9Æ/GûÇJ*y ₫<¿yÔ‘ ‹wÂâ×=ể:₫»À9Æ0†p¯w·ĂÙy¹Ó¬³̣ ³öåÎ₫öñ“}ÊÈC~́‹ßí™ï³VñÁm„F̃½“WlÚ™ˆ̀ÂĂÑîNëœ(I§ ˆmíltÔơcÆ«OPÜ₫ÔB,xÓ™q/´¥ÏnZ,ï»_pđ: |x̀>èÈ#D9Ă—[ë(Ö¹3ÅæƯœö÷·ûăγœëÅ´́³¼†¥ñËưă]z'gœü nt ‡ă—ÎéVƒ bºº0«;)í§̃hQ@R9ŒÊÆơñ Ư ©£ü:Ưè4Ót‘»k{‰?¿ ¿é†NÔS¾̉q£ -K­»́Og̉è:}§ ›¯¸Î½Ä™₫ECgKB -§Oú:i9-µΦ©Ư8É”?m>̉RùeƯƯưA8sç5³ ¶ƠF̣S}™ÅëçL{îˆrÚZ½¡‘´óµÆ²<¥ùºhM†5»;™®†ÿÉVw&³¦øÑ)j['«ưƯé`wœ6Ëñí³Ơ`pëëmÛº3$³‘ä§kî¬y?K˜äAøX=a4-u{;)ú}t‘­º… ¯½#] æç¤ƯT'Íjz†YMWç”®̀¡»¸aNü/ÇĂÎêg§ùjÙÙÆ“r%û`aú²/…@'p¸(¾1ÿüṇÓI¸ù‰®îxæ;8₫…“³¾p6?“>°;̣)¦Ưv¶7HçßथGt̉É¢5z¬@<㤦ó“ºè¸˜₫é§ÈÍoàăD;î“f₫,̉6“ûø$̃ÜÉû́ổ3›A'Mù²O)9“3HÎ́ ŸÍcyëTOù5Ị̈‰Üщ;Í<Ç/êVưI7̀R͈Đ' Ẳ8ºo>3ơ«₫„ÔÍíx1ëævÉif5ŸkĂhvROúd³;à”ƒ?¥"àæVëI²mv,ÇŸYYw9¯¤Ư¦1uwÿMAsÙȃªçCPưư&?íêu½k:‡/„̃<„ÄÔø#x ÂƯK0®o®Ư½[7‡°<øä>-~đ}ödëJrå£{}ôÙëi†¿¥ù'_}œ5?|Ơü?\Í>{<½qóe}ûÍ÷]ëß|™<ư´ù5ưèÚ£̣ơ¹‹+ưKçV®\ø2tsî£Áês ƯƠs—_ÿøèÜGÏî¿Ÿ\¹öô;,έ]ú*Ÿ¤“{a87ßäŸ=øáÓ́F•Uå?˽~²̣ĂúÈ“%Ûí¯ÉõñøÓµí›ÿ\~ûù—ơäÓĐÍZÿƠÏ܇×?Ù#³÷ÍÈ/ew®aäaœŸ¾ùäÜ¥ûÿ¬Ă°wÂ?¼\åÛ~¼yị̂½oo[Iî¥x‘‹¿ñE®m]¿|pÿ ¾Huç“oĐÍúèŸù“[ăçÿLÂK>}¼~íúµAx9¿ßy¹µ•…7ÊeŸJ¯´mÔïm²lÜñøêäqèạ̊ƯäJ¾©iÇ6©Ö§ỎïGoÎ'Ûç»~Đ>wü]2ù$<¼:üèÓÛçη¯¾zeRƯΫ]ư1üóönè&Ü₫ôfÛ1z}>₫î̃ó¯÷zÛ_||÷ûƠ…½~ÿ₫ưE½†nØq¹óåíØñ‘^?9wq»”l~<œ,îơË•ç¡›/·‡—§ _wóÅgW—özÿù/n,ëu-yráÛªíoÓéxă½ĂÍW[ÛƠÂ^Ÿl¸Ñ̉^¿¾}åöî²^qH'O²Ï¿îÆg—|ÿÍ×öúÍg[.íơÅ‹ëÓoÚ^±̉º?Ï“ï<¾´¸×û^ưøíÍO>\ØëwÓƠ¯–öºÙ{øÑùo—¼îóơäÅWÿúdq¯>¿4~v}²¶°×[?®”m¯ø6³²âÜ7÷“ ơÚwz{vó|6>üæY^?:²y¼wở÷‡{/B¯×Æs½âÛüđá£÷­ă7ơ‡s'Ïùä̃ư‚½¦>½xg¶×ƠñäƠï¡×•£‚bcơÜó÷ÊơĐëçĐÈÅç£Z½~vñÖå¹I>w}ưÁơúƯôă/ffø̉×]½sí ôze®W¨‡?|íVüÆùCÇë‡ó½w>»d½~üƠế»̃ùúÆç?a¯Ùg_ol̀¼ë{Ï&ÅÖ³‡è5áJ››äûơùw¿~u7ô‘Ơăë[û_ôAöÉâ_oT~½ñü‹¹_ă±6>́Ÿû$¹ûü£éÂÛû>uưÉĂ÷ư¾ĂúåsÜxÿ&~] â¶öC7¥_ß¡Á‘Åóî×ñl[ôë₫Ø?øîN±ä×é´rŸ₫ÓóדöîƯóç>đÍ­¯ß~wzơË»_¼7Yüë—Éw}TÎưÚèï~Y¾¸ÿù§‡–Ü₫åˇû'+ ­îÿÓų5½|Td%ï‡nztío”ç¿î?»ơé’_?¹ødí“o>_̣ë¿y¼sƒ¿.´;—_¬•Ÿ/¾ữơơ¯]Ê.-₫ơ₫£×?N^Üúhî×v̉îûă^6¹’.¹}ï_£•7Ăjñ¯O¿{X>ê¯,¹÷»Ÿ…nu~´Á‹=‰ÂqÁ¯ưoÓkï^öë‹~{}úîƒá̉I{™|÷úÂwß]xûß|µưäâ¹ûŸ-úu<¾öâ«́ó¯.^į« ¶çøZzăń¡Á¼H -{ÿÅ̃äÜwn.üơđ‡/~øÁ'ç~X̣ë5€¿|øù7öÛŸ\úä 1 -(°>­ï¼ÿ#%Yr燇kñæ7̃Î}øåUwị̂ÍGßÀö¼üäû`{¾Ø~|îâ³K‡øÛC˜©kçV¾|T¤oßxƯùéµÑ›0œÍÏÙaÛơ•{Ơ₫…`²~{Hc(ÈÙ—Ÿ4½¾weçÚ̃&¨~ï̃ -öĐ•dæuÇ瑩=\1{èưQ÷|üô½ §Á{2†¶̃ô¦ƯƯ^óKÏÙkG°Ơë»ë߯,í5Y¿Q>X̉kP÷³«/æz¥ª®Ÿ~¿èu­×Ûçå½̃¾=~ÚôÍôZƯyï_åáó.p{wv’¿ëöo¾ßéµúêóG^·ÏŸ¯íơ£«»_ߟëUªºM2ÖÜÁâ^ó§O—÷úîú«di¯´+ºº@67É0-¶–ơÚ?¦×ÛÉÇs½†nÚ©§,ízÊ×Ëfø̣qïz÷ü¼°¿ë.„Càß́cÜ?Ü>U»ï µ“%=ÓôƯ×Îâ‘ăĂo>èÈ ¼µƯñĂŸÍí`usùâG7F“ûÚáokP ïrnâü6{ÿöƒGaï]¶?>K:®s&å—uwƠĂó{́æ³5GÿÑ zÖ>=¸¸ưx^L…₫o\y=¼ù₫x¯éáĂE^±0œg7Ư‡7~\G“¬±ÖZø駷>èüÄd«ŒÏ9ơ¾ÂPÿ›Ø ·#N¾(>ø€„n°(¾éZ6¶Í;ÜüèÓ­́Ng;³ûë‡áŸçqœ~¸tḶ hX“ă‡ueç½/óM©|;|P»^4ë‡s³̃LÚ̀ÄóG]Ó}Á~æ¾Ü8é ùGÿÛ›æLY₫³Ï<¦S+;æ^¾g‹F†ÚÑ7ÔysơĂS|ÆS}ĂÛ›û‹V)¤À̉ù:æiOζ棰Y4_ÉË ÿúö·¬¯fq0iư÷/Üùͳ©¿²tê±Î8û[“³Ë;o -¢§—÷ç¥Đ­çkƯ÷?Y -Í́aο¿ån}7¹Ư<#[<‡·¿¯½´l$É°ÿâëăß‹ªú—ÎóƠ/û§—ÚƒăW¾öÍ­ç7WgV|³g„M2|¸úÿÛû®îÄ•eá÷½ÿÁ`PxÁà€ĂØcsf<3Îé̃{^¾ß₫Uu·¤–Z-ÄyÛkŸă–¨NƠ•«k*,Èó[µĐ={K\ä7²kó«Y>IbÜß}̀ wU„ĐʯVưtHh§ïF{!§/0B"R\ơ’'A ¸îø¯ĐƯä5oCk«5ú‡í—ú7ú˜̀–2Y€‘DbAûë±-5Ză÷Ku~p`Í·DYàF{ÓVNÜx¼­­”c¤3Ѷ<8¯Q$ç†??xR^Y*ÔÅ…éDØ”ç#’&¹M¾ë†Í › --$ïÜäçk4BUá¾yÖÆ[˜Ë÷4ë`„(T“H`Û -”¦…¬aIC¡•°˜½³…=1;ñÜ4¿ç]¾Í€/ú{‘4k|Ư óÅX´đÅÁÖØü>s³åB -•4’R˜ê©SÎ,ª/LêCN‰GèÍ68)xƯŒ²?ßdZ›ˆ…QB̀ D²%HǸ­”½Đ̃À¯ ó:ÚÏ~ă%| Çæ‡!¬åh·Ÿ•`¡K̀ZCÉđOœ,–’—Ä´`I—¾¤(´eÉÎeäéŒ -+›Cœ …Ă@ÀÏ@a­Đ/̀Ω,@ …™ E êƯưçˆ$JÍûÏiôCÖ䆠bØOà#Èd¤Áñ -¼FIÂ$Éâè‡äj̣‚i u çUe&†ºŒ†ºÑ½9|Éí»E3™†.9ù¡%{©[¿AhZnêêĂ₫بÀ,uK£#¡nr d8£đâ@—‰î·%¥ÿyĐ`ˆ4(„#ïÙ¾Í)‰Æ—2̃DÇ0‚dtä…™«z+O,Ex₫‡²(ª§Kl¸¥²g“FtZº̀/¦¸…h¹*0(V«ô¼¸̉{ơ¬]ÍNb0 ¥Hx’ĂáB*<áv8@R""B!Æ ä”.¯s XȈZDú¯Å0ÇŸ”¦Fú?{Kd”BÔÂR8Z"sŒÄÜ2hR*f’ôeD-·C¡ÛơXblíç(,` -äç?Å̀Q­ư”•₫ă p -;44Ü´--JḷñGŒB¯E˜c$ &́^ÍÊDÆh>. ‰Ư‘¸0¶‹¨Ö¿ÁÉLvá¡.‘"kJ ÁÊd2ç¼Y6%HF8º[°hÏÖ÷d”Gè#É/rɈ³yó¡V©{$œÖè!óöFÈ©ê1œê8›¢:‘SƯ|Ô8‰Đ¸ˆ]à8ŸtÎdÙÊÑĂبB{Ú›«¡|Æß›÷Bû̀…ËáE“±CsrÇ@¬c°›q‘¾{‚ÜѪ7‘¾ÂoF‘Î'Îå+°[Fr§óÅöêç̣ü&g:Ÿ8—¯à¥.æMçç̣ñÆ”\é|Ua._K]̀•Î—Đ+Ëå+$¥.fMçç̣‚ÔÅ|é|‘}$Ôù(7OœËGöQ¤ó‰sùb)äó‰sù˜•CœŒô*¡G"ï““‘–’ĂÛdÇD0mwê=-V|₫¦,‘«˜<ĂÅJzv§Râ×3ˆa©v³Ä¯'nßôXj ”́RE™ÿ`Û T–IJ«f\ª¨pÆ -@«"|4Îf`’’3¬Ë©–ˆơ 7&J:†ç´¥’K̃K‹’ĂÛăÚ»X3ôCS̉¸’Ó¦¥âK8áöb5·¹Ù›Ül4›)! .er™ăKâük¯‰!&2 y3óĐâƠLn>̣~0í.Y#ôVÀÂđÇSZQI†&̀+¤%´p€(¶ƯjX²è¤©ị̂úź(Ñ%£ót'S̃«(röº!…!¶–ÙĐ:Æ9‹J$F`k¬ZT9LN¯'ùâÙd´BSxú)–1äs -¯VˆØ2䣥¦/m½„"ïĂú\ÜH`œóÅ‘÷¾ˆ(• åN$îaÖ°„«§Ụ̈¤3´^i990¢»ÿ‘rA¤èx‘÷ ¥óg\4ẢNÖ]¾rÑ·5H-ZØ^l¿G#ú1×(5¡LNú¾_KM%ö}¢­3=uOƒ&Û iå?Tú'åœ.&Ÿs±£dĐ½:`胱"¶r<6•vÑL*Œ£Ki­&X“pÆJzÖÄ.E+±üÆS’“í̉¨@Ür¢éqZö‰,GÎEbC,/IYK¿Ÿe\hU$ëÜÔĂæzZ¦nD ôùÇs‡T¢̃ ³8F§MNË’M›N—Ó`X)‰¶NR_N;ÿ,GîpK57¦Pp’́RIØ‚"x`è -̉ô$M¢¬ºé·£ˆî)‡1c̉3²5Ñ°2Yo’Câ#z—*l½YT7>oÑ'Ü×µOÆz³¨nÆ^×’1 ncHëMläưÓƈ¬70¹é'mÈZoè¢ ̣ár[o0NăhZ|¸4ë “:ÓróZo¿µÉ ”´@–BË….} ¤Æ6¿—$bƠeé×Í‘dH.¨)†\I=d3b₫‰Úl2Y€^7e£tB'åĂÍẈ§±! ó›áRü$®=£±ƒ))~¹3Z7i7CÇrk#L`)ø ̀éa'$Ï/Ùr*:ÆÔ „®Ç§#>Yh+‹ă½’9_D¿ñ’óF™7è#Ï]É™WH»?m4™y’ó2ædæÉĂæỀăc÷391DĂ8‡ -»$ Œ™yÉ•‘fæX̃€Qi´™y–Ûáw¢̀¼Bä™23½¸¢Î¡2óÈ ÚÚ]FO2³ÜùHË[“½¸B9Qꔸy£5x1$8ÈŸưôH6àRPÂơ™œÅ(rËñọ́%üب,€’µëHä}zˆơĐ€&‘ÇàßçŸÇ‡ ¦Óe= ¦!܌ܗƯ¶ân9ö)töó¸zv(q‚"4-ñ<®GĂ´3óÀCróêÈÖGsƠ1”ơ<ÆæI œG„’ qGÙZ: ‰̀ơA©?–Ø ´đ8ie â%p†ƠÂ@ÙlN½CFíÆ$¨‘$ɽ2I 0Iö́mI²êXlÖOFë I«e§}…Á$Y4" -P8%kè(HZ¿*ˆ’då/ºN³€á̃àA\› =Ír-rI7ÍrÊfHÇä¤Â1æø±i>¸ëb9M: ßDq‡RÆđŒĐDw2f“ln>ÂW(ë™ ¹t -w P*´^ ÉL'@‹Hø¨hX’hË+ă=³}§¥ôb±Ú“ IQû×é/»}̉9Ü_j|®ôVß/æ/ç´ÔÖJcăg{¼¿±ß^¬îÿ˜¹ªZĐ |éî«ӭÎÑYçN›^kS₫D¬Äœ} -¥Ưm/̣a‘ză‡Ç»á`Øp*ÚB³uzœ”vw”˜v‡ Ơ¸^ ~Iqef‹Ô§ƯÙ{•ó¤´;a²ß«qẺîÆO{Åbä÷Ia•¸Œ°BP§ĐÔ¸é†SÑHun¿×hÚV}LÈ~«ˆ“ưÆ:×ơÄ^•Îæ·Ư„^­ñIc»}%Hö»¥ƯMXɽv¿Ÿ$ôê¬MîüÙº.$'ûí êno&÷ººzĐ ïëUß­‘/\^à×̀|#̣jü{zK‘x¯@J#7—%^­6_>Wf -³>6¢"ªï0X*µjWz|o$8fÙ¬„]LíÂà 0C'Í'®ñ¶N¹hƒ—¦KŒi0¢ †ƠJ)Q#™qøU£FZ^/ÎUøGÂ6œZ^OÚl\Ü–w©^”˜Ë̉…±ƒ™+ë ÍvĂWÖ“!éF228m†éƠ¤‡•)I!Ó€ùA¢¨ü˜|S/rÈ„Ö_•/d MbŸ”Í&bŸ÷2ŸƠÇm”‰}q“#JáhûẩͼÀ±&öÅMÉ Nab_r€ÿHûâ†ê7#ḾK¿̀d$‰}’L:ob7&_»N1w“Øg’)Đ˳F™Ø—Ơ5”+±/NbŒD "±/.«¯÷Cľ8:Ë»WG”Ø7&ÆoF™Ø—ƠP#Ḱ‹[™F0Êľ¸=,ÈÛÊ–ØjgqJb_¨‚0ßs¨Ä¾äEibß(-MbL[´ü‰}q^˜ØÈû|‰}qY}̉9ľ¸¬¾B̉cĂ'öE9fợ̉=³'öÅÅC™ØÁ y;ôˆûâßsØľđrh®ÍI\Y~çyÍEBæ ‚É5Ëù³‰^7¥â;°ÔsaNŸTÍ¡”2‹ yM¼î)—Ó7T¤q„{b¾crdˆ|=ĂhBn!₫ -½ô µÍL)M…˜b[Ñzc»Oƒ)M»O™8ŸÀ·†¹ơQ”a¨Ơ₫HC­ö‡d»¬ySè';V"n”8óƒ ‰DßJ!) "Á´BΡDàX* ‰4{ÉÀ1m¸l_IG³}cn`†¶y1ăÎp½!BkŦÏdưÈT×;ÑÊ,od)¤‡/£,îxø’)…"‘ -|< [Ñ7@7¼˜¯ă‘ L.Fë›ÊŸ`JcçFÔP™ó‘Ë3eQƒ’=…Bä,F™lf ¥iú-e3¤÷|é¿<ùeÁaL ²>Ruÿ¢4-ñ<æ«ûçgE$•₫ˈ_÷o0ù"ĐÇh@Å–₫Ë1¯ÑäFe©û—fUo¦î_ x$”₫Ëí˧`I—O¥… B@ -ÙîREh—ù¹Qᇷ „¡ƯHÛ’DˆŸAüTë :%²}Óï袀̣¿ -AüÙ¾0a§­¥’˾³€q±êÁAÉ—}rjWf=ŸÇ£ÑœGœº"¢iâ\ª¸̀4‰Œ™‚K¼DfÚđ!’{s4²;Ü”§Ưgë /„/©{bæ©TÄdB"³§ảăa2™Î&1â˜"h1?dÏ»mƠg$ÆV=:âA{d̃í±l1LÂ]R™´¶rô;ñ: ™b˜9­UOÑ›¤oƠPÑLÙ#¤T̃m«{ÇàP’Í@PÈP‰‚_u1̃-ÖLà…tl©LÑ Å0}“j »N²̉è\ÎÆu襻ơ[ïï‹ê$Íü±h·O:Ç?Ú'«ïKËkÖÖJưuÏÖJcăE÷_=^4ù ³EEJÓ¼>Ægæ}+F2ó -ôZ¿4]s—G¨Pf^uîv')Đü=3ù­ô’”ˆ×éR*+ßzµÆI¶ZÓÂ_ zíκ\¯Ñ̉tÍê‡ßk4GçÑï̃\B"âX91GîưëJMά~›{âÊđ‘ă® ê q|**ˆ÷”œûˆ4­ọ́ăGÂt­ñ©^ÿ÷]R¯}Q>à÷£0錦nŸv9T×1Úë÷}…3Jú/°+>Æ7_ ¿7'ơqÖ‹ä{&¿ZÙ¥ ắY1â¨w‚;_QfÆåBr¬¥¸k'DÑ™ƠẠ́v§^2ħ‰Øơi[bkLINAzË4/ĂÊb“œó5µÅu“ÉËuÚN œ é̃$Ùv§ebÜ$¶ºÉ¸%HĈ2¦Ç¥¥FDíå T!涀–†£̉3d1`̉!‚J h!T¹\0¬øàpÙ1®̀” KÓ@ ïôZ¢#T…¤«₫2@ºk¯b1· [1æb5ÔÓ/¿Ë—¿§#Gˆ´|±: -7Lî,¯=íb50ƒå±§ÁÆËú¿E±ê#±H¯zö´ük#Ÿ·–ªƠ ´đ}wY.&"( E¹Ñ^¢©¼×c=ñỡ̉ÄæFû&gCæ‰M&zG•5q ¼D-ù¢.Ÿ<ưº:ä₫PDB7ưf!…j̉w]»̀–Đæ|Æ )!oT†¼Ä ñU Ù2½̉¢ñC6Üä1ơDZê¢:?¦ù'%¸ú2y‰ạ/['@=¸)ň2́áᬄâ!-%ëÈS -%¡%»êÅó¼Qah©•’å¦tÑR*e\´”JGÍH„6Åœ,6̣ùˆ¿*%qđhÅç#FÂy²¦$&‹Ù‰*î0)‰ñdz0qx;t¦Bƒ R§lJ¢l>¢·h‰Û°, ÆÀȧ$Êæ#R̉|RReóăA†”D¡ËG”Ö¤“\xâ|D6?v0Ñ­5b…I=âb…̣f»\Å -©ß3="g±Â±ùï+LsH¨X!Y4AaÀ+Œ‰åøo+,Ä–y±ÂY W±ÂȘzÆYĽʋÖRË¡‚‡^X|µC>1&WÁẮ e#ºƠ¯v˜p™µàa¾Đ^邇â@/Ô*WúQ{é¿4&•‰ßs•“̃íaƠ­n•̃&zÊƯäsûư}±¹[9ïn•´•Ôªs·Æ’̉Q”Ο¥;₫²­tw¶¿¿ÿYœ}ÿú½0ư1]Û¾ÁÙ—XæçÂÛjũÙ>Åm)‘đïUúÊÍ«7|ŸơiÏàK«ă|§·]ă[·V }WÈùêíÚùÊà̃œªDQÇjckÿA‡§?jä©V±w^¯?ê´¥2á(Úă₫äjÍ­\@7KsÊá™~~(ÑB#*nD3¸8¸ ²ÁU8¸jcüă[ơ`îƯZ80¶—íó»2=_•“ưCèăSÔ–Ÿ^zË›?^è¦U¾î¼uøÉ!Ö^›Gañ'Ew­}²®̉sĐ¾Ư1Ù§Ç}ú:½đŒ÷Újåú’|ÑVµ{a˜ÆåÉ‚<©“dị́Ô}̣ó˯=üªräcß‰Æ çjîî¯ÿÀ@X=eÓè†'?¦ñ >}ÛXj|¼.Ô₫º̃Ư‰¥=‘ÚǼlđiĂŸđ‹Ú6Ơ2!l¤F)|µg(Ù¹Û7pT·̃ -Ù)°k P²9ñâđ₫®ư’î_ÍÓXĂ ¥3†›₫| Ö¦7‚T‚­"úû‰æéͪ?èsÖ:Œr¡EÈ_cHVÇ;jíÂîs¤hÁ>\k]=,ß ûºÂˆs·YÁ Ú"¥©au•yq'ÔîAÙM¾ßARcë̃@LÛ®³—C }¢9ß EH¿Ñ§JÖ*®¡¦ü*ï4™l̀ÊßD¸ › ‰åx ÈÙ)Ưó™$å1”﹡üj—Ú°J º6ûÚ¦+=³|¶ăíDQ¢gFE4ïkeĐG@*wWç÷Wï˜è3;Å‹ˆä|Û¤ê$yYkư¸19‘´á% [€«Éèæưëf÷ ¤#ç‹Ê‹S'Ú' ¶̉ü1̃̃¼÷‰ăAèj”"ă]€ù̉‰€nJúúic?|—%;°rö3¸i­̣´b#£/ăºWÑWXÛơÛ k{«"n>æ̃±Ee^<)ª©Ẩ̀̉Ï™k"'¡x…Ük÷e°06ĐrG®“EP"»@®ÿpǘ4ùx°CĂgÇákÊÁ̃•^«ôÖŒ÷•c44m=“^Ë7{gpæŒnä: JlH²0Y´¯R Ă–ŒÓ)O|%eÑƯñ`8Ét–ôjè‚“+²®ŒF°8¢‡hf]‡öǾ¿gü"|îø‹©̉^óŒ÷âuĐ—¾—Ël®wBÊPóƒ-ëµÄoKh -¬Ü{th¤+9+•MÖÎëc$ K¯Û;¿r¹ö¥̣ëp»³•„ ,èˆ\»ÿª¹ ½hfGJ9TÂ@Ècơla&îdˆï&¦=sL„GË€ÈñÂX9:Í>̃ă1Ms,rL„Z|ÔàU€ĐBaồŒ›eO¿ÉqÎË.Â0f²SÁ&= gâbˆGÙ¢É.Æ Ï¤3 ϤgdĐSDÄfFÀ¤g´•cuûz₫Ùܾ‚*ØÚ¤ºoÿo¿Á̀푱J³g«ûhß.UO¦wKƠîÛ^i¬µ<††íëwÛamÊ^¦aˆú̉ŒÄªÇªxDQjÿÁªÄtXú‰[­>ØÓJă³\E]ƯTá Ă‚ ¨LA îRiAGʸÿ£Á›=%Uá}̃äµ;v ¬\|åT,­¦4̀:_§øijS¦²a3úÔŒ̃"¯ˆY– -A9tPyûô{£]½tG>"ÏmfsĂ *jíâzY«,~´ĐZ­ºX~Ñ´Ê©¾Îfê|Ÿ̉Ú³÷mµÛ;­Ăv¹^êjí½Yƒùoưu˜C2±Ç ñr5µ¡‚æß[ Æ f:Ñ×ÈVé+îا×|GóX0:gsn4₫ßK‰ƠøÓƯëxQû¯è=DÓ̀9¨¢Ư+₫œ(ä̃§“½¨{PƠ*ß{Ô›±€QñÖscËyû¶”´H^7³̀­np6₫M§©UÖơm8-ª«l¶®WÔÙI ÷°¢ë“w3kd©Ô1là̃l–æ`o`“Z êí̉7|¿¦¢Î7ƠÚ›ÛÄß Ms­¿€ˆ5Êܶ Ư+Ø¥±`0©s·Å°YJxïỲ^«ă=Y³ÄÁw}› =Ä̃vq$›½C†JáÙH<5(o~÷`¯I÷ǽ£±u>6ÅÙ‚¸å\‡×1ek‚ó{x¿§C‡·bé“·O³¸‡Ahpx£T`àüæđ_‰ĂKíµÄ0ë7]~VÀܨŸYª½ÑK±bNaâä)ô·ˆ‰Pé?4mï«o­7~¿Ë̃ ºüÄÜÇSmUÿÖÂ÷ˆg¤ _Oæáå•5 /GËh¼¯¬uXäëEåWóï*‹ƒ0ƠrgUÛrp€œ/Ö̀Î2-ˆ4ÿ´Î"—©ˆOŸzöƯg!†çzü̉ªỒ;ÓRç˜1¸syéÙ‹w_¼í¿ªµûûj>gaphÍ«“Ÿƒøô©(åzƒZÆû×—eöéo†Í -{0D÷µ*ûÚ,#̃®Í²¯=ƒøuĐü$¦ủ¹®ø̃•seíúÓeC]°+Á~ óUîÁơDk̃{Đ ›8X$H7 ëàPăûeïG;ªÿà‚ƯÜŸ Úø®»­÷€ëº»^§É5€« î£₫÷ í©ä«¾xP‚¯»KđK× m¾‚"b…`5‡Dg1¼²Ó@!”\­¿rô6-î Ư Æî©î{ê` Æ¿“=ÔA9îksåuCÈÈÉl»qưmªyÿ<}Đù¶jüåå´)ïx}ˆ3Öz.Œí¹%$†BĂyz®çô!€*Îw×év–¦û?Úëwó%FTeđ®Êăå¸ç®»6üi^ØDfs³Cü'5fƠ¾9h0çÍ‘¢̃ôÖ+đéTe^¦› mÁ]×`on®uïÓA°|Ø#đfIŒ |=jøA}‡SÅûtà×M ÊÀƒ;{pQ̉/©[ñjëm’̀†0Â#oekñ±zĂ -ä&̣x‡Ø½a^ôCœCl„̃0è&Ñ!–Éöÿ -ÿØ«KUëăZÅÆ̃×cÿ}çưÏưŸç"œP`nåuU=x¾{é¼÷û?úÿ÷Ù~¹ưzê?›ÅÆ̣~k}Ư1ÛưÛ—»~‘^€b^Ù₫y©2„g‹¤Ễ»̉₫åvÖÆ÷æ¯Û¿”ă…¨z5ưúă­Tûư¯/Uoë“¥ÚåƯi©¦uöKåµ~=¦6ü°Ç/$o‘ƒ93̃|­V·z?Û›ë¥ư°4¶æ^ưèÎSÏoo©ñqÿ­y¾»å¶OÚ‹¿çU§¾RyÚo¯?¶WO¯ª×ä$F—ơÜ”ª™ójß8«£RíđbµTÛúyT*Ï^Á$6đÏ\ivYß•rê¢Tùó¸P}₫~†ówq®“!Y¤Á₫ç’CU@ß B7¯'Îè?f^ü•£KB·g6û'Œ¿5ß{ b8¹ƒ»ÿñ@Xæ cêXˆNHK«·V÷ÉW̃­ Â:Ç7ïÊå¦÷ 9ñG.Jga©Æ‡2œóÄh¡Sçœ],yz{¸• Wă»î.QÎŒƯÉỡí èÖđO·×`4¬»£ ª2^Ö=ĐkăåPJdä{w}6p0cx'=2»½:y‹‰Á»{ -₫Üá×# -ÎÍ3•n´ÊD½âź¬“Ư¬̣ÿx§îuCÔ͹₫EåWë·ƠÙYytÆ3ư;Ö)´hpwyx¼×ˆƒ‡áYCƒF>k”Ñ$U'‰¯YLăeo(W™@'¿ÔY$ƯRéÜC$65b$ÔøsôY§¡jÍ*Ñ¡ˆƯ".Q°d{Ócû́>8`2Hï!bĂN-Èî̃ 7‹Ơg³Ùv »¤˜7›Èe6U>FnåÍ"ÜrÏí’• àƒH́´J §Ó—A ÏTÀ9ÿ· ‚4CAĂỐ±$½`fMwq‰Ư Feí£ÂèưYÀ¨¶&W¹0“²{¬GÂLÆÆ»^˜ÉË̀`˜ÉÇX§3ÀPúcf#ăèhwÙ‡ñ§̀馃I@(ÙĂƠ³9ĐœçNUXæv ci 9*ùØ œ̣°N›Ï?.uÂ)=<@ư[ưŒùA?¶?%WÂê¢̀kAd†œÇExxá„ñáD‹ˆđ8®=ç‰l rw¬¿SÖ×Påáô]¿ñDƯ°E€n*­“» o곡u —›Ă\f&¸EXl½½đ†Àrû,f&ÜǴ†—̣`5̣„û ªZæt -¤dïB aT"0$Ds*3ÙaÄ¥¼GƯÙ“ÁʆfD §yƠsL¤x¾=K|úDyAAEÉ‹Zª ϨU$ιè×ü›IrÀ±©Ơ`È¢Eͯ¢)†DF]bKIP¥Đqqqµ\¨…¼ÂE†*±Á¡T -Có`XăÁ9ûën¶Bï5f‚÷°¤ÖnlE0x¯*‡i¢y5dt#„1›ƯuÓ†;û i, 0m†’w"‚n†›ˆ–iÓ¢ăĐÓƯ1¿ê?Äû‚©‡hQÄ1iÙÅĐjÙẻZ^Ổêqh1ȤE;¢ÉˆÛ+œ@æ+…#›;ƯˆÓÇF“;†ö4aúØhrÇ( -̉ÇâsÇ´-ã -l[´›ÿ’y+°mÈÿ%óV`Û¢üæ¿d̃ªú¶-FÓ$Í[è§Â( tD]®>ßñN(h„¦ư₫ç×+yż\éßÿyî]ÿ§8 é -ü‡m·¨jNQ3Møbbk˜l™¼]T+Åj+—å÷ÏöŸÛÏ?/Ï×ïÿ)6IÛÑVï`½]léÛ—đö\± cR.áuxT!₫¯Kée᥸Œ₫·đÏùŸRÜÁ?t,đà?øm>ư…¶ÿ-Å­âé¹R¼+À³½Â?ºáÖÍÖ‹†íÖ-Ë*>a›^7,Û6§îºª]Ôu§®*¶-vƯÔá5]×룪ĐbÖuÇÔ‹-ø¡¦ÖMK3‹†âÔMƠ‹½˜ måGEÿ[ùëW>x~¾~êßï߯ï₫ ÛϪg•º©¦â•ºmZ®®ÁûV]Ơ\§¨ÖÚItß$ÿ²VÍR”:ôo ˜‹f8°Å•{èH…7LxcV5ë–¦‡>xÏtƠ:EƯUênhÅX”_8â••¤}+ătײÍJ±±ÿù₫çù¾X^YY¾üÙ{ù¼Æ—ÙÎ5øßÛ/Ÿ{ưÛ—÷;˜Eâk™ÀñQ:Ơt‹àpd`YH°¡¨ªÿjªj¨µÖ][Ï)˜Ú¾`jó¼gÿ’à$8q¿Re°OAZ‰ZÁ¤R×â»cX Á#uÇ=q@Ñ‚Ô}Ô„ØAx¦†hèR%É¥‚Ê"è`ÙH)ÀqˆÖ¨8¦‹üĆ’K.ơH©Îưë= K¥Ö¿t4MÜ®TiäMP¯@̀t€m+°%( (€ÈËtÇ8H΀Ôé¶tÄi* - -„ïÁ+°yÂÔᡦ#S‡₫G%L} ‡l˜èº ưáÔ ‘œđË̀Î¥¯[}Ưú×â” “w,]6Ơ€â¹05DEË ¢)PX c†h0[¦Ú đøºc@´¹Q|è‰â"ˆ­HE ÔŸTƠ¤f~6T4Aï̉È)ÓT—+ j¹ŒŸ†£ -´uơ_º˜ “w,Gƒb£#ųA¸C]û‰i- -zº¸çêÄx®;ZƯ@£¼åPUˆ´Y”Ö…}­» ³Ù@Jtt~K§¤4ÜC+ 6Ú@œAUB¯bÚ%ú -̀7ŸñH·úư¯À˜ E{–nÊt© \iP3j@5Cµ]|–‹˜©#¹TIƒtĆä‡PslÅớêÀ£-‚zü¡–̀h¨£ -32ˆjQ·mSµ‰ŸAI˃ C0$íûà= Gí_d”GFц¥¢"èºiƒœg?U]"4¢Û‡Zt,P‘U]§®!Ơ¶H‹c*ˆnjMñ¹x§¶lÄPPÔ#9ØE&V 27Óµ 2T6̣E××]T₫ƒ÷Œ"¢ư¯hª¿Y©Hh¨”ijÓs•Û*R ¹,>5Ldù&i1 Œ 6º h“ $BâŸ4êï!È‹&~â&w6+ *bƒe°8(Œ:u=Aºøª¸wF–‰* -ºgñÿÑÙåX“OơV=f®ºüïY}ÿƠw2ÑÑÄưJ·a"&*1¨ƒÚ©úfLD<ÔUt´»‹¢k¹Ä¯:MZLjZ‡ư‚sC­‘&üĐ4‰È´lƒZ#£]È£"to:¸VƠ™ Èx.Ÿ" Áưë=!xhkÿZÓ³#¢`·̉đđ đë}₫*ü3óåu¨ ?ô–k -uÊ@9 –(=®QÄG5©’UM£û-*à%´ºx‡M±=PfDYZTŒúp‚ø¡êZ4ÖB̀YQ (B¹à*fÓ²F΂†1'*|P ›úLÇÂáÈß(¢‹ƠB›¾†±U:LÙ@—¬¼ÁRM®Åv@Ê6̣;¿$nWµ`iLz ˆđ¾Ư₫àûóZÈ@ 4lp¯© eº -ơ Ù Ô«¨9̉5P‹m²Ë,-üø€åZü4ƒEöË Z ƒđZp8&ê"¸N₫k¦ ª¶cs ü~‚FàeÈÈP.0=z-øC`₫kÁŒ>stream --¶‹Ú‰¿`:l2ZEZq«Èm£ËTĂĐđ£Ài"%m8ZШ©ˆ -:5p"Gù-̃0èƒF›Y&â`Q0PĂQµ …M„Îs¯±€bà1Tä&gcp'åA#ÆA¹„ âo5Ă á0¨‚đ5—ØŸÑÀÄ㨷ÆbI‘èŸB :é2_h!Mt ‹†ĐÉPÚQLĂ^ää¦Nô? £ª±{l$Æ|lß›d[Uä´@r¶¡Á±éPA qP¥Àym"ÿRváG¡ß´Hè k!{ä`ä˜Î½ÄöJ¥xdrßÑmg¹ª'€³Fv`Q̉€4ÿB8b´ßB̉1YzÖØ 52…#ÜüÉÔ <×Đé‰"̉*çQPö[HplUBK¼×TM DâĂIÓỞqö YAèFº’f°đKƯ@?&œjzÊ`]ñƠ;çæư. 8t.ÿ%PŒl…R‚—¸û€¤*íÎ_PæËDo=‡]•÷¹Œ»Wè¡ÀL₫PûBºFCZÈ*®4Ré×"G‡HÁA;68LÚ€ˆºÍ ¡¤°ËƠq›à¸º´x—IvÊMGç‡AZ\<ĐÁ~GkdẮÅM([Àº‚¤¢’•S0bŒÉœF`#éjäÜ{Èxéèf§ #ƒ¼¦c¥mDYăZLNA -aT¬Pl̃º§™°̣3ÀKGµ¹—€ăXŒÊ•çg©C Ă éï đ™ ÍÔˆ´„ÀK§ç̃r—tiº®Êztºë†¦ ÙÅ°yî543BFÇå7˜Tq'¿óa(!H¸Q₫€₫lÂä¸S£Û¬;ÿ%©é¡1,¢(L_AÖ̀ÂU7:Œ ÀăĐ^e‘°mB/ŸB Đhưu‡1~́WC³²fƒÔL˜µ©«¡Qƒ -a†Ê½B…ÊPÊEÿ!ׂ‡Y[üFNđ‚-ƒMEKJl#ü\EoL¸Ủ hêŒ́Qi*4Ü‘c¬0EWA‘ØÉ°©á€fcƒ¥A,±m¦yüÀ Đ ËNhô–»·‘= ´“œh Ơ¡Ñ5)ß&Đ>ĐapœƠ2=Á‘µ}™“ṃ¯ÁÖa£tË4¢¯5¹ë–êäF’ăƒƯÿ-̀̉&§ß6-‘â0T›?)Ú¬Œ¶ e(“Ư¢ú#NL­:h|d{l’=†í1‰MÂÄĂiñࣣ¸*hjqù•qƒĐ[Ô’AKqđLĂϼï=úƯ…ÜÓÚă~ÛÈỵ̈9lÙ%ºb-ZÍ•bïuÅû]ÑèL¾^?ơןïúÿÇö?_̃ÿă7̀₫‰™L&˜ª̀̃ -Kè]}¾#)·³xOæäîơ}ÿÇûơŸG̀ï½ÿ¸₫Ÿ~ñúùÍûưWxT¼ïô~ñă÷Ëÿb ₫ÈÿÁääêN§đÏÿđ’˜è -endstream endobj 7 0 obj [6 0 R] endobj 30 0 obj <> endobj xref -0 31 -0000000000 65535 f -0000000016 00000 n -0000000144 00000 n -0000051856 00000 n -0000000000 00000 f -0000056854 00000 n -0000056668 00000 n -0000207608 00000 n -0000051907 00000 n -0000052308 00000 n -0000064961 00000 n -0000064848 00000 n -0000056115 00000 n -0000055143 00000 n -0000055553 00000 n -0000055601 00000 n -0000056266 00000 n -0000056294 00000 n -0000056412 00000 n -0000056535 00000 n -0000056738 00000 n -0000056769 00000 n -0000057203 00000 n -0000057448 00000 n -0000065035 00000 n -0000065253 00000 n -0000066353 00000 n -0000075152 00000 n -0000140741 00000 n -0000206330 00000 n -0000207631 00000 n -trailer -<<7E34FCE07468A547AA7C0144DD32033D>]>> -startxref -207810 -%%EOF diff --git a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.eps b/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.eps deleted file mode 100755 index 96af8681d00..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.eps and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.png b/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.png deleted file mode 100755 index 463e85aaeb9..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.png and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.svg b/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.svg deleted file mode 100755 index 5b6f2acf726..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/images/powered_by_solr.svg +++ /dev/null @@ -1,1202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Powered by - diff --git a/solr/site-src/src/documentation/content/xdocs/images/solr-book-image.jpg b/solr/site-src/src/documentation/content/xdocs/images/solr-book-image.jpg deleted file mode 100644 index 295317584d3..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/solr-book-image.jpg and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/solr.jpg b/solr/site-src/src/documentation/content/xdocs/images/solr.jpg deleted file mode 100644 index 29484a8e1f2..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/solr.jpg and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/solr_31_cookbook.jpg b/solr/site-src/src/documentation/content/xdocs/images/solr_31_cookbook.jpg deleted file mode 100644 index bc3ba6e08d5..00000000000 Binary files a/solr/site-src/src/documentation/content/xdocs/images/solr_31_cookbook.jpg and /dev/null differ diff --git a/solr/site-src/src/documentation/content/xdocs/images/solr_FC.eps b/solr/site-src/src/documentation/content/xdocs/images/solr_FC.eps deleted file mode 100644 index d1522e1a973..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/images/solr_FC.eps +++ /dev/null @@ -1,3686 +0,0 @@ -%!PS-Adobe-3.0 EPSF-3.0 -%%Creator: (ImageMagick) -%%Title: (/var/folders/3x/3xFVg3xW2RW7Yk+1YwUrt++++TI/-Tmp-/magick-FfiM451g) -%%CreationDate: (2010-11-09T09:35:09-05:00) -%%BoundingBox: 0 0 284 156 -%%HiResBoundingBox: 0 0 284 156 -%%DocumentData: Clean7Bit -%%LanguageLevel: 1 -%%Pages: 1 -%%EndComments - -%%BeginDefaults -%%EndDefaults - -%%BeginProlog -% -% Display a color image. The image is displayed in color on -% Postscript viewers or printers that support color, otherwise -% it is displayed as grayscale. -% -/DirectClassPacket -{ - % - % Get a DirectClass packet. - % - % Parameters: - % red. - % green. - % blue. - % length: number of pixels minus one of this color (optional). - % - currentfile color_packet readhexstring pop pop - compression 0 eq - { - /number_pixels 3 def - } - { - currentfile byte readhexstring pop 0 get - /number_pixels exch 1 add 3 mul def - } ifelse - 0 3 number_pixels 1 sub - { - pixels exch color_packet putinterval - } for - pixels 0 number_pixels getinterval -} bind def - -/DirectClassImage -{ - % - % Display a DirectClass image. - % - systemdict /colorimage known - { - columns rows 8 - [ - columns 0 0 - rows neg 0 rows - ] - { DirectClassPacket } false 3 colorimage - } - { - % - % No colorimage operator; convert to grayscale. - % - columns rows 8 - [ - columns 0 0 - rows neg 0 rows - ] - { GrayDirectClassPacket } image - } ifelse -} bind def - -/GrayDirectClassPacket -{ - % - % Get a DirectClass packet; convert to grayscale. - % - % Parameters: - % red - % green - % blue - % length: number of pixels minus one of this color (optional). - % - currentfile color_packet readhexstring pop pop - color_packet 0 get 0.299 mul - color_packet 1 get 0.587 mul add - color_packet 2 get 0.114 mul add - cvi - /gray_packet exch def - compression 0 eq - { - /number_pixels 1 def - } - { - currentfile byte readhexstring pop 0 get - /number_pixels exch 1 add def - } ifelse - 0 1 number_pixels 1 sub - { - pixels exch gray_packet put - } for - pixels 0 number_pixels getinterval -} bind def - -/GrayPseudoClassPacket -{ - % - % Get a PseudoClass packet; convert to grayscale. - % - % Parameters: - % index: index into the colormap. - % length: number of pixels minus one of this color (optional). - % - currentfile byte readhexstring pop 0 get - /offset exch 3 mul def - /color_packet colormap offset 3 getinterval def - color_packet 0 get 0.299 mul - color_packet 1 get 0.587 mul add - color_packet 2 get 0.114 mul add - cvi - /gray_packet exch def - compression 0 eq - { - /number_pixels 1 def - } - { - currentfile byte readhexstring pop 0 get - /number_pixels exch 1 add def - } ifelse - 0 1 number_pixels 1 sub - { - pixels exch gray_packet put - } for - pixels 0 number_pixels getinterval -} bind def - -/PseudoClassPacket -{ - % - % Get a PseudoClass packet. - % - % Parameters: - % index: index into the colormap. - % length: number of pixels minus one of this color (optional). - % - currentfile byte readhexstring pop 0 get - /offset exch 3 mul def - /color_packet colormap offset 3 getinterval def - compression 0 eq - { - /number_pixels 3 def - } - { - currentfile byte readhexstring pop 0 get - /number_pixels exch 1 add 3 mul def - } ifelse - 0 3 number_pixels 1 sub - { - pixels exch color_packet putinterval - } for - pixels 0 number_pixels getinterval -} bind def - -/PseudoClassImage -{ - % - % Display a PseudoClass image. - % - % Parameters: - % class: 0-PseudoClass or 1-Grayscale. - % - currentfile buffer readline pop - token pop /class exch def pop - class 0 gt - { - currentfile buffer readline pop - token pop /depth exch def pop - /grays columns 8 add depth sub depth mul 8 idiv string def - columns rows depth - [ - columns 0 0 - rows neg 0 rows - ] - { currentfile grays readhexstring pop } image - } - { - % - % Parameters: - % colors: number of colors in the colormap. - % colormap: red, green, blue color packets. - % - currentfile buffer readline pop - token pop /colors exch def pop - /colors colors 3 mul def - /colormap colors string def - currentfile colormap readhexstring pop pop - systemdict /colorimage known - { - columns rows 8 - [ - columns 0 0 - rows neg 0 rows - ] - { PseudoClassPacket } false 3 colorimage - } - { - % - % No colorimage operator; convert to grayscale. - % - columns rows 8 - [ - columns 0 0 - rows neg 0 rows - ] - { GrayPseudoClassPacket } image - } ifelse - } ifelse -} bind def - -/DisplayImage -{ - % - % Display a DirectClass or PseudoClass image. - % - % Parameters: - % x & y translation. - % x & y scale. - % label pointsize. - % image label. - % image columns & rows. - % class: 0-DirectClass or 1-PseudoClass. - % compression: 0-none or 1-RunlengthEncoded. - % hex color packets. - % - gsave - /buffer 512 string def - /byte 1 string def - /color_packet 3 string def - /pixels 768 string def - - currentfile buffer readline pop - token pop /x exch def - token pop /y exch def pop - x y translate - currentfile buffer readline pop - token pop /x exch def - token pop /y exch def pop - currentfile buffer readline pop - token pop /pointsize exch def pop - /Times-Roman findfont pointsize scalefont setfont - x y scale - currentfile buffer readline pop - token pop /columns exch def - token pop /rows exch def pop - currentfile buffer readline pop - token pop /class exch def pop - currentfile buffer readline pop - token pop /compression exch def pop - class 0 gt { PseudoClassImage } { DirectClassImage } ifelse -} bind def -%%EndProlog -%%Page: 1 1 -%%PageBoundingBox: 0 0 284 156 -userdict begin -DisplayImage -0 0 -284 156 -12 -284 156 -0 -0 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000301010F0605250E0B341511FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000882E2B9C3533822A2A652020 -4616172C0D0E140606050101000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000000B05042C130E62291FAA4835C3523EC1503EC2503E3B1813FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF170807B9413BB9413BB73E3BB83E3BB63B3BB63B3B -B5383AB4383AA83236692022340F11110505000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0100001C0C095C291DB65039 -C7583FC7583FC5553FC6563FC5563FC3523EC3533E060202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF7C2E28BB453CBB443CB9413CB9413BB73E3BB83E3BB63B3AB63B3B -B4383AB4383AB3363AB13339AB3138651C21260A0C030101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000170B076B3122C65B3ECB5E40CB5E41C95B40C95B40C95B3F -C95B3FC7583FC7583FC7583F773426FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0E0504BE4A3DBC473CBD473DBB443CBB443CBA423CB9413CB83E3BB83E3BB63C3BB4383A -B5383BB3353AB23539B1333AB1333AAF30396F1D24030101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF3E1E14B15438CE6341CF6341CD6041CC6040CD6041CB5E40CA5D40CB5E40CB5E41C95B40 -C95B40C95B401F0D0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF692A21 -C04E3EBE4A3DBE4A3DBD483DBC473CBB443CBA443CBA413CB9413BB73E3BB63B3AB63B3BB4383A -B5393BB3363AB2353AB2333A52161BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100D06743 -CF6642D06743D06743CE6341CF6442CE6341CF6341CD6141CC6040CD6141CA5D40CB5E41BB563B -010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF070302C2503EC2503EC04D3E -C04D3DBE4A3DBE4A3DBD473CBD473DBB443CBB443CBA413CB83E3BB83E3CB63B3BB63B3BB4383A -B3363A8C2A2D010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E0704D16B44D06A43D16A44 -D06A43D06743D06743D06743CF6642CE6341CF6341CF6341CD6141CC60404E2419FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF56251BC3523EC3533EC2503EC14F3DC04D3D -C04D3EBE4A3DBE4A3DBC473CBD473DBB443CB9413CB9413BB83E3BB83E3CB63B3BAF36380A0303 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27150DD16D44D16D45D26E45D16D45D16D44 -D06A43D06A44D06A43D06742D06743CF6742D06742CE63410B0503FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF030101C2553EC5553FC5563FC4533FC4533FC2503EC1503EC04D3E -C04D3EBE4A3DBD473CBD483DBB443CBB443CBA413CB9413BB83E3B2B0E0EFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E2B1AD37448D27146D27046D27146D27146D16E45D26E45 -D16E45D16D45D06A43D16A44D16B4494492FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF451F16C95B40C7583FC7583FC5553EC5553EC4533FC4533FC1503EC2503EC04D3D -BE4A3DBE4A3DBC473CBD473DBB443CBB443C692422000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000761B263E0E14010000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF81482CD47849D37447D37447D37448D37447D27146D27046D27046D27146 -D26E45D16D44D16E452B160EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100 -BC573CCA5D40C95B40C95B40C7583FC7583FC5553FC5553FC4533FC3533EC2503EC04D3DC04D3D -BF4B3DBE4A3DBC473CA03A33020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000005A181DAD2B39AB28388C212D130406FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000301C115C3621FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFBE6E42D57B4BD57A4AD47849D47749D47749D47849D37447D37448D37447D27146D27146 -CB6D43020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF361911CD6041CD6141 -CB5E40CB5D40C95B40C85A3FC7583FC7583FC5553FC5553FC4533FC14F3DC2503EC04D3DBF4D3D -BE4A3D110605FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -390F12B0313AAE2D39AD2B39AB2838AB28384F111A010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0302017A492CD7814E452A19FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030201D67D4C -D57D4CD67E4CD57A4BD57B4BD57B4BD57A4AD47849D47749D47749D37447D37447613521FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000B25839CE6341CF6442CD6141CD6041 -CB5D40CB5E40C95B40C95B40C75940C7583FC5553EC3523EC4533FC2503EC1503E3C1813FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C0809B2333AB1333A -B03039AE2E39AD2B39AD2B39AC2938831C2B080203FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E0805B67043D98651 -D7834F2D1C11FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF130B07D7814ED7804ED7814E -D6804DD67E4DD67E4CD67D4CD57A4AD57A4BD57B4BD57A4BD47849120A06FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27140DD06A43D06743CF6742CE6341CF6441CD6041CD6141 -CB5D40CA5D40C95B40C7583FC7583FC6563FC5553FC3523E7F3628000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0404AD3537B3363AB13339B2333AB03039 -AE2D39AD2B38AD2B39AB2838A3243523070B000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF28190FD2844ED98951DA8951DA89521B110A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF301D11D7834FD8844FD7834FD88450D7804E -D6804DD7814ED7814ED67E4DD67D4CD57D4CAC623C000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000A05434D16D45D16A44D06A44CF6642D06743CE6341CE6341CD6141CD6041 -CB5E40C95B40C95B40C7583FC7583FB24D39040201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0301019B3231B63B3BB4383AB3363AB2353AB1333AB0313AAE2E39 -AF2E39AD2B38AC2838AA2537450E17000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000004A2F1BDB8C53DA8B52DB8C53DA8B52DA8B520D0805FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5A3921D88650D98751D88650D98650D8844FD7834FD88450 -D7834FD7804ED7814ED7814E392214FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1B0F09D27046D27146D16D44D16D44D06A44D06A43D06743CF6642CF6442CE6341CD6041CB5D40 -CB5D40C95B40C85A3F1C0C09FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF010000822D29B83E3BB83E3CB63B3AB4383AB3353AB3363AB13339B0313AAE2D38AE2E39 -AD2B39AB2838AA25375E131F010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000603E24DB8F54DB8E54DB8F54DB8E54DB8E54DB8F54040201FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF905C36D98951D98851DA8952D98951D98851D98650D88650D98650D7834F -D88450D5814E040201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8C4F30D37448 -D37447D27146D27146D26E45D16E45D06A43D06A44CF6742D06743CF6341CD6141CD6041CB5E40 -512519FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000061251F -BB443CB9413CB73E3BB83E3BB63C3BB4383AB3353AB3363AB1333AB03139AF2E39AE2D39AD2B39 -AB2838AA2638761826020001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000764E2DDC9155 -DC9256DC9155DC9155DC9155DC9156DC9155000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000CE864FDB8E54DB8C53DA8B52DA8C53DB8C53DA8952DA8952D98851D98650D8865077492C -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF110A06D57A4BD47749D47749D37447 -D37447D27146D27046D16D45D26E45D16B44CF6742CF6642CF6341CE634197462F010000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3D1813BC473CBC473CBB443C -BA423CB9413BB83E3BB63B3AB4383AB4383AB23539B2333AB03039AE2E39AF2E39AC2B38AB2838 -AA253781192A020001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000754E2EDD9457DD9457DD9356DD9457 -DD9457DD9356DD9457BB7D4AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF060402DD9256 -DC9155DB8E54DB8F54DB8E54DB8E54DA8C53DA8B52DA8C53DB8C53DA89521B110AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF78462AD67E4DD57A4AD57B4BD47849D47749D37447 -D37448D27146D27046D16D44D16A44D06A43D06743C6623F080403FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E0C0AC04D3EBE4A3DBE4A3DBC473CBB443CBA413C -B9413BB83E3BB63B3BB4383AB5393BB3363AB13339B0313AAE2D39AE2E39AD2B39AB2838AA2638 -7F1929020001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000006D4A2BDE9658DE9658DE9758DE9759DE9658DE9758DD9658 -DE975892623AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF181009DD9457DD9457DD9457 -DD9256DC9156DC9155DC9155DC8F54DB8E54DB8E54BF7A47000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF090503D7804ED6804DD67E4CD67D4CD57A4BD57B4BD47749D47749D37448 -D37447D27146D16D45D16D44D16A4428140DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0C0504BC4F3CC1503EC04D3EBE4A3DBE4A3DBC473CBB443CB9413CBA423C -B73E3BB63B3BB4383AB4383AB3353AB1333AB0313AAF2E39AE2D39AD2B39AB2838AA26387C1929 -020001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF251A0FDF9A5ADF9959DF995ADF9959DF9959DF995ADF9959DF995ADF99596E4B2C -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF392616DE9658DD9658DE9758DD9457DD9457 -DD9457DD9457DC9156DD9256DC91554A301CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF613C24D88450D8834FD7814ED7804ED57D4CD67D4CD57A4BD47A4AD47749D47849D37447 -D27146D37147693722000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -040201AB4936C3533EC3533EC1503EC04D3DC04D3EBE4A3DBD483DBB443CB9413BB9413BB83E3B -B63B3BB4383AB5383AB2353AB2333AB0313AAE2E39AD2B38AD2B38AC2938701825020001FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1E140CDF9B5AE09C5BDF9B5AE09E5CE09E5CE09E5CE09E5CE19F5DDF9B5A4E361FFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF68472ADF9A5ADF9959DF995ADF9959DD9658DE9658DE9758 -DD9356DD9457DD9457080503FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF040202D3854F -D88650D88650D8844FD7834FD7814ED7814ED67D4CD67E4DD57A4AD47749D47849D37447AB5E3A -010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0100008D402CC7583F -C5553EC5563FC3533EC2503EC04D3DBF4D3DBE4A3DBC473CBB443CBB453DB9413CB73E3BB63B3B -B4383AB5383BB3353AB13339B0313AAE2E39AD2B39491217000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF110C07E09E5C -E1A05DE1A15DE1A15EE1A15DE1A15EE1A15EE1A05DE1A05D352616FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFA37343DF9B5ADF9B5BE09C5BDF9B5ADF9A5ADF995ADE9959DF9A5ADE9658 -8F6138FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4D311DDA8C53DA8952D98951 -D98751D88650D8844FD7834FD7814ED7814ED67E4DD57A4AD57A4AD075470E0805FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000683020CB5E40C95B40C7583FC6563F -C5553EC3533EC2503EC04D3DC04D3EBF4A3DBD473DBB443CBB443CB9413BB83E3CB63B3BB4383A -B3363AB2353AB2333AAA2E37250A0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF070503E2A35FE2A35FE2A35F -E1A35EE1A35EE1A35EE1A35EE2A35FE2A45F20170DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000DB9D5AE19E5CE09E5CE19F5DE09E5CE09C5BDF9B5AE09C5BE09C5BDF9A5A261A0FFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020101CD854EDC8F54DA8B52DA8C53D98851D98951 -D98751D98650D7834FD7834FD7804ED57D4CD67E4C382014FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF432015CD6141CB5D40CA5D40C95B40C7583FC7583FC5553E -C4533FC1503EC04D3DC04D3DBE4A3DBD473DBB443CBB443CB9413BB83E3CB63B3AB4383AB3353A -9A2E310D0404FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020101E3A660E2A55FE2A660E3A660E2A660 -E3A660E3A660E3A660E2A560100C07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF090604E2A35F -E1A35EE1A15EE0A05DE1A05DE1A15EE09E5CE09E5CE09E5CD19255010100FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF3C2817DC9155DC9155DC8F54DB8E54DA8C53DB8C53D98851DA8952 -D88650D98650D88450D7814E804C2E000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF21100AD06743CE6341CD6041CD6141CB5E41C95B40C7583FC7583FC5553FC4533F -C2503EC04D3EC04D3DBE4A3DBC473CBB443CBA413CB9413BB83E3BB63B3B7B2628030101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000DFA55FE3A861E3A861E4A961E3A861E3A861E3A861 -E3A861E4A961050402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F170DE2A660E3A660E2A35F -E2A35FE2A35FE1A35EE1A15EE1A15DE1A15E5D4226FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF000000BF824BDD9356DD9457DC9155DC9155DC8F54DB8E54DA8B53DB8C53D98851D98650 -D88650BC7245030201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0704 -C76540D06A43D06743CF6341CD6040CD6141CB5D40C95B40C75940C75840C5553FC3523EC2503E -BF4D3DC04D3DBF4B3DBD473CBB443CBA413CB9413C521B1A000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFBE8F51E4AA62E4AB62E4AB62E4AB62E4AB62E5AB63E4AB62E4AB62 -010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF43321CE3A861E3A861E3A861E3A660E2A55F -E2A660E2A35FE1A35EE2A35F0F0A06FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B1E11 -DF9959DE9758DE9758DD9457DD9457DC9156DC9155DC8F54DB8E54DA8B52DA8952D98951170E08 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF040201B5623CD26E45D16D45 -D16B44D06743CE6341CE6341CD6141CB5E40C95B3FC7583FC7583FC5553FC4533FC2503EC04D3D -C04D3EBF4A3DBC473CB8433C2B0F0EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF9B7543E5AD63E5AE64E5AE64E5AD63E5AD63E5AD63E4AD63CC9A58FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF755832E4AB62E4AB62E5AB63E3A861E3A861E4A961E3A860 -E2A660AA7C48000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000AD7947DF9C5BDF9A5A -DE9959DE9758DE9658DD9356DD9457DC9155DC9155DC8F54DA8B524B301CFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000955333D37447D27046D16E45D16D44D06A44 -D06743CF6341CF6341CD6141CB5E40C95B40C7583FC7583FC5563FC3523EC2503EC04D3DC04D3D -A84136100605FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -7A5D35E6B064E5B064E5AF64E6B065E6B064E6B065E6B065A07B45FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFB5894FE4AD63E5AD63E5AD63E4AA62E4AB62E4AB62E4AB62E4A961342616 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E160DE19F5DE09E5CDF9B5ADF9B5BDF995A -DF9A5ADE9658DE9758DD9456DD9457DC9156966139000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000006E3F26D47749D4784AD37447D27146D16D45D16D44D06A43D06742 -CE6341CF6341CD6141CA5D40C95B40C75940C7583FC5553FC3533EC2503E87362B040101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020001430D15000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF110D07060503FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5E482AE7B366 -E7B266E7B266E7B467E7B467E8B567E6B265795E36FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -010100E5AF64E6B064E6B065E6B065E6B064E5AE64E5AD63E5AE64DEA65F030201FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF986E3FE1A15DE0A05DE09E5CE19F5DDF9C5BDF9B5ADF995A -DE9959DE9758DD9457CD884F060402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF462A19D67E4DD57A4BD57A4AD47749D37448D27046D27146D16E45D06A43D06743CE6341 -CF6341CD6141CB5D40C95B40C7583FC75940C5553F5C271D000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF150507982331AA2638 -25080CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000A07C4717120AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF45361FE7B567E8B768E8B767 -E8B768E9B768E8B768E8B768594628FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0A06E7B567 -E7B366E7B266E6B265E7B266E5AF64E6B065E6B064715631FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF130E08E2A660E2A35FE2A35FE1A15EE1A05DE09E5CE09E5CE09C5BDF9C5BDF995A -DE975822170DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF22150CD8844F -D6804DD67E4CD57B4BD57A4BD47849D37447D27146D27046D26E45D16A44D06743CE6341CE6341 -CD6141CB5E40C95B40C5573E331610000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0100004B1418AF2E39AD2B38AB28389A2233020001 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF281F12E7B467 -342817FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF312716E8B969E9B969E9BA69E8B969E9BA69 -E8B969E9BA693C301BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF261E11E8B768E7B467E8B567 -E7B567E8B567E7B266E7B266E7B26617110AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -826037E3A861E2A660E2A55FE1A35EE2A35FE0A05DE1A15DE09E5CE19F5DDF9B5B634427FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E0905CF804CD8834FD7834FD7814E -D67D4CD57B4BD47A4AD47749D47448D27146D27046D16D44D06A44CF6642CF6341CF6442CD6040 -B65439130906FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0B03048A282CB0313AAE2D39AD2B39AB2838AA2537450F16FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000BE9554E8B7685D4929FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF20190EEABC6AE9BB6AE9BB6AEABC6AE9BB6AEABC6AE9BB6A -251E11FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4E3E23E9B969E9BA69E8B767E8B768E8B768 -E8B768E7B467C19655000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A0804E4AB62E4AB62 -E3A861E4A961E2A660E3A660E1A35EE2A35FE1A15EE0A05DAB7946010100FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF040302BC7847DA8951D98751D88650D8834FD7814ED67D4C -D67E4CD57B4BD47749D37448D27146D27146D26E45D16A44CF6742CE634196472F050202FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -331010B13739B3363AB1333AB03039AE2E39AD2B39AB2838A82537050102FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF302615E9BA69E8B969907341FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF120E08EABE6BEABD6BEABE6BEBBE6CEABD6BEABE6BEABD6B141009FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF866B3DE9BB6AEABC6AE8B969E9B969E9BA69E8B969E8B768 -44361EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A512EE5AE64E5AD63E4AA62E4AB62 -E3A860E3A861E3A660E2A660E1A35EDA9C5B0B0804FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0100009C653BDC8F54DA8B52D98951D88650D98651D7834FD6804DD67E4DD67D4C -D57A4BD47749D37447D27146D27046D16D44D16A44683421010000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050201792926B63C3BB4383A -B3363AB13339B0313AB03039AE2D39AD2B38AC28384A1018FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000C29B58E9BB6AEABC6ACFA85EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF080604EBC06DEBC06DEBC06CEABF6CEBC06DEBC06DEABF6C080604FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFC8A25CEABD6BEABE6BEBBE6CE9BB6AE9BB6AEABC6AE9B969060503FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050402E2AF63E5AF64E6B065E5AD63E5AE64E4AB62E4AB62 -E3A861E4A961E2A660312315FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -734C2DDC9155DB8E54DC8F54DA8B52DA8952D98650D88650D88450D7814ED67D4CD67E4DD57A4A -D47749D37448D27146D27146391E13000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF200C0AAF3F38B9413BB83E3BB63B3BB4383AB3363A -B13339B0313AAF2E39AD2B38AC2938A72436040101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0908081A19191A1919181717000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF090808151414151414000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF241E11EABD6BEABE6BEBC06DEBBF6C020201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020201 -EBC16DEBC16DECC26EECC26EEBC16DECC26EEBC16D010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020201EBC06DEBC06DEBBF6CEBC06DEBBE6CEABD6BEBBE6C8A6F3EFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF554225E7B567E7B266E6B265E6B064E6B065E5AD63E5AE64E4AB62E4AA62 -7A5A34000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF49311DDD9658DD9457 -DC9155DC9155DB8E54DB8C53D98851D98951D88650D7834FD7814ED67D4CD67E4CD57B4BD47749 -C06941170C08FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF02010062261FBC473CBB443CB9413BB83E3BB83E3CB63B3AB4383AB3353AB2333A -B03039AE2E39AC2B38AB2838450F16FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000001817171A19191A19191A1919070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000A7884D -EBC06DEBC06CEBC16DECC26E0E0C07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000E8C06DECC46F -ECC36EECC46FECC46FECC36EDDB768FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF110E08ECC26E -EBC16DECC26EEBC16DEABF6CEBC06DEBBF6C221C10FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020101D9AB61E8B768E7B466E7B567E7B366E7B266E6B064E6B065E5AE64BF9052020201FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF24190EDE9959DF9959DE9759DD9457DC9256 -DC9156DB8E54DA8B52DA8952DA8952D88650D8844FD6804DD67E4DD67E4C9F5B38060302FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF130806 -A44335C04D3DBE4A3DBD483DBB443CB9413CB73E3BB63B3BB5393BB23539B1333AB0313AAE2E39 -AE2E39AD2B39A52636020000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF070707 -1A19191A19191A19191A1919171616000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100D07EBC16DEBC16DECC26E -EDC46FECC36E262012FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7A65EECC56FEDC670ECC56F -EDC770EDC570AE9252FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F2716ECC46FECC36EECC46F -EDC46FECC46FECC26ED5AF63010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF41341EE8B969 -E9B969E9B768E8B768E8B567E7B466E7B366E6B266E3AD63120E08FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0F0A06D79758E09C5BDF995ADE9959DD9658DD9457DC9155DC9155 -DC8F54DA8B53D98851DA8951D88650D88450D7804E704128010100FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000461E15C3533EC2503EC04D3D -C04D3EBE4A3DBC473CBB443CBA423CB73E3BB63B3BB4383AB3363AB13339AF3039AF2E39AC2B38 -AB283826090CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001616161A1919171616 -1514141A19191A1919060505FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF64532FECC46FECC46FEDC56FEDC570EDC670 -4A3E23FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2884CEEC972EEC972EEC972EEC972EEC972 -877240FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B4C2BEDC770EDC670EDC570ECC56FEDC56F -ECC46F564728FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000CDA75EEABC6AE9BB6AE9B969 -E9B969E8B768E8B768E7B466E7B56744341EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF040302C28A50E1A15EE09E5CDF9C5BE09C5BDF9959DE9758DD9457DD9457DC9155DB8F54 -DA8B52DA8952D98951D98650402717000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF09040392422EC7583FC6563FC3523EC1503EC04D3EBE4A3D -BD473DBB443CB9413CB83E3BB63B3BB63B3BB4383AB3353AB2333AB0313AAE2E39AD2B39801D2A -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0505051A19191A19190808080606061A1919 -1A1919151414000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000010101010101 -FFFFFFFFFFFF0202020909090C0C0C080808010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0101010605050A0A0A0D0C0C0B0B0B070606000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000000303030909090D0C0C0A0A0A070707010101000000FFFFFFFFFFFFFFFFFF -0B0A0A1A19191A19190000000000000605050B0B0B0C0B0B060505000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000000303030A09090D0C0C090909030303000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000DFBA68EDC670ECC56FEEC771EDC770EDC770796539FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF806D3EEFCB73EECA72EECA72EECB72EECA7262542FFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF957E47EEC972EDC770EDC771EEC771EDC770EDC6700B0905 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F2716EBBF6CEABE6BEABD6BEABB6AEABC6BE9B969 -E9B969E8B768917241000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100A07543 -E2A35FE1A35EE1A15EE09E5CE09C5BDF9B5ADF9959DE9658DD9457DD9457DC9155DB8E54DA8B52 -C97E4B1A1009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000002F160FC45B3DC85A3FC7583FC7583FC5563FC4533FC1503EC04D3EBE4A3DBD473D -BB443CB9413BB83E3BB63B3BB4383AB3363AB1333AAF3039AE2E39AD2B38AB28380C0304FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000001414141A19191918180000000000001716161A19191A1919 -050404FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A19190101010D0D0D -1A19191A19191A19191A19191A1919080707FFFFFFFFFFFFFFFFFFFFFFFF000000100F0F1A1919 -1A19191A19191A19191A19191A1919191818030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101 -1312121A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFF0B0A0A1A1919 -1A19190505051716161A19191A19191A19191A1919151414000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000001212121A19191A19191A19191A19191A19190E0E0E000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFA88D50EDC770EDC771EEC972EEC972EEC972B49756FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF62542FEFCC73EFCD74EFCC74EFCD74EFCD74443A21FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000D9B968EFCB73EECA72EEC972EEC971EEC972A58B4EFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000BA9956ECC26EEBC06DEBC06DEABE6BEABD6BEABC6AEABC6BCFA45E -040302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000765933E4A961E3A660E2A35F -E1A35EE1A15DE09E5CE09C5BE09C5BDF9A5ADE9758DD9457DC9155DC9155AA6E40080503FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0402017C3D28 -CF6441CD6141CB5D40C95B40C7583FC6563FC3533EC2503EC04D3DBE4A3DBF4B3DBD473DBB453C -BA413CB73E3BB63B3AB5383AB3363AB13339B0313AAE2D39AD2B39441016FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0303031A19191A19190A0A0AFFFFFFFFFFFF0808081A19191A1919131212000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A19191716161A1919191818121212 -1515151A19191A19191A1919040404FFFFFFFFFFFFFFFFFFFFFFFF1413131A19191312120C0B0B -0A0A0A1010101A19191A1919151414000000FFFFFFFFFFFFFFFFFF0000001615151A19191A1919 -1413130A0A0A0C0C0C1514141A1919020202FFFFFFFFFFFFFFFFFF0B0A0A1A19191A19191A1919 -1A19191414141312121918181A19191A19190D0C0CFFFFFFFFFFFFFFFFFFFFFFFF000000151414 -1A19191918180B0B0B0706060E0D0D1A19191A19190C0C0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF352C19EEC972EEC972EECA72EECA72EECB72EBC871000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF493E23EFCE74F0CE75EFCE74EFCE74EFCE742B2515FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -040402EFCD74F0CD74EFCC73EECB73EFCB73EECB732F2716FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF221C10ECC36EECC46FEBC16DEBC16DEBC06DEBC06DEABD6BEABE6B1C170DFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B3921E5AD63E4AB62E3A861E2A560E2A660E2A45F -E1A15EE09E5CDF9B5AE09C5BDE9959DE9658DD945779502F010100FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C0F09BB5F3DD06743D06742CE6341 -CD6141CA5D40C95B40C7583FC5553EC4533FC2503EC04D3EBE4A3DBC473CBB443CB9413BB83E3B -B63C3BB4383AB23539B1333AB03039AF2E39AD2B38932230000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -1111111A19191A1919010101FFFFFFFFFFFF0000001918181A19191A1919030303FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A19191A19190B0B0B000000FFFFFF000000040404 -1918181A1919151414000000FFFFFFFFFFFFFFFFFF030303010101FFFFFFFFFFFFFFFFFFFFFFFF -0B0B0B1A19191A1919010101FFFFFFFFFFFFFFFFFF0908081A19191A19190A0A0A000000FFFFFF -FFFFFF000000040404020202FFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919131212010101FFFFFF -FFFFFF0101011615151A1919191818000000FFFFFFFFFFFFFFFFFF0808081A19191A1919030303 -FFFFFFFFFFFFFFFFFF0A0A0A1A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030301 -ECC871EFCC73EFCD74EFCC74EFCD74F0CE75070604FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF332C19 -F0D075F0D075F0D076F0D075F1D07618150CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF17140BEFCE74 -F0CE75EFCE74F0CE75EFCC73E5C46E020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000A4894D -EDC56FEDC670ECC46FECC46FEBC16DECC26EEBC06D594929FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF251D10E6B065E5AE64E5AD63E4AA62E4A961E2A660E2A55FE1A35EE1A15E -E09E5CE09C5BE09C5BDE995948301C000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0101005F3420D27146D16E45D06A43D06743CF6341CD6141CB5E40 -C95B40C7583FC7583FC6563FC3523EC1503EC04D3EBE4A3DBD473DBB443CB9413CB83E3BB63B3B -B4383AB3363AB1333AB0313AAE2E39AD2B380B0304FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A1919 -0E0D0DFFFFFFFFFFFFFFFFFFFFFFFF0B0B0B1A19191A1919121111000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101011A19191A19190D0C0C000000FFFFFFFFFFFFFFFFFFFFFFFF0A0A0A1A1919 -1A1919020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0605051A1919 -1A1919010101FFFFFFFFFFFF0000001615151A1919161515000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF -0808081A19191A1919010101FFFFFFFFFFFF0000001716161A19190E0D0DFFFFFFFFFFFFFFFFFF -FFFFFF0101011A19191A1919070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF826F3FEFCE74 -F0CE75EFCE74F0CF75F0CF751B170DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF211D10F1D277F1D277 -F0D176F0D176F1D1760A0905FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38301BF0D076F0CF75F0D076 -F0D075EFCE746B5D34FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15120AEEC972EEC771EDC770 -EDC670ECC56FECC46FECC36EA98A4E000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0F0B07DFAF63E6B265E6B065E5AE64E5AD63E4AB62E4A961E2A660E3A660E1A35EE1A05DE09E5C -D191541E150CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0F0905A95F3AD37447D37447D27046D26E45D06A43D06742CE6341CD6141CB5D40C95A3F -C7583FC5553FC4533FC1503EC04D3EBE4A3DBC473CBB443CB9413BB83E3BB63B3AB4383AB2353A -B2333AB03039AE2D39AD2B38360C12FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F0F0F1A19191A1919020202FFFFFF -FFFFFFFFFFFFFFFFFF0101011A19191A19191A1919030202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0101011A19191A1919070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919050404 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000101010303030404040C0C0C1A19191A1919010101 -FFFFFFFFFFFF0000001A19191A19190A0A0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF0404041A1919 -1A1919020202FFFFFFFFFFFF0101011A19191A19190D0C0C030303030303030303030303040404 -1918181A19190D0C0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF201C0FF0D075F0D075F0D076 -F1D176F1D2773A321CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF131009F1D277F1D277F1D377F1D377 -F2D378020201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF675A32F0D176F1D176F1D277F0D176F0D075 -121009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8B7643EFCB73EEC972EEC972EDC770EDC771 -EDC670E0BA69080704FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050402C89E59E8B768 -E7B567E6B265E6B064E6B065E5AD63E4AB62E4A861E2A55FE2A660E2A45FB27F49090704FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000422818D47C4B -D57B4BD47749D37448D27146D16E45D06A43D06742D06743CE6341CD6041CB5E40C95B40C7583F -C5553EC3533EC2503EC04D3EBE4A3DBD483DBB443CBA413CB73E3BB63B3BB5383AB23539B1333A -B0313AA02A34290A0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A1919111010FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0F0F0F1A19191A1919100F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A1919 -1A1919070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A1919060606FFFFFFFFFFFF -FFFFFF0000000B0B0B1817171A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFF -0101011A19191A1919070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919020202 -FFFFFFFFFFFF0202021A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -0F0F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000DCBF6CF1D277F0D176F1D277F1D377 -635731FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080804F2D478F2D478F2D478F1D478EACD74000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA79252F2D578F1D377F1D277F1D377BDA45D000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0B0A05EFCD74EFCC73EECA72EFCB73EEC972EEC972EDC7702A2313 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100A5844BE9B969E8B768E8B768E8B567 -E7B266E6B064E6B065E5AD63E4AB62E4A961E2A55F836038020101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF070402925836D88450D7814ED67D4CD57A4A -D47749D37447D27146D26E45D06A43CF6642CF6341CD6041CB5D40C95B40C7583FC5553FC4533F -C2503EC04D3DBE4A3DBD473DBA443CB9413BB83E3CB63B3BB4383AB3363A8A282D250A0C010000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0C0C0C1A19191A1919151414100F0F100F0F100F0F100F0F100F0F100F0F -1414141A19191A19191A1919020202FFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A1919070707 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919040404FFFFFFFFFFFF010101171616 -1A19191A19191111110A0A0A0808080F0F0F1A19191A1919010101FFFFFFFFFFFF0101011A1919 -1A1919080707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0A0A -1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919020202FFFFFFFFFFFF -0202021A19191A1919090909030303030303030303030303030303030303030303020202FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF615430F1D277F1D377F2D478F2D5789A884DFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF020201F2D679F2D779F3D77AF3D87ABFA860FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000E8CC73F2D679F1D478F2D478F2D5793E361EFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF716137F0CE75F0CE75EFCC73EFCD74EECA72EECB72715F36FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000007B6338EABE6CE9BB6AE9BA69E8B768E8B768E7B466E7B366 -E6B065E6B065E5AD63E4AB624E3A22000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000291A0FCE814DD88650D7834FD6804DD67E4CD57A4BD47849D47849 -D37447D27146D26E45D06A43D06743CF6441CC6040CB5D40C95B40C7583FC5553EC3523EC2503E -C04D3DBF4A3DBD473DBB443CB9413CB83E3B6F2423150707000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0100001918181A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19190E0D0DFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A1919080707FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0B0B0B1A19191A1919010101FFFFFFFFFFFF0E0D0D1A1919191818020202 -FFFFFFFFFFFFFFFFFF0505051A19191A1919010101FFFFFFFFFFFF0000001A19191A19190D0C0C -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919 -000000FFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919020202FFFFFFFFFFFF0000001A1919 -1A1919080707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF110F09F2D478F2D679F2D579F2D679DBC06DFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000F1D779F3D97BF3D87AF3D97B928249FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -070604F3D77AF3D77AF3D679F2D679F0D478040302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF060503 -EDCE75F0CF75F0CF75F0CE74EFCE74EFCD74BDA15B010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF4E4024EBC06DEBBF6CEABE6BE9BB6AE9B969E9BA69E8B768E7B466E6B265E6B064 -DAA65F221A0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020101764C2DDB8E54DA8C53DA8952D88650D8844FD7804ED67D4CD57B4BD47749D37448D27146 -D16D44D16A44CF6642CF6341CD6141CB5E40C95B40C75940C5553FC3533EC1503EC04D3DC04D3E -BE4A3DB8453B531E1B090303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A09091A1919 -1A1919100F0F0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0E0E0E1A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFF0101011A19191A1919171717020202FFFFFFFFFFFFFFFFFF -0303031A19191A1919131212000000FFFFFF0000001918181A19190E0D0DFFFFFFFFFFFFFFFFFF -FFFFFF0505051A19191A1919010101FFFFFFFFFFFFFFFFFF1414141A1919191818000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFF -FFFFFFFFFFFFFFFFFF0303031A19191A1919020202FFFFFFFFFFFFFFFFFF1313131A1919151414 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000C1AA60F3D77AF3D87AF3D77AF3D87A030301FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFCEB868F3DA7BF4DA7BF3DA7B6C6137FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1D1A0EF3D87A -F3D87AF3D87AF2D779837341FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5B4F2CF1D277F1D176 -F1D277F1D076F0D076E9C9710E0C07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF262012 -ECC36EEBC26DEBC06DEBC06CEABE6BEABC6AE9B969E9BA69E8B768E8B567BA90520B0805FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171009BF7E49DC9155 -DB8E54DA8B52D98951D98651D88650D8844FD7814ED67D4CD57A4AD47749D47448D27146D26E45 -D06A44CF6642CF6341CD6041CA5D40C95B40C7583FC5553EC3533EC2503EA94436391612030101 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001817171A1919171616000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001514141A19191A19190C0C0C -FFFFFFFFFFFFFFFFFF0101011A19191A19191A19191A1919100F0F0B0A0A1010101A19191A1919 -1A1919020202FFFFFFFFFFFF0000001A19191A1919131212000000FFFFFFFFFFFF0000000D0C0C -1A19191A1919030303FFFFFFFFFFFFFFFFFF0706061A19191A1919131212020202000000FFFFFF -010101080808070606FFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFF -FFFFFF0303031A19191A1919020202FFFFFFFFFFFFFFFFFF0504041A19191A1919111010010101 -000000FFFFFFFFFFFF000000030303050404FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF453D23F3D87AF3D97BF3D87AF4DA7C110F08FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA69654 -F4DB7CF4DC7CF4DB7C4B4326FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF423B21F4DA7BF4DA7BF4DA7C -F3D87A1C190EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020201E4C871F2D478F1D377F1D377F1D176 -F0D1763A321CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100D07E5BE6CECC56FECC46F -EBC16DEBC16DEBC06DEABD6BEABB6AE9B969E8B9698C6E3E020201FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000543B22DE9959DE9759DD9457DC9155DC8F54 -DA8B52D98951D98751D7834FD7814ED57D4CD57B4BD47749D37447D27046D16E45D06A43D06743 -CF6341CE6341CD6141CB5D40C95B40C75940923F2E240F0C000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A1919080808FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0605051A19191A1919191818010101FFFFFF -FFFFFF0101011A19191A19190E0D0D1514141A19191A19191A19191A1919181717040404FFFFFF -FFFFFFFFFFFFFFFFFF1110101A19191A19191312120A0A0A0E0E0E1918181A19191A19191A1919 -151414010000FFFFFFFFFFFF0000001212121A19191A19191A19191918181817171A19191A1919 -040404FFFFFFFFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF030303 -1A19191A1919020202FFFFFFFFFFFFFFFFFF0000000D0D0D1A19191A19191A1919171616131212 -1514141A19191A19190A0A0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100E08FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF070603 -F4DA7BF3DA7BF4DA7BF4DB7C2B2616FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF857744F5DD7DF4DC7C -F5DD7D322D19FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF756A3CF4DB7CF4DB7CF4DB7CD1BB6A000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF463E23F2D679F3D679F2D578F1D478F1D2778B7844000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050402CDAD62EDC770EEC771EDC570ECC36EECC26E -ECC26EEBBF6CEBBE6CE9BB6A564426000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0B0805A87644E09C5BDF9959DE9758DD9456DC9156DC9156DC8F54DA8B52 -D98951D98650D8834FD7814ED67D4CD57A4BD47849D37447D37146D16D45D16A44CF6642CF6441 -CD6141CB5E40733324130806000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000001716161A1919191818000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001716161A19191A19190A0909FFFFFFFFFFFF010101 -1A19191A19190707070000000404040909090A0909060505000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0101011716161A19191A19191A19191A1919131212030303131212191818060606000000 -FFFFFFFFFFFFFFFFFF0000000E0D0D1A19191A19191A19191A19191A1919171717010101FFFFFF -FFFFFFFFFFFF0B0A0A1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A1919 -020202FFFFFFFFFFFFFFFFFFFFFFFF0000000807071817171A19191A19191A19191A19191A1919 -191818070606FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000A49252FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C8D4FF4DB7C -F4DB7CF4DD7D51492AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF665D34F5DF7EF6DF7EF5DE7D1C1A0E -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB8A65EF5DD7DF5DD7DF5DD7D4F4728FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF010100D7BF6BF3D77AF2D779F2D679F3D679CEB567030301FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010100A99151EECA72EEC972EEC871EDC770EDC670ECC36EEBC16DECC26E -E1B867282012FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -392A18DB9F5BE1A15EE09E5CDF9C5BDF9A5ADE9758DD9356DC9155DB8E54DA8B52D98951D98651 -D7834FD7814ED67D4CD57B4BD47749D47749D37447D27146D26E45D06A44C5613E54281A070302 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000FFFFFFFFFFFF0101011A19191A1919 -070707FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000040404070606050505010101000000FFFFFF000000020202FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010101050404070606050505020202000000FFFFFFFFFFFFFFFFFFFFFFFF -000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000030303060505070606050505020202000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF040402F3D97B121009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2D2917F5DD7DF6DF7EF5DE7D -827542FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C4527F5DF7EF6E07EF6E07F0D0C07FFFFFFFFFFFF -FFFFFFFFFFFF000000F4DE7DF5DE7DF5DE7DF5DE7E080704FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -332E1AF4DA7BF3D97BF3D87AF2D779F3D77A17140BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000007E6C3DEFCC74EFCD74EFCB73EEC971EEC971EEC771EDC670ECC36EC3A05B0D0B06FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0503028C673BE2A660E2A35F -E1A15DE09E5CE09E5CDF9B5BDF9A5ADE9658DD9457DD9256DB8F54DA8B52D98851D98650D88450 -D6804DD67D4CD57B4BD47749D37448D27146B35D39391D12020101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A1919070707FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E0D07 -F4DA7B7E703FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020101EDD779F6E07EF5DF7EC1AF62FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF35311CF6E17FF6E17FF6E17F040302FFFFFFFFFFFFFFFFFFFFFFFF -0B0A06F6E17FF6E17FF6E07F9B8E50FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000C5B064F4DB7C -F4DA7BF3D97BF3D97B4E4627FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF504627F0CF75 -EFCE74EFCE74EFCC73EFCB73EEC972EEC972EDC770947C46030201FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF221A0FD29E5AE4AA62E3A861E2A55FE1A35EE1A15E -E09E5CDF9C5BDF995ADD9658DD9457DC9155DB8E54DB8C53DA8952D98851D98650D7834FD7814E -D67D4CD57A4A95543322130B000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000E0D0D0E0D0D040303FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1E1B0FF4DB7CF2D97B -060603FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF776D3DF6E17FF6E17FF4DF7E000000FFFFFFFFFFFF -FFFFFFFFFFFF232012F7E481F7E480F5E27F000000FFFFFFFFFFFFFFFFFFFFFFFF242113F7E380 -F6E27FF6E17F282415FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF242112F5DD7DF4DD7DF5DC7CF4DB7C -A18F50000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF282314F1D377F1D176F0D075F0CE75 -F0CE75EFCC74EECA72EEC9725D4F2C000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101016C5430E6B065E5AD63E4AB62E4AB62E3A861E3A660E2A35FE1A05DE19F5D -DF9B5ADF9959DE9759DD9457DC9156DB8E54DA8B53DA8952D88650D8844FD7814E704128110A06 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0301010C0304FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2B2616F5DD7DF4DC7C58502DFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1B180EF6E280F7E380F6E27F090805FFFFFFFFFFFFFFFFFFFFFFFF -14120AF7E481F7E481CCBD6AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C4628F7E380F7E481E5D277 -010100FFFFFFFFFFFFFFFFFFFFFFFF000000AD9D59F5DE7DF5DE7EF4DC7CDFCA72060503FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100E08EACD74F1D277F2D378F0D176F0CF75EFCE74EFCE74 -E7C6702D2615FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF120E08 -BD9554E7B466E6B265E6B064E5AE64E4AA62E3A861E2A660E1A35EE1A15DE09E5CE09C5BDF995A -DE9658DE9758DD9356DC9156DB8E54DA8B52CB804C50321E060402FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000001A0808641F20B135394F1719FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF38321CF5DE7EF5DF7EE4CF75010101FFFFFFFFFFFF -FFFFFFFFFFFF000000D7C670F7E380F7E3801E1C10FFFFFFFFFFFFFFFFFFFFFFFF090805F8E682 -F8E6829E9353FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF837945F7E581F8E581625A33FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF18150CF6E17FF6E07FF6E07EF5DE7E231F12FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF050402D1B969F2D578F2D478F2D478F1D377F1D176F1D076CCB0630F0D07FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000004B3C22E7B768E9B768E7B567 -E8B567E7B266E5AF64E5AD63E4AB62E4A961E2A560E2A35FE1A05DE19F5DE09C5BDF9959DE9658 -DD9457DD9256B37445342114010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -0E05044C1A18AB3A37B63B3BB4383AB2353A5E1B1EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF433D22F5DF7EF6E07EF6E07E39341DFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF56502DF7E581F8E6823F3B21FFFFFFFFFFFFFFFFFFFFFFFF030201F9E783F8E782766E3D -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCBBC6AF8E682F7E5810F0D08FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF91854BF7E280F6E17FF6E17F675D35FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100 -AE9B57F3D87AF3D77AF3D679F2D578F2D478F1D2779D884D040302FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080604A4864CEABE6BEABC6AE9B969E8B667E7B567E6B265 -E6B065E5AE64E4AB62E3A861E3A660E2A55FE2A35FE1A05DE09E5CE09C5BDF995A92633A1F140C -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0602023816129A3C32BC473CBB443C -B9413BB83E3BB63B3BB4383A6E2123FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF050402E1CE74F7E17FF7E280C7B768000000FFFFFFFFFFFFFFFFFFFFFFFF0C0B06 -F8E682F8E7826B6438FFFFFFFFFFFFFFFFFFFFFFFF000000F7E681F9E883554F2CFFFFFFFFFFFF -FFFFFFFFFFFF010101F8E782F9E783B5A85FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0C07F7E481 -F7E480F6E280B8A85F010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000807341F3DA7BF4DA7B -F3D87AF2D779F2D578F2D478655932000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF000000302817E0B868EBC06DEABF6CEABD6BEABC6AE9B969E8B768E8B567E7B266E6B064 -E5AE64E4AB62E4A961E2A660E2A35FE1A15DDC9B5A6D4B2C0E0A06FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF02010025110C803629C1503EC04D3EBF4B3DBD473DBB443CB9413CB73E3B -B63B3BB4383A732224FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF262314F6E27FF7E380F7E481211E11FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB6AA5FF9E883 -A59A56FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD5C771F9E98338341DFFFFFFFFFFFFFFFFFFFFFFFF -0F0E08F9E984F8E88337331DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF776E3EF8E581F7E481EDDB7B -0A0905FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF51492AF5DD7DF4DB7CF4DA7BF4DA7CF3D87A -EDD277322C19FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030201836E3E -ECC56FECC46FEBC26DEBC06DEABD6BEABC6BE8B969E8B768E7B466E6B265E7B366E5AF64E5AD63 -E4AB62E4A961CF96574B371F040302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000170B07 -642E20C2583DC7583FC5553EC4533FC1503EBF4D3DBE4A3DBD473DBB443CB83E3CB63B3BB4383A -742325FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -756C3DF8E581F7E581A09454FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3C371FF9E984E8D87A000000 -FFFFFFFFFFFFFFFFFFFFFFFFACA15AFAEA84211F12FFFFFFFFFFFFFFFFFFFFFFFF2C2917F9EA84 -F1E17F020201FFFFFFFFFFFFFFFFFFFFFFFF060603F6E580F7E681F8E681322E1AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF292515F5DE7DF5DE7DF5DD7DF4DB7CF3DA7BD2BC6A121009FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C180DD0AF62EEC972EEC871EDC670 -ECC46FEBC16DEBC06DEABE6BEABC6AE9B969E9B768E7B567E7B266E5B064E5AD63B3864C312414 -010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000B06034B2618B85A3BCD6040CB5D40C95B40 -C7583FC5563FC3523EC2503EC04D3EBE4A3DBD483DBB443CB9413CB73E3BB63B3B772526FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101CABC69 -F8E682F8E7820F0E08FFFFFFFFFFFFFFFFFFFFFFFF040402F7E882FAEA84040302FFFFFFFFFFFF -FFFFFFFFFFFF898149FAEB84100F08FFFFFFFFFFFFFFFFFFFFFFFF58532EFAEB84787140FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF5F5932F8E882F8E782807643000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF100F09EEDA7BF6E07FF6E07EF5DE7DF5DD7DA59454040402FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0101005F522EEFCC73EFCB73EEC972EDC770EDC670ECC36EECC26E -EBC06DEBBE6CEABD6BE9BB6AE9B969E8B667E7B5678E6E3F1B140C000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF040201341C119E5534D16D44D06A43CF6742CE6341CD6041CB5E41C95B3FC7583F -C5553FC4533FC04D3DBE4A3DBC473CBB443CBA423CB83E3BB63B3A6F2323FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF14120AF9E783F9E883 -766E3EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8E864BFAEB8513120AFFFFFFFFFFFFFFFFFFFFFFFF -696438FAED86060503FFFFFFFFFFFFFFFFFFFFFFFF948B4EFAEC8517160CFFFFFFFFFFFFFFFFFF -FFFFFF030201EDDE7DF9E984CCBE6B020201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050503D7C670 -F6E280F6E17FF5DF7EF6E07F6D6338000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0D0C06B79E59F1D076EFCE74F0CD74EFCB73EEC971EDC770EDC670ECC36EEBC16DEABF6C -EABE6CE9BB6AE3B56667512E0B0905FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01010022140C7D492C -D57B4BD47749D27146D16D45D16A44D06743CF6341CD6041CA5D40C95B40C75940C5553EC3533E -C2503EC04D3DBE4A3DBD473DBB443CB83E3BB63B3B611E1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF514C2BF9E983F5E580050402 -FFFFFFFFFFFFFFFFFFFFFFFF262314FAEC852F2C19FFFFFFFFFFFFFFFFFFFFFFFF4F4B2AFAED86 -000000FFFFFFFFFFFFFFFFFFFFFFFFDBCF74CDC26D000000FFFFFFFFFFFFFFFFFFFFFFFF4A4627 -F9EA84F5E681111009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101B2A35CF7E380F7E380F7E380 -F2DD7D38331DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000403820EDCF75 -F0D176F0CF75F0CE75EFCC73EFCB73EEC972EDC771EEC771EDC670ECC46FECC26ED0A95F463920 -030301FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000120B075F3A23CB7C4AD6804DD67E4DD57A4AD47749 -D37448D27046D16E45D06A43CF6642CF6442CB5E41C95B3FC7583FC5553EC4533FC1503EC04D3D -BF4A3DBC473CBB443CBA413CB73E3B551B1BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000ADA35CFAEA84524D2CFFFFFFFFFFFF -FFFFFFFFFFFF010100E9DC7C58532FFFFFFFFFFFFFFFFFFFFFFFFF38351EDAD076FFFFFFFFFFFF -FFFFFFFFFFFF030302FBEE86474326FFFFFFFFFFFFFFFFFFFFFFFF010100DFD376FAEB85454124 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000847B45F8E682F7E581F8E581D9C77014120AFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0605039A884DF2D679F2D478F2D378F1D176 -F0D076F0CE74EFCC73EECB72EEC972EDC770EDC570AF92522C2414010000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF080503462E1BB77645DB8C53D98951D88650D7814ED67E4CD57A4AD47849D37447D27146 -D26E45D06A44D06743CE6341CD6041CB5E40C95B40C7583FC5553FC1503EC04D3EBE4A3DBC473C -BB453CB9413BB83E3B451716FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000000000000202020404040706060908080A09090B0B0B0B0A0A0A0909090808 -080707060505030303020202000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF090805EEE07DE3D578010100FFFFFFFFFFFFFFFFFF -FFFFFF6B66398B844AFFFFFFFFFFFFFFFFFFFFFFFF232213AAA25BFFFFFFFFFFFFFFFFFFFFFFFF -14130BFAEE86050503FFFFFFFFFFFFFFFFFFFFFFFF37331DFAED85978E50000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF524D2BF8E883F8E782F8E682AC9F59050503FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF282414E0C871F2D779F3D679F2D478F1D277F0D176F0D076F0CF75 -F0CE75EFCD74EECA7288734117130B000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202012D1F1297673CDD9356 -DC9155DB8E54DB8C53D98851D98650D8834FD6804DD67E4CD57A4BD47749D37448D16D44D16B44 -CF6642CF6341CC6040CB5D40C95B40C75940C5553EC3533EC2503EBF4D3DBE4A3DBB443CB9413C -B83E3B300F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000504040D0C0C -1616161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191918181312120B0B0B070606030303000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF010101010101010101010101010101010101010101010101010101 -010101010101010101000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF34321CFBED8633301BFFFFFFFFFFFFFFFFFFFFFFFF14130B -CCC26DFFFFFFFFFFFFFFFFFFFFFFFF14130B817B45FFFFFFFFFFFFFFFFFFFFFFFF35321C8F894D -FFFFFFFFFFFFFFFFFFFFFFFF000000CBC06CDCD175040402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -2A2816F9E983F8E883F9E883756C3D010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -020201786D3DF4DC7CF3DA7BF3D97BF2D779F2D679F2D578F1D377F1D277F0D075E6C57061532F -090804FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000001C140C755430DC9B5ADF9C5BDF9A5ADE9758DD9457DB8F54 -DA8B52D98851D98651D88450D7804ED67E4CD57A4AD4784AD37448D27146D16D44D06A43D06743 -CD6041CB5E41C95B40C7583FC5553EC4533FC2503EC04D3DBF4A3DBC473CBB453DB9413C1D0909 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000303030E0E0E1A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191413130A0A0A040404000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000010101FFFFFF -FFFFFFFFFFFFFFFFFF0000008D854BC0B767000000FFFFFFFFFFFFFFFFFF000000CFC56F010100 -FFFFFFFFFFFFFFFFFF0A09055D5932FFFFFFFFFFFFFFFFFFFFFFFF646036222012FFFFFFFFFFFF -FFFFFFFFFFFF262414FBEF871B1A0EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF111009F2E380F9EA83 -F7E7823E3A20000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF15130BCCB968F5DD7D -F4DC7CF3DA7BF3D87AF2D779F3D77AF2D579F2D478CEB466413920020201FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -0F0B06574025CD9757E1A35EE1A15EE09E5CE09C5BDE9959DD9658DD9457DC9155DB8E54DB8C53 -D98951D88650D7814ED57D4CD57B4BD47749D37447D27146D16E45D16A44CF6642CE6341CD6041 -CA5D40C95B40C6563FC3533EC1503EC04D3EBE4A3DBD473DBB443CB9413B0F0505FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0202021110101A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19190B0B0BFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C5631000000FFFFFFFFFFFF -FFFFFFFFFFFF030302DCD2761C1A0FFFFFFFFFFFFFFFFFFFFFFFFF4D4A290B0A06FFFFFFFFFFFF -FFFFFF0303013E3C21FFFFFFFFFFFFFFFFFFFFFFFF8D874C000000FFFFFFFFFFFFFFFFFF000000 -B4AC615A5631FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF050503D9CD74FAEC85DED27517160CFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000544D2BF7E17FF6E07FF5DE7DF4DC7CF4DC7C -F4DA7CF3D87AF3D77AAA9655262213000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0504023E301BB2894FE5AD63E4AB62 -E3A861E2A55FE2A35FE09E5CDF9C5BDF995ADE9758DD9457DC9156DB8E54DA8B52DA8952D88650 -D88450D7804ED67E4CD57A4BD37447D27146D16D44D06A43D06743CF6341CD6141CB5E41C95B40 -C7583FC5563FC3533EC04D3EBE4A3DBD473DBB443CB9413B030101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000080808 -1918181A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19190A0A0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF827A45494526FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1E1C10979052FFFFFFFFFFFFFFFFFFFFFFFF080805212012FFFFFFFFFFFFFFFFFF000000 -252414FFFFFFFFFFFFFFFFFF0000004F4C2BFFFFFFFFFFFFFFFFFFFFFFFF19180DAEA65E000000 -FFFFFFFFFFFFFFFFFFFFFFFF010101B4AA60FAEC85B4AA60060603FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF090905AFA15AF7E280F6E17FF5DF7EF5DF7EF6DF7EF4DD7CF4DB7C807241 -13110A000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010101271F128E7040E7B466E7B266E5B064E5AE64E4AB62E3A860E2A660 -E1A35EE1A15EE09E5CE09C5BDE9959DE9658DC9155DB8E54DB8C53D98851D98650D7834FD7814E -D67D4CD57A4AD47749D37447D27146D16D44D06743CE6341CD6041CB5D40C95B3FC75840C5553E -C3533EC1503EC04D3EBE4A3DBB443CA63A35000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000E0D0D1A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19190A0909FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4B4728FBED861E1C10FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -6762370C0B06FFFFFFFFFFFFFFFFFFFFFFFF1D1B10FFFFFFFFFFFFFFFFFFFFFFFF0B0A06FFFFFF -FFFFFFFFFFFF0605030A0A06FFFFFFFFFFFFFFFFFFFFFFFF8B864C070704FFFFFFFFFFFFFFFFFF -FFFFFF000000857E47FBEF877D7742010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -37331DEDDC7CF7E581F7E481F7E280F6E17FF5DF7EE6D0765A512E070603FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000017130B -6C5832E0B567E9BB6AE9B969E8B768E7B567E7B366E5AF64E4AB62E4A961E3A660E2A35FE0A05D -E09E5CDF9C5BDF995ADE9758DD9457DC9155DB8F54DA8B52D98650D88450D6804DD67E4CD57B4B -D4784AD37448D27046D16E45D06A43D06743CF6341CB5E41C95B40C7583FC5553EC3533EC2503E -BF4D3DBE4A3DBD473D6E2923FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000E0E0E1A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19190A0909FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010101857E47E8DC7C090805FFFFFFFFFFFFFFFFFFFFFFFF010100474426 -FFFFFFFFFFFFFFFFFFFFFFFF040402FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFF -050503FFFFFFFFFFFFFFFFFFFFFFFF0E0E08292716FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF55512D -F9ED85434024000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0403028E844BF8E782F8E682 -F8E581F7E481F7E481C9B9683B361E010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B09054E4225CAA85EEBC16DEABF6CEABE6B -E9BB6AE9B969E9B768E7B567E6B265E6B065E5AD63E4AB62E3A861E2A560E2A35FE19F5DE09C5B -DE9959DD9658DD9457DC9155DB8E54DB8C53D98951D88650D8844FD7804ED57A4AD47749D37447 -D27146D26E45D16B44CF6742CE6341CD6041CA5D40C95B40C6563FC3533EC1503EC04D3EBE4A3D -BC473C3A1512FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0909091A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -0B0B0BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000534F2CC2B868010101FFFFFFFFFFFFFFFFFFFFFFFF0D0C07030201FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF232213FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2A2917E5DA7B1B190EFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F1D10DCCF75F9E984F8E883F9E783F8E682A29654 -221F12000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF030302362E1AAB9152EEC971EDC770ECC56FECC46FEBC26DEBC06DEABE6CE9B969 -E8B768E8B567E6B265E6B065E5AE64E4AA62E3A861E3A660E1A35EE1A05DE09E5CE09C5BDF9959 -DD9457DC9156DB8E54DA8C53DA8952D88650D88450D7814ED57D4CD57A4AD47849D27146D16D44 -D06A43D06743CF6341CD6141CB5E40BE563B8B3D2B5B271C3415111A0A08080303000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0202021A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19190C0C0CFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF2A2817847E47000000FFFFFFFFFFFFFFFFFFFFFFFF010101FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -010100010100FFFFFFFFFFFFFFFFFFFFFFFF111109B5AD62070704FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF010101696438F9EB84F9EA84F9EA84F5E581797040100F08FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010000211D10837240 -EECC74EFCC73EFCB73EEC972EDC771EDC670ECC36EEBC16DEBC06DEABD6BEABC6AE9B969E8B767 -E7B567E6B265E5AD63E5AB63E3A861E3A660E2A35FE1A15DE09E5CDF9C5BDE9959DE9758DD9356 -DC9156DA8B52D98851D98650D7834FD7814ED67E4DD57A4BD47749D37448B05F3B793D274C2618 -29140D120806040201000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001110101A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19190D0D0DFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1111093D3B21FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0505036C673A010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100F08 -C4BA69FBED86FAEC85E5D67A544E2C050503FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000121009625731DFC46EF1D377F0D176F0CF75F0CE74 -EFCC73EECB72EEC972EDC770ECC46FECC26EEBC06CEABD6BEABC6AE9BA69E8B768E8B567E7B266 -E5AF64E5AD63E4AB62E3A860E1A35EE1A15EE09E5CE09C5BDF9A5ADE9758DD9457DC9156DC8F54 -DA8B53D0804D9A5D38653D253D24151F110B0B0604010000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191616160B0B0B050505 -0202020101010101010101010101010302020404040707070C0C0C1312121918181A19191A1919 -1A19191A19191A19191A19191A19191A19191A19190F0F0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171616 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0505030C0B06FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -010101262415000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000484527F7EA83FBEE86C4BA69 -34321C010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF070603453E23C2AD61F2D779F2D579F2D478F1D277F1D277F0D076EFCE74F0CD74EECA72 -EEC972EEC871ECC56FECC46FEBC16DEBC06DE9BB6AE8B969E8B768E8B567E6B265E6B065E5AE64 -E4AB62E4A961E2A55FE2A35FE19F5DDF9B5AC0844D855A355438212F1F12150D08050302000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A09091A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919181717060505000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000020202070707101010191818 -1A19191A19191A19191A19191A1919131212FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF060603A49D58FCF0889A93521D1C10000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0201012E29179D8E50F5DD7D -F4DB7CF4DA7BF3D97BF3D77AF3D679F2D478F1D377F0D176F0CE75EFCC74EFCB73EEC971EDC770 -EDC670ECC36FECC26EEBBF6CEABE6BEABC6AE9B969E8B567E6B266E6B065DDA75FA77D48705230 -44321D23190E0C0905010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1414141A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919191818020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000404040E0D0D -1918181A19191A1919171616FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF2C2A18E5DA7B706B3C0D0C07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000001B190E776E3EEFDB7BF6E17FF5DE7DF5DD7DF4DB7CF4DA7B -F3D87AF3D77AF2D578F2D478F2D378F1D176F0CF75EFCE74EFCC73EECA72EDC771EDC670ECC36E -ECC26EEBC06CCEA75F9074415D492A34291718120B060503000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000001A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19190C0C0CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000060505 -1312121A1919000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202016C683A -4C4829040402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0D0C0758522ED9CA71F8E581F7E481F6E280F6E17FF6E07EF6DF7EF4DC7CF4DB7CF4DA7CF3D87A -F2D679F2D578F1D277F1D277F0CF75F0CE75EBC972B5995779663A4A3E232620120F0C07020201 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919050404 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000020202 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000020202040404070707 -0909090A0A0A0A0A0A090909080808050505030303010000000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000000202020706060A09090A0909070707030303000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0707041F1E11010100FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0404023D3920B7AC61F9E984F8E882 -F8E782F7E481F7E380F7E380F7E17FF5DF7EF5DE7EF4DC7CF4DC7CF3DA7BF3D87ADAC26D9A884D -6457323A321C1B170D070604000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0302021A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919040404FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000000202020909091313131A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191716160D0C0C040404000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000 -000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0000000606061515151A19191A19191A19191A19191A19191A19191918180A0909000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF01010026241491894DF8EA83F9EB84FAEA84F9E983F8E883F9E783F8E682 -F8E581F7E380F5E07FBFAF638275425048292A2615110F08030201FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19190A0909FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -0606061414141A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191817170A0909000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919050505FFFFFFFFFFFFFFFFFFFFFFFF0101011312121A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919191818060505000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000015130B -6C673AEBE07DFBEF87FBEE86FAEC85FAEC85F9EA84E6D678A499566A63383E3A211D1B0F090805 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000040201020100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919191818020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0404041514141A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919191818090909000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919080707FFFFFFFFFFFFFFFFFF0202021716161A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919030303FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0808054D4A29D0C871FCF188FBF087C8BE6B -8A834A55512E2D2B1813120A030302000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0100000402010C0704160C0724130C361C1149261761301F7E3C27984630BA543B -C75940C3543E050202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919191818080808000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000D0D0D1A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919151414010101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A1919 -1A19190B0A0AFFFFFFFFFFFF0101011817171A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919151414000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0202012C2B18686439706C3D424023201F110A0A06010100FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0100000503020C080518100A271A0F3925164F331E684227855431A5673DCA7B4A -D6804DD57A4AD47849D37447D27046D16D44D16A44CF6742CF6442CD6141C95B3FC7583F662C20 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000001A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191918180E0E0E060606010101000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101011312121A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919191818030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A19190E0E0E -FFFFFF0000001514141A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919060606FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -030302000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101000504020D0B061A140B2920123D2F1B5340246E522F8C683CAF7F4AD59A59 -E1A15EE09E5CDF9B5BDF995ADD9658DC9156DB8E54DA8B52DA8951D88650D8844FD7814ED67D4C -D47749D37448D27146D16E45D06A43CF6642CF6341CC6040CB5E40C7583F0E0604FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1312121A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191918181110100A0909050404020202000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -1514141A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919191818040404FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0807071A19191A19191A19191A19191A19191A19191A19191A19191A1919131212FFFFFF0A0A0A -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919171717000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101000605030E0C071B170D2C251540361F584A29736036937B45B89856DEB667 -EABE6BEABC6BE9B969E8B768E7B567E6B265E6B064E5AD63E3A860E2A660E2A35FE1A05DE09E5C -E09C5BDF9959DE9658DD9457DC8F54DA8B53DA8952D88650D7834FD7804ED67E4CD47A4AD37447 -D27146D16D44D06A44D06743CF6341CD6040CB5E40813929FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080707 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191918181312120A0909 -040404000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001312121A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919191818020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191817170101011A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919080808 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0101010605030F0E081C190E2D2917433D225B512E776B3C98874CBFA860E6CA73 -F2D578F1D377F0D176F0CE75EFCC73EFCB73EEC972EDC770EDC670ECC36EECC26EEBC06CE9BB6A -E9B969E8B768E7B466E7B366E6B065E5AD63E5AB63E2A55FE2A35FE1A15EE09E5CE09C5BDE9959 -DE9759DD9457DB8E54DA8B52DA8952D88650D88450D7814ED67D4CD57B4BD47749D27146D26E45 -D06A43D06743CE6341CD6141CB5E40100705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -111111060606000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0C0C1A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919151515000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919100F0F1A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919191818000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF000000010101060603100F081D1C0F2F2D194541245E59327C74429E9354C4B666ECDB7B -F7E481F7E380F7E280F6E17FF6E07FF5DE7DF4DB7CF4DA7BF3D87AF3D87AF2D679F2D478F1D277 -F1D177F0D075EFCD74EECA72EEC971EEC771EDC570ECC46FEBC16DEBC06DEABC6AE9B969E9B768 -E7B567E7B266E5B064E5AE64E4AB62E3A660E1A35EE1A05DE09E5CDF9C5BDF995ADE9658DD9457 -DC9156DA8B52DA8952D88650D8844FD6804DD67E4DD57B4BD47849D27046D16D45D16A44CF6742 -CF6341CC6040723423FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0C0C1A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1918180D0C0C010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0303031A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191716160E0E0E0B0B0B0A0A0A0E0D0D141313 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19190B0B0BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171616 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19190B0A0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02020116150C3E3B22615D347F7A45A29B57C7BF6CF2E782 -FBF088FCF087FBEE86FBEE86FAEC85FAEC85FAEB85F9EA84F9E983F8E883F8E682F7E481F7E480 -F7E380F6E17FF6E07EF5DE7DF5DD7DF3DA7BF3D97BF2D779F2D679F2D579F1D377F0D176F0CF75 -EFCC73EECA72EEC972EEC871EDC56FECC46FEBC16DEBC06DEABD6BE9B969E8B667E7B467E7B366 -E6B065E5AD63E4AB62E3A861E1A35EE1A15DE19F5DDF9B5BDF995ADE9658DD9457DC9155DA8C53 -D98851D88650D88450D7814ED57D4CD57A4AD47749D27046D16D45D16B44CF6742CF6341CD6040 -090403FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011918181A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19190D0C0C000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000001313131A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919100F0F020202000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101010A0909 -1918181A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF01010108080515140B282616413F23605C33858047 -AFA65EE0D477FAED86FAEC85F9EB84FAEA84F9E983F9E883F8E682F8E581F7E380F6E27FF6E17F -F6E07FF5DE7DF5DD7DF4DC7CF3D87AF3D77AF2D679F1D478F2D378F1D176F0D076F0CE75EFCB73 -EEC972EDC770EDC670EDC46FEBC16DEBC06CEBBE6CE9B969E8B768E8B567E6B265E6B065E5AD63 -E4AB62E4A961E2A35FE1A15DE09E5CE09C5BDF9A5ADD9658DD9457DD9256DA8B53D98951D98651 -D7834FD7814ED67D4CD57B4BD47849D27146D16D45D06A43D06743CF644254271AFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0505051A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -171616030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030303 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -060505000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101161515 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19190C0C0C -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191515150808080202020101010101010404040E0E0E -1A19191A19190E0D0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0101000606031211092321123B371F58522E7A7240A29654D1C26DF7E380F6E17FF6E07EF5DE7D -F5DD7DF4DB7CF3D87AF3D77AF2D579F2D478F1D377F0D176F0D075EFCE74EECA72EEC972EEC871 -ECC56FECC46FEBC16DEBC06DEABD6BE8B969E8B768E7B466E7B266E6B065E5AD63E4AB62E3A861 -E1A35EE1A15EE19F5DDF9B5BDF995ADD9658DD9457DC9155DA8B52DA8952D88650D8834FD7814E -D57D4CD57A4BD47749D37447D26E45D16B44CF6642B15438010100FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000000A09091A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -050404FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF100F0F1A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919060505FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011817171A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919191818000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919191818070606000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030303181717 -020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000404020E0D071E1C10342F1A4E4728 -6F643897854CC3AC61ECD075F1D277F1D176F1D076F0CE74EFCB73EEC971EDC771EDC670ECC36E -ECC26EEBC06CEABD6BE9BA69E8B768E7B467E7B366E5AF64E5AD63E5AB63E3A861E2A35FE1A05D -E09E5CE09C5BDF9959DE9758DD9457DC9155DB8C53DA8952D88650D88450D6804DD67D4CD57B4B -D47749D37448D16D45D06A44D067431D0D09FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000000808081A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919030303 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19190E0E0E000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0504041A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919050505FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080707 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919151414 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000000303010B0A061A160D2D2716463C22665630897341B39554E1BA69ECC26EEABF6C -EABE6BE9B969E8B767E7B567E6B266E6B065E5AE64E4AA62E4A961E1A35EE1A15EE09E5CDF9C5B -DF9959DE9759DD9356DC9156DA8B52D98851D98650D8834FD7814ED67E4DD57A4AD47849D27046 -D16E45D06A436F3723FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0303031514141A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919171616000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0404041A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001514141A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19190D0D0DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919131212000000FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000E0D082F2D19222012090905000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000002010109070415110A271F12 -3F321C5C48297D6137A47E48D29E5AE4AB62E3A861E2A35FE1A15EE09E5CE09C5BDF995ADE9658 -DD9457DC9156DB8C53D98951D98651D7834FD7814ED67E4CD57A4BD47749D27146D26E45B05939 -020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000000706061615151A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19190A0A0AFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0A0A0A1A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919131212FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0707071A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919171616000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000016160C777341EEE681B4AD61726E3E -3F3C221B1A0E060603000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010100060503110D0722180E372716513921714E2D96663BC0814CDC9155 -DA8B52DA8951D88650D88450D7804ED57D4CD57B4BD47749D27146CF6C43110906FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000000303030D0C0C1817171A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919191818000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1110101A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02020136331DB3AC61FCF188FBF087 -FBEF87EDE27FA49C5866613635321C15140B030302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000403020E0905 -1C120B301D11482B1A653B24884E30AD5F3AD070453F2114FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000020202080707100F0F1918181A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919060505FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171616 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919030303FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000001918181A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0E0D07625D34E6DA7BFBEE86 -FBEE86FAED85FAEC85FAEA84F9E983E0D176948A4E59532F2D2918100F08010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000101010505050B0B0B1414141A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19190F0F0FFFFFFFFFFFFFFFFFFFFFFFFF0000001A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919020202FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1615151A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000B0B06010100 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101002725159B9353FAEC85 -F9EB84F9EA84F9E983F8E882F8E782F8E682F7E481F7E480F7E380CDBA698578444E4628252113 -0B0A06010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000030202080707111010191818 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919181717000000FFFFFFFFFFFFFFFFFF0000001A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF131212 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919040404FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919090808 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000033321C413E23020201 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0707044F4929D4C670 -F9E983F9E883F8E782F8E682F7E481F6E27FF6E17FF5DF7EF6DF7EF5DD7DF4DB7CF3DA7BF1D77A -B9A45C75673A423A211E1A0E080704000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000060606161515 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919000000FFFFFFFFFFFFFFFFFF0000001A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1212121A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919050404FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF101009BDB66657542F050502 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001C1A0F827944 -F4E27FF7E481F7E380F7E280F6E17FF6E07FF5DE7DF5DD7DF4DC7CF3DA7BF3D87AF2D679F2D478 -F1D277F1D177F0D075E8C771A68E5068593239301B17130B050402000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001110101A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919020202 -FFFFFFFFFFFFFFFFFF0000001A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1514141A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101017E7A45F8EE86706B3D0A0A06 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0303023D3820 -BDAE61F7E380F6E17FF6E07EF5DE7DF5DD7DF3DA7BF3D97BF2D779F2D679F2D579F1D377F0D176 -F0CF75F0CE74EFCC73EEC972EEC871EDC56FECC46FDAB365967A465A49292F2615120E08030201 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001918181A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919020202FFFFFFFFFFFF -FFFFFF0000001918181A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000001817171A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171616 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0B0A06030302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000033301BF0E581FCF1888A834A13120A -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000121109 -6C6237E8D277F4DC7CF4DB7CF4DA7CF3D87AF3D77AF2D679F1D478F2D378F1D176F0D076EFCD74 -EFCB73EEC972EDC770EDC670EDC46FEBC16DEBC06CEBBE6CE9BB6AE8B768E8B567C89B5884653A -4F3B22271E110D0A06010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F0F0F1A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919030303FFFFFFFFFFFFFFFFFFFFFFFF -1312121A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919060505 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000001A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F3D22 -0C0C07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A0A05BAB164FBF087FBEF87A69E591F1E11 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101 -2E2917A39453F3DA7BF3D87AF3D77AF2D579F2D478F1D176F0CF75F0CE75EFCC73EECA72EEC972 -EEC871ECC56FECC46FEBC16DEABE6CE9BB6AE8B969E8B768E7B466E7B266E6B065E5AD63E4AB62 -E3A861E1A35EB5814B765331432F1B1F150C090603000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0B0B0B1A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919020202FFFFFFFFFFFFFFFFFFFFFFFF0E0D0D1A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19190D0C0CFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0303031A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100FFFFFFFFFFFFFFFFFFFFFFFF00000089844A201F11 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100666137FBEF87FBEE86FBED86C0B6662D2B18 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0A0905564C2BD7BF6CF2D478F2D378F0D176F0CF75F0CE75EFCC73EFCB73EEC971EDC771EDC670 -EBC26DEBC06DEABE6BE9BB6AE9BA69E8B768E7B467E7B366E5AF64E5AD63E4A961E2A660E2A35F -E1A05DE09E5CE09C5BDF9959DE9758D99155A16A3F6742263924160D0805FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1615150E0E0E020202000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0E0D0D1A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919020202FFFFFFFFFFFFFFFFFFFFFFFF0706061A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919181717000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0D0D -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919121212FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0202010A0A06FFFFFFFFFFFFFFFFFFFFFFFF020201C7C06C444225000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF232112E4D779FAEC85FAEC85FAEB85D5C8713E3A21 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000221D108B7A44F1D176F0D076EFCE74EFCD74EECA72EEC771ECC56FECC36EEBC26DEBC06D -EABD6BEABC6AE9B969E8B767E7B567E6B266E5AD63E4AB62E3A861E3A660E1A35EE1A15EE09E5C -DF9C5BDF9959DE9759DC9155DB8E54DA8B52875432020101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1110101A19191A1919141414070606000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0000001817171A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919000000FFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919060606FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011918181A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080707 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF434125010100FFFFFFFFFFFFFFFFFFFFFFFF0B0B06EEE58175713F000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF060503A29956FAEB85F9EA84F9E984F9E883E7D679534D2C -040402FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF050402443A21C4A75EEECA72EEC972EEC871EDC570EDC46FECC26EEBBF6CEABD6BEABC6A -E9BA69E7B467E7B266E5B064E5AE64E4AB62E3A861E2A560E2A35FE1A15EE09E5CE09C5BDE9758 -DD9456DC9155DB8F54593922000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0D0C0C1A1919 -1A19191A19191A19191A1919100F0F050505010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000C0C0C -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919171616 -000000FFFFFFFFFFFFFFFFFFFFFFFF0000001615151A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919181717000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000100F0F1A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919020202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0C0B06615E35FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF232213FCF288A69F59030302FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000004F4B2AF9EA84F9E984F8E882F8E782F8E681F1DE7E696237 -090805FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF00000017130B746237E5C06CECC56FECC36EEBC16DEBC06DEABC6BE9B969E8B768E7B567 -E6B265E6B065E5AD63E4AA62E4A861E2A660E2A35FE09E5CDF9B5ADF9959DD9658DD9457CF8950 -26190FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0A09091A19191A19191A1919 -1A19191A19191A19191A19191A19191414140A0909040404010101000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000303031110101A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19190E0D0DFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0908081A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919131212000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0000000A09091A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919151414000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF979051 -1A190EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF524E2CFCF188CFC66F0C0B06FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF16150CD2C46EF8E782F8E682F7E581F7E480F6E280F6E17F837743 -111009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF020201342B18AC8D50EBC06DEABE6BE9BB6AE9B969E8B768E8B567E7B266E6B065 -E5AD63E4AB62E3A660E1A35EE1A05DE09E5CDF9C5BDF995ADE975898663C060402FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000 -000000000000000000FFFFFF000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191918181312120D0D0D0A0909080707070606 -0605050706060807070A0A0A100F0F1817171A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919050404FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000001918181A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919151414030303000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0101010E0D0D1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919050505FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C1B0FD9CF75030201 -FFFFFFFFFFFFFFFFFFFFFFFF0000008D864CFBEF87EBE07E201E11FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0302018B8249F7E681F8E581F7E380F7E280F6E17FF5DF7EF5DE7D9E8E50 -1C190E000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0E0B065E4C2BD9AF62E8B969E8B768E7B467E7B366E5AD63E4AA62E3A861 -E2A660E2A35FE1A05DE19F5DDF9B5AD99556422D1A000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFF000000000000 -000000FFFFFF000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0605051A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919191818000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0A09091A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191211110707070303030101010101010202020505050E0D0D1918181A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919151414 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000C2BA69858048000000FFFFFF -FFFFFFFFFFFFFFFFFF020201C3B969FBEF87FAED86444024000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0000003B371FF1DD7DF6E280F7E17FF6E07EF5DF7EF5DD7DF4DB7CF3DA7BB6A35C -2A2515000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF010100261E11927340E7B366E5AF64E5AD63E4AB62E4A961E3A660E1A35E -E1A15DE09E5C8E6339080603FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFF000000000000000000FFFFFF -000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0504041A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919090909FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -1615151A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919030303FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF33311CFCF188302F1AFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0A0905E9DD7CFAED85FAEC86746E3D000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF0E0C07C1B264F6E17FF5DF7EF5DE7DF4DC7CF4DB7CF4DA7CF3D87AF2D779CCB466 -3A321C010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0706034B3921C59556E4AB62E3A861E2A560E1A15DBA834C261A0F -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFF000000000000000000000000000000000000 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF0303031A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -171616000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0302021A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19190A0A0AFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -0807071A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919090808 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010100C7BF6CEDE37F070704FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF201E11FAEC85FAEC85F9EB84A49957030302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF01010172683AF5DE7EF5DD7DF4DC7CF4DA7BF3D87AF3D77AF2D679F2D578F1D377DCBE6B -4D4326040302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000001B140B795933CB9255382817000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFF000000FFFFFFFFFFFF000000000000000000000000000000000000000000FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF030303 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919030303FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0706061A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919100F0F000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000272615756F3F000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -4B4728FAEB85F9EA84F9E984CBBE6B0B0B06FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF2A2615E5CF75F4DB7CF3DA7BF3D97BF2D779F2D679F1D478F2D378F0D176F0CF75E8C770 -62542F080604FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -000000FFFFFFFFFFFF000000000000000000000000FFFFFF000000000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919070707FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919101010000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF1716161A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF000000010100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000857D46 -F9E984F8E882F9E883E8D8791F1D10FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -080704AB9957F3D87AF3D77AF2D579F2D478F2D377F0D176F0D075F0CE75F0CD74EFCB73EEC971 -7A663A0F0D07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0202021A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A1919070606FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0504041918181A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19190C0C0C000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF171616 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF010101BCAF63F9E883 -F8E782F8E682F7E481423D22000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000 -5B502DF2D679F3D679F2D478F1D277F1D176F0D075EFCE74EFCC74EECA72EEC972EEC871EDC670 -937A4519150C000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A1919161515030202FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0101011211111A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919171616040404000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080704E4D378F8E681F7E481 -F7E380F7E38073693B000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1C190E -D7BD6BF2D378F1D176F1D076F0CE75EFCC73EECA72EEC972EDC770ECC56FECC36EECC26EEBC06C -AD8C4F261E11000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFF0000000909091312121A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919181717080808000000 -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000504041514141A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A1919191818090808000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1716161A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A1919010101FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF0807071A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A1919090808FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF13120A776E3ED0C16CF7E481F7E280 -F6E17FA29353030302FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF040302958149 -F0D076EFCE74F0CE75EFCD74EECB73EEC972EEC871EDC570ECC46FDAB36589703F3D311C0E0B06 -000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000303030808080E0E0E1515151A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919 -1A19191A19191A19191A19191A19191A1919111010060505000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000303030E0D0D1918181A19191A19191A1919 -1A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A19191A1919121212 -060606000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF080808090909090909090909090909090909090909 -090909090909090909090909090909000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFF030303090909090909090909090909090909090909090909090909090909090909090909 -030303FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0504031A180D3D371F6D6338 -7B703F050503FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000453C22EECC73 -EFCC73D9BA68AA9051736036463A21231D10080604000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000202020404040706060A0909 -0D0C0C1110101514141716161918181A19191A19191A19191A19191A19191A1919171616131212 -0D0D0D080808030303000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000000303030707070D0D0D131212181717 -1A19191A19191A19191A1919191818151414100F0F090909040404000000FFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF020201010100FFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFF000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000 -000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - -end -%%PageTrailer -%%Trailer -%%EOF diff --git a/solr/site-src/src/documentation/content/xdocs/images/solr_FC.svg b/solr/site-src/src/documentation/content/xdocs/images/solr_FC.svg deleted file mode 100644 index 813cd02ea11..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/images/solr_FC.svg +++ /dev/null @@ -1,1201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/content/xdocs/index.xml b/solr/site-src/src/documentation/content/xdocs/index.xml deleted file mode 100755 index 3d61c183621..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/index.xml +++ /dev/null @@ -1,645 +0,0 @@ - - - - -
    - Welcome to Solr -
    - -
    - What Is Solr? - -

    -Solr is the popular, blazing fast open source enterprise search -platform from the Apache Lucene project. Its major features include -powerful full-text search, hit highlighting, faceted search, dynamic -clustering, database integration, rich document (e.g., Word, PDF) -handling, and geospatial search. Solr is highly scalable, providing distributed search and -index replication, and it powers the search and navigation features of -many of the world's largest internet sites. -

    -

    -Solr is written in Java and runs as a standalone full-text search server -within a servlet container such as Tomcat. -Solr uses the Lucene Java -search library at its core for full-text indexing and search, and has -REST-like HTTP/XML and JSON APIs that make it easy to use from virtually -any programming language. Solr's powerful external configuration allows it to -be tailored to almost any type of application without Java coding, and -it has an extensive plugin architecture when more advanced -customization is required. -

    - -

    - See the complete feature list for more details. -

    - -

    - For more information about Solr, please see the Solr wiki. -

    - -
    -
    - Get Started - -
    -
    - News - -
    - 27 November 2011 - Solr 3.5.0 Released -

    - The Lucene PMC is pleased to announce the release of - Apache Solr 3.5.0! -

    -

    - See the CHANGES.txt file included with the release for a full list of details. -

    -

    Solr 3.5.0 Release Highlights:

    -
      -
    • - Bug fixes and improvements from Apache Lucene 3.5.0, including a - very substantial (3-5X) RAM reduction required to hold the terms - index on opening an IndexReader. - (LUCENE-2205) -
    • -
    • - Added support for distributed result grouping. - (SOLR-2066, - SOLR-2776) -
    • -
    • - Added support for Hunspell stemmer TokenFilter supporting - stemming for 99 languages. - (SOLR-2769) -
    • -
    • - A new contrib module "langid" adds language identification - capabilities as an Update Processor, using Tika's - LanguageIdentifier or Cybozu language-detection library - (SOLR-1979) -
    • -
    • - Numeric types including Trie and date types now support - sortMissingFirst/Last. - (SOLR-2881) -
    • -
    • - Added hl.q parameter. It is optional and if it is specified, - it overrides q parameter in Highlighter. - (SOLR-1926) -
    • -
    • - Several minor bugfixes like date parsing for years from 0001-1000, ignored - configurations when using QueryAnalyzer with - SpellCheckComponent and many more. - See CHANGES.txt entries for full details. -
    • -
    -
    -
    - 18 November 2011 - 2nd edition of the first book on Solr published! -

    Apache Solr 3 Enterprise Search Server cover - David Smiley and Eric Pugh proudly announce the second edition of the first book on Solr, - "Apache Solr 3 Enterprise Search Server" - from Packt Publishing.

    -

    Apache Solr 3 Enterprise Search Server is a comprehensive reference guide for nearly every feature has to - offer. Through using a large set of metadata about artists, releases, and tracks courtesy of the - MusicBrainz.org project, you will have a testing ground for learning Solr. You'll learn how to design a - schema, use appropriate text analysis and then how to import this data in various - ways. Next, you'll learn how to search this data, how to use advanced relevancy tuning, and how to enhance - standard search results with highlighting, faceting, query auto-complete, and other features. The book, - supported with working code examples in various languages, shows how to use a wide selection of Solr - integration client libraries, frameworks and other software like web crawlers. The book wraps up with - deployment considerations, tuning Solr performance, and scaling Solr to multiple machines. -

    -

    This edition naturally covers the latest features in Solr as of version 3.4 like Result Grouping and - Geospatial, but this is not a small update to the first book. No chapter was untouched — some were - revamped significantly and the content was expanded by about 25% by page count. Each chapter has a tip in the - introduction that advises readers in a hurry on what parts should be read now or later. Finally, it includes a - 2-page parameter quick-reference appendix that you will surely find useful printed on your desk. -

    -

    You can find further information at the publisher's site - and at the authors' site, including a free chapter and - search parameter quick-reference sheet (the appendix). -

    -
    - -
    26 October 2011 - Java 7u1 fixes index corruption and crash bugs in Apache Lucene Core and Apache Solr -

    Oracle released Java 7u1 on October 19. - According to the release notes and tests done by the Lucene committers, all bugs reported on July 28 are fixed in this release, - so code using Porter stemmer no longer crashes with SIGSEGV. We were not able to experience any index corruption anymore, - so it is safe to use Java 7u1 with Lucene Core and Solr.

    -

    On the same day, Oracle released Java 6u29 - fixing the same problems occurring with Java 6, if the JVM switches -XX:+AggressiveOpts - or -XX:+OptimizeStringConcat were used. Of course, you should not use experimental JVM options like - -XX:+AggressiveOpts in production environments! We recommend everybody to upgrade to this latest version 6u29.

    -

    In case you upgrade to Java 7, remember that you may have to reindex, as the unicode - version shipped with Java 7 changed and tokenization behaves differently - (e.g. lowercasing). For more information, read JRE_VERSION_MIGRATION.txt - in your distribution package!

    -
    - -
    - 14 September 2011 - Solr 3.4.0 Released -

    The Lucene PMC is pleased to announce the release of Apache Solr 3.4.0! -

    -

    - Solr's version number was synced with Lucene following the Lucene/Solr merge, so Solr 3.4.0 contains Lucene 3.4.0. -

    -

    - If you are already using Apache Solr 3.1, 3.2 or 3.3, we strongly recommend you upgrade to 3.4.0 because of the index corruption bug on OS or computer crash or power loss (LUCENE-3418), now fixed in 3.4.0.

    - -

    - Solr 3.4.0 release highlights include -

    -
      -
    • Bug fixes and improvements from Apache Lucene 3.4.0, including a - major bug (LUCENE-3418) whereby a Lucene index could - easily become corrupted if the OS or computer crashed or lost - power.
    • - -
    • SolrJ client can now parse grouped and range facets results - (SOLR-2523).
    • - -
    • A new XsltUpdateRequestHandler allows posting XML that's - transformed by a provided XSLT into a valid Solr document - (SOLR-2630).
    • - -
    • Post-group faceting option (group.truncate) can now compute - facet counts for only the highest ranking documents per-group. - (SOLR-2665).
    • - -
    • Add commitWithin update request parameter to all update handlers - that were previously missing it. This tells Solr to commit the - change within the specified amount of time (SOLR-2540).
    • - -
    • You can now specify NIOFSDirectory (SOLR-2670).
    • - -
    • New parameter hl.phraseLimit speeds up FastVectorHighlighter - (LUCENE-3234).
    • - -
    • The query cache and filter cache can now be disabled per request. - See this wiki page - (SOLR-2429).
    • - -
    • Improved memory usage, build time, and performance of - SynonymFilterFactory (LUCENE-3233).
    • - -
    • Added omitPositions to the schema, so you can omit position - information while still indexing term frequencies (LUCENE-2048).
    • - -
    • Various fixes for multi-threaded DataImportHandler.
    • -
    - -

    See the release notes for a more complete list of all the new features, improvements, and bugfixes. -

    - -
    - -
    28 July 2011 - WARNING: Index corruption and crashes in Apache Lucene Core / Apache Solr with Java 7 -

    Oracle released Java 7 today. - Unfortunately it contains hotspot compiler optimizations, which miscompile some loops. - This can affect code of several Apache projects. Sometimes JVMs only crash, but in several cases, - results calculated can be incorrect, leading to bugs in applications - (see Hotspot bugs 7070134, - 7044738, - 7068051).

    -

    Apache Lucene Core and Apache Solr are two Apache projects, - which are affected by these bugs, namely all versions released until today. - Solr users with the default configuration will have - Java crashing with SIGSEGV as soon as they start to index documents, as one - affected part is the well-known Porter stemmer - (see LUCENE-3335). - Other loops in Lucene may be miscompiled, too, leading to index corruption - (especially on Lucene trunk with pulsing codec; other loops may be - affected, too - LUCENE-3346).

    -

    These problems were detected only 5 days before the official Java 7 release, - so Oracle had no time to fix those bugs, affecting also many more applications. - In response to our questions, they proposed to include the fixes into service - release u2 (eventually into service release u1, see - this mail). - This means you cannot use Apache Lucene/Solr with Java 7 releases before Update 2! - If you do, please don't open bug reports, it is not the committers' fault! - At least disable loop optimizations using the -XX:-UseLoopPredicate JVM option - to not risk index corruptions.

    -

    Please note: Also Java 6 users are affected, if they use one of those - JVM options, which are not enabled by default: -XX:+OptimizeStringConcat - or -XX:+AggressiveOpts.

    -

    It is strongly recommended not to use any hotspot optimization switches in any Java - version without extensive testing!

    -

    In case you upgrade to Java 7, remember that you may have to reindex, as the unicode - version shipped with Java 7 changed and tokenization behaves differently - (e.g. lowercasing). For more information, read JRE_VERSION_MIGRATION.txt - in your distribution package!

    -
    -
    - 22 July 2011 - Solr 3.1 cookbook published! -

    Solr Cookbook coverRafał Kuć is proud to introduce a new book on Solr, "Apache Solr 3.1 Cookbook" from Packt Publishing.

    -

    The Solr 3.1 Cookbook will make your everyday work easier by using real-life examples that show you how to deal with the most common problems that can arise while using the Apache Solr search engine.

    -

    This cookbook will show you how to get the most out of your search engine. Each chapter covers a different aspect of working with Solr from analyzing your text data through querying, performance improvement, and developing your own modules. The practical recipes will help you to quickly solve common problems with data analysis, show you how to use faceting to collect data and to speed up the performance of Solr. You will learn about functionalities that most newbies are unaware of, such as sorting results by a function value, highlighting matched words, and computing statistics to make your work with Solr easy and stress free.

    -
    -
    - July 2011 - Solr 3.3 Released -

    The Lucene PMC is pleased to announce the release of Apache Solr 3.3! -

    -

    - Solr's version number was synced with Lucene following the Lucene/Solr merge, so Solr 3.3 contains Lucene 3.3. -

    -

    - Solr 3.3 release highlights include -

    -
      -
    • Grouping / Field Collapsing
    • -
    • A new, automaton-based suggest/autocomplete implementation offering an - order of magnitude smaller RAM consumption.
    • -
    • KStemFilterFactory, an optimized implementation of a less aggressive - stemmer for English.
    • -
    • Solr defaults to a new, more efficient merge policy (TieredMergePolicy). - See http://s.apache.org/merging for more information.
    • -
    • Important bugfixes, including extremely high RAM usage in spellchecking.
    • -
    • Bugfixes and improvements from Apache Lucene 3.3
    • -
    - -

    See the release notes for a more complete list of all the new features, improvements, and bugfixes. -

    - -
    -
    - May 2011 - Solr 3.2 Released -

    The Lucene PMC is pleased to announce the release of Apache Solr 3.2! -

    -

    - Solr's version number was synced with Lucene following the Lucene/Solr merge, so Solr 3.2 contains Lucene 3.2. - Solr 3.2 is the first release after Solr 3.1. -

    -

    - Solr 3.2 release highlights include -

    -
      -
    • Ability to specify overwrite and commitWithin as request parameters when using the JSON update format
    • -
    • TermQParserPlugin, useful when generating filter queries from terms returned from field faceting or the terms component.
    • -
    • DebugComponent now supports using a NamedList to model Explanation objects in it's responses instead of Explanation.toString
    • -
    • Improvements to the UIMA and Carrot2 integrations
    • -
    • Bugfixes and improvements from Apache Lucene 3.2
    • -
    - -

    See the release notes for a more complete list of all the new features, improvements, and bugfixes. -

    - -
    -
    - March 2011 - Solr 3.1 Released -

    The Lucene PMC is pleased to announce the release of Apache Solr 3.1! -

    -

    - Solr's version number was synced with Lucene following the Lucene/Solr merge, so Solr 3.1 contains Lucene 3.1. - Solr 3.1 is the first release after Solr 1.4.1. -

    -

    - Solr 3.1 release highlights include -

    - - -

    See the release notes for a more complete list of all the new features, improvements, and bugfixes. -

    - -
    -
    - 25 June 2010 - Solr 1.4.1 Released -

    - Solr 1.4.1 has been released and is now available for public - download! - - Solr 1.4.1 is a bug fix release for Solr 1.4 that includes many - Solr bug fixes as well as Lucene bug fixes from Lucene 2.9.3. -

    - -

    - See the release notes - for more details. -

    - -
    -
    - 7 May 2010 - Apache Lucene Eurocon 2010 Coming to Prague May 18-21 -

    - On May 18th to the 21st Prague will play host to the first - ever dedicated Lucene and Solr User Conference in Europe: - Apache Lucene - Eurocon 2010. - This is a a not-for-profit conference presented by Lucid - Imagination, with net proceeds being - donated to The Apache Software Foundation. - Registration - is now open. Schedule highlights include: -

    - - -
    -
    - 10 November 2009 - Solr 1.4 Released -

    - Solr 1.4 has been released and is now available for public download! - - New Solr 1.4 features include -

    -
      -
    • Major performance enhancements in indexing, searching, and faceting
    • -
    • Revamped all-Java index replication that's simple to configure and - can replicate config files
    • -
    • Greatly improved database integration via the DataImportHandler
    • -
    • Rich document processing (Word, PDF, HTML) via Apache Tika
    • -
    • Dynamic search results clustering via Carrot2
    • -
    • Multi-select faceting (support for multiple items in a single - category to be selected)
    • -
    • Many powerful query enhancements, including ranges over arbitrary - functions, nested queries of different syntaxes
    • -
    • Many other plugins including Terms for auto-suggest, Statistics, - TermVectors, Deduplication
    • -
    - -

    See the release notes for more details. -

    - -
    -
    - 20 August 2009 - Solr's first book is published! -

    - Solr book cover - David Smiley and Eric Pugh are proud to introduce the first book on Solr, "Solr 1.4 Enterprise Search Server" from Packt Publishing. -

    -

    This book is a comprehensive reference guide for nearly every feature Solr has to offer. It serves the reader right from initiation to development to deployment. It also comes with complete running examples to demonstrate its use and show how to integrate it with other languages and frameworks. -

    To keep this interesting and realistic, it uses a large open source set of metadata about artists, releases, and tracks courtesy of the MusicBrainz.org project. Using this data as a testing ground for Solr, you will learn how to import this data in various ways from CSV to XML to database access. You will then learn how to search this data in a myriad of ways, including Solr's rich query syntax, "boosting" match scores based on record data and other means, about searching across multiple fields with different boosts, getting facets on the results, auto-complete user queries, spell-correcting searches, highlighting queried text in search results, and so on. -

    After this thorough tour, you'll see working examples of integrating a variety of technologies with Solr such as Java, JavaScript, Drupal, Ruby, PHP, and Python. -

    Finally, this book covers various deployment considerations to include indexing strategies and performance-oriented configuration that will enable you to scale Solr to meet the needs of a high-volume site. -

    -
    - - -
    - 18 August 2009 - Lucene at US ApacheCon -

    - ApacheCon Logo - -ApacheCon US -is once again in the Bay Area and Lucene is coming along -for the ride! The Lucene community has planned two full days of -talks, plus a meetup and the usual bevy of training. With a -well-balanced mix of first time and veteran ApacheCon speakers, the Lucene -track at ApacheCon US promises to have something for everyone. -Be sure not to miss: -

    -

    Training:

    - -

    Thursday, Nov. 5th

    - -

    Friday, Nov. 6th

    - -
    -
    - 09 February 2009 - Lucene at ApacheCon Europe 2009 in Amsterdam -

    - ApacheCon EU 2009 Logo - - Lucene will be extremely well represented at - ApacheCon US 2009 - in Amsterdam, Netherlands this March 23-27, 2009: -

    - - -
    -
    19 December 2008 - Solr Logo Contest Results -

    Many great logos were submitted, but only one could be chosen. Congratulations Michiel, - the creator of the winning logo that is proudly displayed at the top of this page. -

    -
    -
    03 October 2008 - Solr Logo Contest -

    By popular demand, Solr is holding a contest to pick a new Solr logo. Details about how to submit an entry can be found on the wiki. The Deadline for submissions is November 20th, 2008 @ 11:59PM GMT. -

    -
    -
    15 September 2008 - Solr 1.3.0 Available -

    Solr 1.3.0 is available for public download. This version contains many enhancements and bug fixes, including distributed search capabilities, - Lucene 2.3.x performance improvements and many others. -

    -

    See the release notes for more details. Download is - available from a Apache Mirror.

    - -
    -
    - 28 August 2008 - Lucene/Solr at ApacheCon New Orleans -

    - ApacheCon US 2008 Logo - - Lucene will be extremely well represented at - ApacheCon US 2008 - in New Orleans this November 3-7, 2008: -

    - -
    - -
    - 03 September 2007 - Lucene at ApacheCon Atlanta -

    ApacheCon US logo - Lucene will once again be well represented at ApacheCon USA in Atlanta this November 12-16, 2007. -

    -

    The following talks and trainings are scheduled for this year's conference:

    - - -
    - -
    - 06 June 2007: Release 1.2 available -

    - This is the first release since Solr graduated from the Incubator, - bringing many new features, including CSV/delimited-text data - loading, time based autocommit, faster faceting, negative filters, - a spell-check handler, sounds-like word filters, regex text filters, - and more flexible plugins. -

    -

    See the release notes for more details.

    -
    - -
    - 17 January 2007: Solr graduates from Incubator -

    - Solr has graduated from the Apache Incubator, and is now a sub-project of Lucene. -

    -
    - -
    - 22 December 2006: Release 1.1.0 available -

    - This is the first release since Solr joined the Incubator, and brings - many new features and performance optimizations including highlighting, - faceted search, and JSON/Python/Ruby response formats. -

    -
    -
    - 15 August 2006: Solr at ApacheCon US -

    Chris Hostetter will be presenting - "Faceted Searching With Apache Solr" - at ApacheCon US 2006, on October 13th at 4:30pm. - See the ApacheCon website for more details. -

    -
    - -
    - 21 April 2006: Solr at ApacheCon -

    Yonik Seeley will be presenting - "Apache Solr, a Full-Text Search Server based on Lucene" - at ApacheCon Europe 2006, on June 29th at 5:30pm. - See the ApacheCon website for more details. -

    -
    - - -
    - 21 February 2006: nightly builds -

    Solr now has nightly builds. This automatically creates a - downloadable version of Solr every - night. All unit tests must pass, or a message is sent to - the developers mailing list and no new version is created. This - also updates the javadoc.

    -
    - - -
    - 17 January 2006: Solr Joins Apache Incubator -

    Solr, a search server based on Lucene, has been accepted into the Apache Incubator. - Solr was originally developed by CNET Networks, and is widely used within CNET - to provide high relevancy search and faceted browsing capabilities. -

    -
    - -
    - - -
    diff --git a/solr/site-src/src/documentation/content/xdocs/mailing_lists.xml b/solr/site-src/src/documentation/content/xdocs/mailing_lists.xml deleted file mode 100755 index f32889c5ec9..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/mailing_lists.xml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - -
    - Solr Mailing Lists -
    - - - -
    - Users - -

    If you use Solr, please subscribe to the Solr user mailing list.

    - -

    - The Solr user mailing list is : - solr-user@lucene.apache.org. -

    - - In order to post to the list, it is necessary to first subscribe to it. -
    - -
    - Developers - -

    If you'd like to contribute to Solr, please subscribe to the - Lucene/Solr developer mailing list.

    - -

    - The Lucene/Solr developer mailing list is: - dev@lucene.apache.org. -

    - - In order to post to the list, it is necessary to first subscribe to it. -
    - -
    - Commits - -

    If you'd like to see changes made in Solr's version control system - then subscribe to the Solr commit mailing list.

    - - -
    - - - -
    diff --git a/solr/site-src/src/documentation/content/xdocs/site.xml b/solr/site-src/src/documentation/content/xdocs/site.xml deleted file mode 100755 index 594c551397c..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/site.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/content/xdocs/tabs.xml b/solr/site-src/src/documentation/content/xdocs/tabs.xml deleted file mode 100755 index 8e009e36433..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/tabs.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/content/xdocs/tutorial.xml b/solr/site-src/src/documentation/content/xdocs/tutorial.xml deleted file mode 100755 index 1e7c2ace44c..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/tutorial.xml +++ /dev/null @@ -1,543 +0,0 @@ - - - - - - - -
    - Solr tutorial -
    - - - -
    -Overview - -

    -This document covers the basics of running Solr using an example -schema, and some sample data. -

    - -
    - -
    -Requirements - -

    -To follow along with this tutorial, you will need... -

    - -
      -
    1. Java 1.6 or greater. Some places you can get it are from - Oracle, - Open JDK, - IBM, or -
      - Running java -version at the command line should indicate a version - number starting with 1.6. Gnu's GCJ is not supported and does not work with Solr. -
    2. -
    3. A Solr release. -
    4. -
    -
    - -
    -Getting Started -

    -Please run the browser showing this tutorial and the Solr server on the same machine so tutorial links will correctly point to your Solr server. -

    -

    -Begin by unziping the Solr release and changing your working directory -to be the "example" directory. (Note that the base directory name may vary with the version of Solr downloaded.) For example, with a shell in UNIX, Cygwin, or MacOS: -

    - -user:~solr$ ls -solr-nightly.zip -user:~solr$ unzip -q solr-nightly.zip -user:~solr$ cd solr-nightly/example/ - - -

    -Solr can run in any Java Servlet Container of your choice, but to simplify -this tutorial, the example index includes a small installation of Jetty. -

    -

    -To launch Jetty with the Solr WAR, and the example configs, just run the start.jar ... -

    - - -user:~/solr/example$ java -jar start.jar -2009-10-23 16:42:53.816::INFO: Logging to STDERR via org.mortbay.log.StdErrLog -2009-10-23 16:42:53.907::INFO: jetty-6.1.26 - -... - -Oct 23, 2009 4:41:56 PM org.apache.solr.core.SolrCore registerSearcher -INFO: [] Registered new searcher Searcher@7c3885 main - - -

    -This will start up the Jetty application server on port 8983, and use your terminal to display the logging information from Solr. -

    -

    -You can see that the Solr is running by loading http://localhost:8983/solr/admin/ in your web browser. This is the main starting point for Administering Solr. -

    - - -
    - - - -
    -Indexing Data - -

    -Your Solr server is up and running, but it doesn't contain any data. You can -modify a Solr index by POSTing XML Documents containing instructions to add (or -update) documents, delete documents, commit pending adds and deletes, and -optimize your index. -

    -

    -The exampledocs directory contains samples of the types of -instructions Solr expects, as well as a java utility for posting them from the -command line (a post.sh shell script is also available, but for -this tutorial we'll use the cross-platform Java client).

    To try this, -open a new terminal window, enter the exampledocs directory, and run -"java -jar post.jar" on some of the XML files in that directory, -indicating the URL of the Solr server: -

    - - -user:~/solr/example/exampledocs$ java -jar post.jar solr.xml monitor.xml -SimplePostTool: version 1.2 -SimplePostTool: WARNING: Make sure your XML documents are encoded in UTF-8, other encodings are not currently supported -SimplePostTool: POSTing files to http://localhost:8983/solr/update.. -SimplePostTool: POSTing file solr.xml -SimplePostTool: POSTing file monitor.xml -SimplePostTool: COMMITting Solr index changes.. - - -

    -You have now indexed two documents in Solr, and committed these changes. -You can now search for "solr" using the "Make a Query" interface on the Admin screen, and you should get one result. -Clicking the "Search" button should take you to the following URL... -

    -

    -http://localhost:8983/solr/select/?q=solr&start=0&rows=10&indent=on -

    - -

    -You can index all of the sample data, using the following command (assuming your command line shell supports the *.xml notation): -

    - - - user:~/solr/example/exampledocs$ java -jar post.jar *.xml -SimplePostTool: version 1.2 -SimplePostTool: WARNING: Make sure your XML documents are encoded in UTF-8, other encodings are not currently supported -SimplePostTool: POSTing files to http://localhost:8983/solr/update.. -SimplePostTool: POSTing file hd.xml -SimplePostTool: POSTing file ipod_other.xml -SimplePostTool: POSTing file ipod_video.xml -SimplePostTool: POSTing file mem.xml -SimplePostTool: POSTing file monitor.xml -SimplePostTool: POSTing file monitor2.xml -SimplePostTool: POSTing file mp500.xml -SimplePostTool: POSTing file sd500.xml -SimplePostTool: POSTing file solr.xml -SimplePostTool: POSTing file spellchecker.xml -SimplePostTool: POSTing file utf8-example.xml -SimplePostTool: POSTing file vidcard.xml -SimplePostTool: COMMITting Solr index changes.. - - -

    - ...and now you can search for all sorts of things using the default Solr Query Syntax (a superset of the Lucene query syntax)... -

    - - -

    -

    - There are many other different ways to import your data into Solr... one can -

    -
      -
    • Import records from a database using the - Data Import Handler (DIH). -
    • -
    • Load a CSV file (comma separated values), - including those exported by Excel or MySQL. -
    • -
    • POST JSON documents -
    • -
    • Index binary documents such as Word and PDF with - Solr Cell (ExtractingRequestHandler). -
    • -
    • - Use SolrJ for Java or other Solr clients to - programatically create documents to send to Solr. -
    • - -
    - -
    - - - -
    -Updating Data - -

    -You may have noticed that even though the file solr.xml has now -been POSTed to the server twice, you still only get 1 result when searching for -"solr". This is because the example schema.xml specifies a "uniqueKey" field -called "id". Whenever you POST instructions to Solr to add a -document with the same value for the uniqueKey as an existing document, it -automatically replaces it for you. You can see that that has happened by -looking at the values for numDocs and maxDoc in the -"CORE"/searcher section of the statistics page...

    -

    -http://localhost:8983/solr/admin/stats.jsp -

    - -

    - numDocs represents the number of searchable documents in the - index (and will be larger than the number of XML files since some files - contained more than one <doc>). maxDoc - may be larger as the maxDoc count includes logically deleted documents that - have not yet been removed from the index. You can re-post the sample XML - files over and over again as much as you want and numDocs will never - increase, because the new documents will constantly be replacing the old. -

    -

    -Go ahead and edit the existing XML files to change some of the data, and re-run -the java -jar post.jar command, you'll see your changes reflected -in subsequent searches. -

    - -
    - Deleting Data -

    You can delete data by POSTing a delete command to the update URL and specifying the value - of the document's unique key field, or a query that matches multiple documents (be careful with that one!). Since these commands - are smaller, we will specify them right on the command line rather than reference an XML file. -

    -

    Execute the following command to delete a document

    - java -Ddata=args -Dcommit=no -jar post.jar "<delete><id>SP2514N</id></delete>" -

    Now if you go to the statistics page and scroll down - to the UPDATE_HANDLERS section and verify that "deletesById : 1"

    -

    If you search for id:SP2514N it will still be found, - because index changes are not visible until changes are committed and a new searcher is opened. To cause - this to happen, send a commit command to Solr (post.jar does this for you by default):

    - java -jar post.jar -

    Now re-execute the previous search and verify that no matching documents are found. Also revisit the - statistics page and observe the changes in both the UPDATE_HANDLERS section and the CORE section.

    -

    Here is an example of using delete-by-query to delete anything with - DDR in the name:

    - java -Ddata=args -jar post.jar "<delete><query>name:DDR</query></delete>" - -

    Commit can be an expensive operation so it's best to make many changes to an index in a batch and - then send the commit command at the end. There is also an optimize command that does the same thing as commit, - in addition to merging all index segments into a single segment, making it faster to search and causing any - deleted documents to be removed. All of the update commands are documented here. -

    - -

    To continue with the tutorial, re-add any documents you may have deleted by going to the exampledocs directory and executing

    - java -jar post.jar *.xml - -
    - -
    - -
    - Querying Data - -

    - Searches are done via HTTP GET on the select URL with the query string in the q parameter. - You can pass a number of optional request parameters - to the request handler to control what information is returned. For example, you can use the "fl" parameter - to control what stored fields are returned, and if the relevancy score is returned: -

    - - - -

    - Solr provides a query form within the web admin interface - that allows setting the various request parameters and is useful when testing or debugging queries. -

    - -
    - Sorting - -

    - Solr provides a simple method to sort on one or more indexed fields. - Use the "sort' parameter to specify "field direction" pairs, separated by commas if there's more than one sort field: -

    - - - -

    - "score" can also be used as a field name when specifying a sort: -

    - - -

    - Complex functions may also be used to sort results: -

    - - - -

    - If no sort is specified, the default is score desc to return the matches having the highest relevancy. -

    - -
    - -
    - - -
    - Highlighting -

    - Hit highlighting returns relevent snippets of each returned document, and highlights - terms from the query within those context snippets. -

    -

    - The following example searches for video card and requests - highlighting on the fields name,features. This causes a - highlighting section to be added to the response with the - words to highlight surrounded with <em> (for emphasis) - tags. -

    -

    - ...&q=video card&fl=name,id&hl=true&hl.fl=name,features -

    -

    - More request parameters related to controlling highlighting may be found - here. -

    - -
    - - -
    - Faceted Search -

    - Faceted search takes the documents matched by a query and generates counts for various - properties or categories. Links are usually provided that allows users to "drill down" or - refine their search results based on the returned categories. -

    -

    - The following example searches for all documents (*:*) and - requests counts by the category field cat. -

    -

    - ...&q=*:*&facet=true&facet.field=cat -

    -

    - Notice that although only the first 10 documents are returned in the results list, - the facet counts generated are for the complete set of documents that match the query. -

    - -

    - We can facet multiple ways at the same time. The following example adds a facet on the - boolean inStock field: -

    -

    - ...&q=*:*&facet=true&facet.field=cat&facet.field=inStock -

    - -

    - Solr can also generate counts for arbitrary queries. The following example - queries for ipod and shows prices below and above 100 by using - range queries on the price field. -

    - -

    - ...&q=ipod&facet=true&facet.query=price:[0 TO 100]&facet.query=price:[100 TO *] -

    - -

    - One can even facet by date ranges. This example requests counts for the manufacture date (manufacturedate_dt field) for each year between 2004 and 2010. -

    - -

    - ...&q=*:*&facet=true&facet.date=manufacturedate_dt&facet.date.start=2004-01-01T00:00:00Z&facet.date.end=2010-01-01T00:00:00Z&facet.date.gap=+1YEAR -

    - -

    - More information on faceted search may be found on the - faceting overview - and - faceting parameters - pages. -

    - -
    - - -
    - Search UI -

    - Solr includes an example search interface built with velocity templating - that demonstrates many features, including searching, faceting, highlighting, - autocomplete, and geospatial searching. -

    -

    - Try it out at - http://localhost:8983/solr/browse -

    -
    - - - -
    - Text Analysis - -

    - Text fields are typically indexed by breaking the text into words and applying various transformations such as - lowercasing, removing plurals, or stemming to increase relevancy. The same text transformations are normally - applied to any queries in order to match what is indexed. -

    - -

    - The schema defines - the fields in the index and what type of analysis is applied to them. The current schema your server is using - may be accessed via the [SCHEMA] link on the admin page. -

    - -

    - The best analysis components (tokenization and filtering) for your textual content depends heavily on language. - As you can see in the above [SCHEMA] link, the fields in the example schema are using a fieldType - named text_general, which has defaults appropriate for all languages. -

    - -

    - If you know your textual content is English, as is the case for the example documents in this tutorial, - and you'd like to apply English-specific stemming and stop word removal, as well as split compound words, you can use the text_en_splitting fieldType instead. - Go ahead and edit the schema.xml under the solr/example/solr/conf directory, - and change the type for fields text and features from text_general to text_en_splitting. - Restart the server and then re-post all of the documents, and then these queries will show the English-specific transformations: -

    -
      -
    • A search for - power-shot - matches PowerShot, and - adata - matches A-DATA due to the use of WordDelimiterFilter and LowerCaseFilter. -
    • - -
    • A search for - features:recharging - matches Rechargeable due to stemming with the EnglishPorterFilter. -
    • - -
    • A search for - "1 gigabyte" - matches things with GB, and the misspelled - pixima - matches Pixma due to use of a SynonymFilter. -
    • - -
    - -

    A full description of the analysis components, Analyzers, Tokenizers, and TokenFilters - available for use is here. -

    - - -
    - Analysis Debugging -

    There is a handy analysis - debugging page where you can see how a text value is broken down into words, - and shows the resulting tokens after they pass through each filter in the chain. -

    -

    - This - shows how "Canon Power-Shot SD500" would be indexed as a value in the name field. Each row of - the table shows the resulting tokens after having passed through the next TokenFilter in the analyzer for the name field. - Notice how both powershot and power, shot are indexed. Tokens generated at the same position - are shown in the same column, in this case shot and powershot. -

    -

    Selecting verbose output - will show more details, such as the name of each analyzer component in the chain, token positions, and the start and end positions - of the token in the original text. -

    -

    Selecting highlight matches - when both index and query values are provided will take the resulting terms from the query value and highlight - all matches in the index value analysis. -

    -

    Here - is an example of stemming and stop-words at work. -

    -
    -
    - -
    - Conclusion -

    - Congratulations! You successfully ran a small Solr instance, added some - documents, and made changes to the index and schema. You learned about queries, text - analysis, and the Solr admin interface. You're ready to start using Solr on - your own project! Continue on with the following steps: -

    -
      -
    • Subscribe to the Solr mailing lists!
    • -
    • Make a copy of the Solr example directory as a template for your project.
    • -
    • Customize the schema and other config in solr/conf/ to meet your needs.
    • -
    - -

    - Solr has a ton of other features that we haven't touched on here, including - distributed search - to handle huge document collections, - function queries, - numeric field statistics, - and - search results clustering. - Explore the Solr Wiki to find - more details about Solr's many features. -

    - -

    - Have Fun, and we'll see you on the Solr mailing lists! -

    - -
    - - -
    diff --git a/solr/site-src/src/documentation/content/xdocs/version_control.xml b/solr/site-src/src/documentation/content/xdocs/version_control.xml deleted file mode 100755 index a64328cd43e..00000000000 --- a/solr/site-src/src/documentation/content/xdocs/version_control.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - -
    - Solr Version Control System -
    - - - -
    - Overview -

    - The Solr source code resides in the Apache Subversion (SVN) repository. - The command-line SVN client can be obtained here or as an optional package for cygwin. - The TortoiseSVN GUI client for Windows can be obtained here. There - are also SVN plugins available for older versions of Eclipse and - IntelliJ IDEA that don't have subversion support already included. -

    -
    - -
    - Web Access (read-only) -

    - The source code can be browsed via the Web at - http://svn.apache.org/viewcvs.cgi/lucene/dev/. - No SVN client software is required. -

    -
    - -
    - Anonymous Access (read-only) -

    - The SVN URL for anonymous users is - http://svn.apache.org/repos/asf/lucene/dev/. - Instructions for anonymous SVN access are - here. -

    -
    - -
    - Committer Access (read-write) -

    - The SVN URL for committers is - https://svn.apache.org/repos/asf/lucene/dev/. - Instructions for committer SVN access are - here. -

    -
    - - - -
    diff --git a/solr/site-src/src/documentation/resources/schema/catalog.xcat b/solr/site-src/src/documentation/resources/schema/catalog.xcat deleted file mode 100644 index cc883651e93..00000000000 --- a/solr/site-src/src/documentation/resources/schema/catalog.xcat +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - diff --git a/solr/site-src/src/documentation/skinconf.xml b/solr/site-src/src/documentation/skinconf.xml deleted file mode 100755 index 419d6ffa9a5..00000000000 --- a/solr/site-src/src/documentation/skinconf.xml +++ /dev/null @@ -1,461 +0,0 @@ - - - - - - - - - - - - true - - false - - true - - true - - - true - - - true - - - true - - - false - ... - - - true - - - Solr - Solr Description - http://lucene.apache.org/solr/ - images/solr.jpg - - - - Lucene - Apache Lucene - http://lucene.apache.org/ - images/lucene_green_150.gif - - - - - - - - images/favicon.ico - - - 2007 - The Apache Software Foundation. - http://www.apache.org/licenses/ - - - - - - - - - - - - - - - - - - - - - - - p.quote { - margin-left: 2em; - padding: .5em; - background-color: #f0f0f0; - font-family: monospace; - } - img.float-right { - float: right; - margin-left: 2em; - padding: .5em; - } - - #footer a { color: #0F3660; } - #footer a:visited { color: #009999; } - - pre.code { - margin-left: 2em; - margin-right: 2em; - padding: 0.5em; - background-color: #f0f0f0; - } - - - - - - - - - - - - - - - - - - - - - - - - - - Page 1 - - - - 1in - 1in - 1.25in - 1in - - - - false - - - false - - - - - - Built with Apache Forrest - http://forrest.apache.org/ - images/built-with-forrest-button.png - 88 - 31 - - - - Book: Apache Solr 3 Enterprise Search Server - http://link.packtpub.com/2LjDxE - images/as3ess_book.jpg - 150 - 185 - - - - Book: Apache Solr 3.1 Cookbook - http://www.packtpub.com/solr-3-1-enterprise-search-server-cookbook/book - images/solr_31_cookbook.jpg - 150 - 180 - - - - LucidWorks for Solr Certified Distribution Reference Guide - http://www.lucidimagination.com/Downloads/LucidWorks-for-Solr/Reference-Guide?sc=AP - images/lucidworks_reference_guide.png - 150 - 185 - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/css/forrest.css.xslt b/solr/site-src/src/documentation/skins/common/css/forrest.css.xslt deleted file mode 100644 index fbfa020f60f..00000000000 --- a/solr/site-src/src/documentation/skins/common/css/forrest.css.xslt +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - -/* ==================== aural ============================ */ - -@media aural { - h1, h2, h3, h4, h5, h6 { voice-family: paul, male; stress: 20; richness: 90 } - h1 { pitch: x-low; pitch-range: 90 } - h2 { pitch: x-low; pitch-range: 80 } - h3 { pitch: low; pitch-range: 70 } - h4 { pitch: medium; pitch-range: 60 } - h5 { pitch: medium; pitch-range: 50 } - h6 { pitch: medium; pitch-range: 40 } - li, dt, dd { pitch: medium; richness: 60 } - dt { stress: 80 } - pre, code, tt { pitch: medium; pitch-range: 0; stress: 0; richness: 80 } - em { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } - strong { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } - dfn { pitch: high; pitch-range: 60; stress: 60 } - s, strike { richness: 0 } - i { pitch: medium; pitch-range: 60; stress: 60; richness: 50 } - b { pitch: medium; pitch-range: 60; stress: 90; richness: 90 } - u { richness: 0 } - - :link { voice-family: harry, male } - :visited { voice-family: betty, female } - :active { voice-family: betty, female; pitch-range: 80; pitch: x-high } -} - - -a.external { - padding: 0 20px 0px 0px; - display:inline; - background-repeat: no-repeat; - background-position: center right; - background-image: url(images/external-link.gif); -} - - -/* extra-css */ - - - - - diff --git a/solr/site-src/src/documentation/skins/common/images/README.txt b/solr/site-src/src/documentation/skins/common/images/README.txt deleted file mode 100644 index e0932f4a46d..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/README.txt +++ /dev/null @@ -1 +0,0 @@ -The images in this directory are used if the current skin lacks them. diff --git a/solr/site-src/src/documentation/skins/common/images/corner-imports.svg.xslt b/solr/site-src/src/documentation/skins/common/images/corner-imports.svg.xslt deleted file mode 100644 index 95b58d26f3c..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/corner-imports.svg.xslt +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - 0 - - - - - - fill:; - - - fill:; - - - stroke:; - - - - - - - - - 1 - -1 - - - - - 1 - -1 - - - - - 0 - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/images/dc.svg.xslt b/solr/site-src/src/documentation/skins/common/images/dc.svg.xslt deleted file mode 100644 index f900f2f9549..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/dc.svg.xslt +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/images/poddoc.svg.xslt b/solr/site-src/src/documentation/skins/common/images/poddoc.svg.xslt deleted file mode 100644 index 6097d2b357d..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/poddoc.svg.xslt +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - POD - - - diff --git a/solr/site-src/src/documentation/skins/common/images/rc.svg.xslt b/solr/site-src/src/documentation/skins/common/images/rc.svg.xslt deleted file mode 100644 index d8924fec1b4..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/rc.svg.xslt +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/images/txtdoc.svg.xslt b/solr/site-src/src/documentation/skins/common/images/txtdoc.svg.xslt deleted file mode 100644 index d85715c750d..00000000000 --- a/solr/site-src/src/documentation/skins/common/images/txtdoc.svg.xslt +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - TXT - - - diff --git a/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs-optimized.js b/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs-optimized.js deleted file mode 100644 index 507612af872..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs-optimized.js +++ /dev/null @@ -1,90 +0,0 @@ -/* -* 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. -*/ -var PREPREND_CRUMBS=new Array(); -var link1="@skinconfig.trail.link1.name@"; -var link2="@skinconfig.trail.link2.name@"; -var link3="@skinconfig.trail.link3.name@"; -if(!(link1=="")&&!link1.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link1, @skinconfig.trail.link1.href@ ) ); } -if(!(link2=="")&&!link2.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link2, @skinconfig.trail.link2.href@ ) ); } -if(!(link3=="")&&!link3.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link3, @skinconfig.trail.link3.href@ ) ); } -var DISPLAY_SEPARATOR=" > "; -var DISPLAY_PREPREND=" > "; -var DISPLAY_POSTPREND=":"; -var CSS_CLASS_CRUMB="breadcrumb"; -var CSS_CLASS_TRAIL="breadcrumbTrail"; -var CSS_CLASS_SEPARATOR="crumbSeparator"; -var FILE_EXTENSIONS=new Array( ".html", ".htm", ".jsp", ".php", ".php3", ".php4" ); -var PATH_SEPARATOR="/"; - -function sc(s) { - var l=s.toLowerCase(); - return l.substr(0,1).toUpperCase()+l.substr(1); -} -function getdirs() { - var t=document.location.pathname.split(PATH_SEPARATOR); - var lc=t[t.length-1]; - for(var i=0;i < FILE_EXTENSIONS.length;i++) - { - if(lc.indexOf(FILE_EXTENSIONS[i])) - return t.slice(1,t.length-1); } - return t.slice(1,t.length); -} -function getcrumbs( d ) -{ - var pre = "/"; - var post = "/"; - var c = new Array(); - if( d != null ) - { - for(var i=0;i < d.length;i++) { - pre+=d[i]+postfix; - c.push(new Array(d[i],pre)); } - } - if(PREPREND_CRUMBS.length > 0 ) - return PREPREND_CRUMBS.concat( c ); - return c; -} -function gettrail( c ) -{ - var h=DISPLAY_PREPREND; - for(var i=0;i < c.length;i++) - { - h+=''+sc(c[i][0])+''; - if(i!=(c.length-1)) - h+=DISPLAY_SEPARATOR; } - return h+DISPLAY_POSTPREND; -} - -function gettrailXHTML( c ) -{ - var h=''+DISPLAY_PREPREND; - for(var i=0;i < c.length;i++) - { - h+=''+sc(c[i][0])+''; - if(i!=(c.length-1)) - h+=''+DISPLAY_SEPARATOR+''; } - return h+DISPLAY_POSTPREND+''; -} - -if(document.location.href.toLowerCase().indexOf("http://")==-1) - document.write(gettrail(getcrumbs())); -else - document.write(gettrail(getcrumbs(getdirs()))); - diff --git a/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs.js b/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs.js deleted file mode 100644 index aea80ec045b..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/breadcrumbs.js +++ /dev/null @@ -1,237 +0,0 @@ -/* -* 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. -*/ -/** - * This script, when included in a html file, builds a neat breadcrumb trail - * based on its url. That is, if it doesn't contains bugs (I'm relatively - * sure it does). - * - * Typical usage: - * - */ - -/** - * IE 5 on Mac doesn't know Array.push. - * - * Implement it - courtesy to fritz. - */ -var abc = new Array(); -if (!abc.push) { - Array.prototype.push = function(what){this[this.length]=what} -} - -/* ======================================================================== - CONSTANTS - ======================================================================== */ - -/** - * Two-dimensional array containing extra crumbs to place at the front of - * the trail. Specify first the name of the crumb, then the URI that belongs - * to it. You'll need to modify this for every domain or subdomain where - * you use this script (you can leave it as an empty array if you wish) - */ -var PREPREND_CRUMBS = new Array(); - -var link1 = "@skinconfig.trail.link1.name@"; -var link2 = "@skinconfig.trail.link2.name@"; -var link3 = "@skinconfig.trail.link3.name@"; - -var href1 = "@skinconfig.trail.link1.href@"; -var href2 = "@skinconfig.trail.link2.href@"; -var href3 = "@skinconfig.trail.link3.href@"; - - if(!(link1=="")&&!link1.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link1, href1 ) ); - } - if(!(link2=="")&&!link2.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link2, href2 ) ); - } - if(!(link3=="")&&!link3.indexOf( "@" ) == 0){ - PREPREND_CRUMBS.push( new Array( link3, href3 ) ); - } - -/** - * String to include between crumbs: - */ -var DISPLAY_SEPARATOR = " > "; -/** - * String to include at the beginning of the trail - */ -var DISPLAY_PREPREND = " > "; -/** - * String to include at the end of the trail - */ -var DISPLAY_POSTPREND = ""; - -/** - * CSS Class to use for a single crumb: - */ -var CSS_CLASS_CRUMB = "breadcrumb"; - -/** - * CSS Class to use for the complete trail: - */ -var CSS_CLASS_TRAIL = "breadcrumbTrail"; - -/** - * CSS Class to use for crumb separator: - */ -var CSS_CLASS_SEPARATOR = "crumbSeparator"; - -/** - * Array of strings containing common file extensions. We use this to - * determine what part of the url to ignore (if it contains one of the - * string specified here, we ignore it). - */ -var FILE_EXTENSIONS = new Array( ".html", ".htm", ".jsp", ".php", ".php3", ".php4" ); - -/** - * String that separates parts of the breadcrumb trail from each other. - * When this is no longer a slash, I'm sure I'll be old and grey. - */ -var PATH_SEPARATOR = "/"; - -/* ======================================================================== - UTILITY FUNCTIONS - ======================================================================== */ -/** - * Capitalize first letter of the provided string and return the modified - * string. - */ -function sentenceCase( string ) -{ return string; - //var lower = string.toLowerCase(); - //return lower.substr(0,1).toUpperCase() + lower.substr(1); -} - -/** - * Returns an array containing the names of all the directories in the - * current document URL - */ -function getDirectoriesInURL() -{ - var trail = document.location.pathname.split( PATH_SEPARATOR ); - - // check whether last section is a file or a directory - var lastcrumb = trail[trail.length-1]; - for( var i = 0; i < FILE_EXTENSIONS.length; i++ ) - { - if( lastcrumb.indexOf( FILE_EXTENSIONS[i] ) ) - { - // it is, remove it and send results - return trail.slice( 1, trail.length-1 ); - } - } - - // it's not; send the trail unmodified - return trail.slice( 1, trail.length ); -} - -/* ======================================================================== - BREADCRUMB FUNCTIONALITY - ======================================================================== */ -/** - * Return a two-dimensional array describing the breadcrumbs based on the - * array of directories passed in. - */ -function getBreadcrumbs( dirs ) -{ - var prefix = "/"; - var postfix = "/"; - - // the array we will return - var crumbs = new Array(); - - if( dirs != null ) - { - for( var i = 0; i < dirs.length; i++ ) - { - prefix += dirs[i] + postfix; - crumbs.push( new Array( dirs[i], prefix ) ); - } - } - - // preprend the PREPREND_CRUMBS - if(PREPREND_CRUMBS.length > 0 ) - { - return PREPREND_CRUMBS.concat( crumbs ); - } - - return crumbs; -} - -/** - * Return a string containing a simple text breadcrumb trail based on the - * two-dimensional array passed in. - */ -function getCrumbTrail( crumbs ) -{ - var xhtml = DISPLAY_PREPREND; - - for( var i = 0; i < crumbs.length; i++ ) - { - xhtml += ''; - xhtml += unescape( crumbs[i][0] ) + ''; - if( i != (crumbs.length-1) ) - { - xhtml += DISPLAY_SEPARATOR; - } - } - - xhtml += DISPLAY_POSTPREND; - - return xhtml; -} - -/** - * Return a string containing an XHTML breadcrumb trail based on the - * two-dimensional array passed in. - */ -function getCrumbTrailXHTML( crumbs ) -{ - var xhtml = ''; - xhtml += DISPLAY_PREPREND; - - for( var i = 0; i < crumbs.length; i++ ) - { - xhtml += ''; - xhtml += unescape( crumbs[i][0] ) + ''; - if( i != (crumbs.length-1) ) - { - xhtml += '' + DISPLAY_SEPARATOR + ''; - } - } - - xhtml += DISPLAY_POSTPREND; - xhtml += ''; - - return xhtml; -} - -/* ======================================================================== - PRINT BREADCRUMB TRAIL - ======================================================================== */ - -// check if we're local; if so, only print the PREPREND_CRUMBS -if( document.location.href.toLowerCase().indexOf( "http://" ) == -1 ) -{ - document.write( getCrumbTrail( getBreadcrumbs() ) ); -} -else -{ - document.write( getCrumbTrail( getBreadcrumbs( getDirectoriesInURL() ) ) ); -} - diff --git a/solr/site-src/src/documentation/skins/common/scripts/fontsize.js b/solr/site-src/src/documentation/skins/common/scripts/fontsize.js deleted file mode 100644 index 11722bfc52f..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/fontsize.js +++ /dev/null @@ -1,166 +0,0 @@ -/* -* 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. -*/ -function init() -{ //embedded in the doc - //ndeSetTextSize(); -} - -function checkBrowser(){ - if (!document.getElementsByTagName){ - return true; - } - else{ - return false; - } -} - - -function ndeSetTextSize(chgsize,rs) -{ - var startSize; - var newSize; - - if (!checkBrowser) - { - return; - } - - startSize = parseInt(ndeGetDocTextSize()); - - if (!startSize) - { - startSize = 16; - } - - switch (chgsize) - { - case 'incr': - newSize = startSize + 2; - break; - - case 'decr': - newSize = startSize - 2; - break; - - case 'reset': - if (rs) {newSize = rs;} else {newSize = 16;} - break; - - default: - try{ - newSize = parseInt(ndeReadCookie("nde-textsize")); - } - catch(e){ - alert(e); - } - - if (!newSize || newSize == 'NaN') - { - newSize = startSize; - } - break; - - } - - if (newSize < 10) - { - newSize = 10; - } - - newSize += 'px'; - - document.getElementsByTagName('html')[0].style.fontSize = newSize; - document.getElementsByTagName('body')[0].style.fontSize = newSize; - - ndeCreateCookie("nde-textsize", newSize, 365); -} - -function ndeGetDocTextSize() -{ - if (!checkBrowser) - { - return 0; - } - - var size = 0; - var body = document.getElementsByTagName('body')[0]; - - if (body.style && body.style.fontSize) - { - size = body.style.fontSize; - } - else if (typeof(getComputedStyle) != 'undefined') - { - size = getComputedStyle(body,'').getPropertyValue('font-size'); - } - else if (body.currentStyle) - { - size = body.currentStyle.fontSize; - } - - //fix IE bug - if( isNaN(size)){ - if(size.substring(size.length-1)=="%"){ - return - } - - } - - return size; - -} - - - -function ndeCreateCookie(name,value,days) -{ - var cookie = name + "=" + value + ";"; - - if (days) - { - var date = new Date(); - date.setTime(date.getTime()+(days*24*60*60*1000)); - cookie += " expires=" + date.toGMTString() + ";"; - } - cookie += " path=/"; - - document.cookie = cookie; - -} - -function ndeReadCookie(name) -{ - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - - - for(var i = 0; i < ca.length; i++) - { - var c = ca[i]; - while (c.charAt(0) == ' ') - { - c = c.substring(1, c.length); - } - - ctest = c.substring(0,name.length); - - if(ctest == name){ - return c.substring(nameEQ.length,c.length); - } - } - return null; -} diff --git a/solr/site-src/src/documentation/skins/common/scripts/getBlank.js b/solr/site-src/src/documentation/skins/common/scripts/getBlank.js deleted file mode 100644 index d9978c0b3e6..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/getBlank.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -* 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. -*/ -/** - * getBlank script - when included in a html file and called from a form text field, will set the value of this field to "" - * if the text value is still the standard value. - * getPrompt script - when included in a html file and called from a form text field, will set the value of this field to the prompt - * if the text value is empty. - * - * Typical usage: - * - * - */ - diff --git a/solr/site-src/src/documentation/skins/common/scripts/getMenu.js b/solr/site-src/src/documentation/skins/common/scripts/getMenu.js deleted file mode 100644 index b17aad61858..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/getMenu.js +++ /dev/null @@ -1,45 +0,0 @@ -/* -* 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. -*/ -/** - * This script, when included in a html file, can be used to make collapsible menus - * - * Typical usage: - * - */ - -if (document.getElementById){ - document.write('') -} - - -function SwitchMenu(obj, thePath) -{ -var open = 'url("'+thePath + 'images/chapter_open.gif")'; -var close = 'url("'+thePath + 'images/chapter.gif")'; - if(document.getElementById) { - var el = document.getElementById(obj); - var title = document.getElementById(obj+'Title'); - - if(el.style.display != "block"){ - title.style.backgroundImage = open; - el.style.display = "block"; - }else{ - title.style.backgroundImage = close; - el.style.display = "none"; - } - }// end - if(document.getElementById) -}//end - function SwitchMenu(obj) diff --git a/solr/site-src/src/documentation/skins/common/scripts/menu.js b/solr/site-src/src/documentation/skins/common/scripts/menu.js deleted file mode 100644 index 06ea471dc57..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/menu.js +++ /dev/null @@ -1,48 +0,0 @@ -/* -* 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. -*/ -/** - * This script, when included in a html file, can be used to make collapsible menus - * - * Typical usage: - * - */ - -if (document.getElementById){ - document.write('') -} - -function SwitchMenu(obj) -{ - if(document.getElementById) { - var el = document.getElementById(obj); - var title = document.getElementById(obj+'Title'); - - if(obj.indexOf("_selected_")==0&&el.style.display == ""){ - el.style.display = "block"; - title.className = "pagegroupselected"; - } - - if(el.style.display != "block"){ - el.style.display = "block"; - title.className = "pagegroupopen"; - } - else{ - el.style.display = "none"; - title.className = "pagegroup"; - } - }// end - if(document.getElementById) -}//end - function SwitchMenu(obj) diff --git a/solr/site-src/src/documentation/skins/common/scripts/prototype.js b/solr/site-src/src/documentation/skins/common/scripts/prototype.js deleted file mode 100644 index ed7d920cb5f..00000000000 --- a/solr/site-src/src/documentation/skins/common/scripts/prototype.js +++ /dev/null @@ -1,1257 +0,0 @@ -/* Prototype JavaScript framework, version 1.4.0_pre4 - * (c) 2005 Sam Stephenson - * - * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff - * against the source tree, available from the Prototype darcs repository. - * - * Prototype is freely distributable under the terms of an MIT-style license. - * - * For details, see the Prototype web site: http://prototype.conio.net/ - * -/*--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.4.0_pre4', - - emptyFunction: function() {}, - K: function(x) {return x} -} - -var Class = { - create: function() { - return function() { - this.initialize.apply(this, arguments); - } - } -} - -var Abstract = new Object(); - -Object.extend = function(destination, source) { - for (property in source) { - destination[property] = source[property]; - } - return destination; -} - -Function.prototype.bind = function(object) { - var __method = this; - return function() { - return __method.apply(object, arguments); - } -} - -Function.prototype.bindAsEventListener = function(object) { - var __method = this; - return function(event) { - return __method.call(object, event || window.event); - } -} - -Number.prototype.toColorPart = function() { - var digits = this.toString(16); - if (this < 16) return '0' + digits; - return digits; -} - -var Try = { - these: function() { - var returnValue; - - for (var i = 0; i < arguments.length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) {} - } - - return returnValue; - } -} - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create(); -PeriodicalExecuter.prototype = { - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.callback(); - } finally { - this.currentlyExecuting = false; - } - } - } -} - -/*--------------------------------------------------------------------------*/ - -function $() { - var elements = new Array(); - - for (var i = 0; i < arguments.length; i++) { - var element = arguments[i]; - if (typeof element == 'string') - element = document.getElementById(element); - - if (arguments.length == 1) - return element; - - elements.push(element); - } - - return elements; -} - -if (!Array.prototype.push) { - Array.prototype.push = function() { - var startLength = this.length; - for (var i = 0; i < arguments.length; i++) - this[startLength + i] = arguments[i]; - return this.length; - } -} - -if (!Function.prototype.apply) { - // Based on code from http://www.youngpup.net/ - Function.prototype.apply = function(object, parameters) { - var parameterStrings = new Array(); - if (!object) object = window; - if (!parameters) parameters = new Array(); - - for (var i = 0; i < parameters.length; i++) - parameterStrings[i] = 'parameters[' + i + ']'; - - object.__apply__ = this; - var result = eval('object.__apply__(' + - parameterStrings.join(', ') + ')'); - object.__apply__ = null; - - return result; - } -} - -Object.extend(String.prototype, { - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - escapeHTML: function() { - var div = document.createElement('div'); - var text = document.createTextNode(this); - div.appendChild(text); - return div.innerHTML; - }, - - unescapeHTML: function() { - var div = document.createElement('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0].nodeValue; - }, - - parseQuery: function() { - var str = this; - if (str.substring(0,1) == '?') { - str = this.substring(1); - } - var result = {}; - var pairs = str.split('&'); - for (var i = 0; i < pairs.length; i++) { - var pair = pairs[i].split('='); - result[pair[0]] = pair[1]; - } - return result; - } -}); - - -var _break = new Object(); -var _continue = new Object(); - -var Enumerable = { - each: function(iterator) { - var index = 0; - try { - this._each(function(value) { - try { - iterator(value, index++); - } catch (e) { - if (e != _continue) throw e; - } - }); - } catch (e) { - if (e != _break) throw e; - } - }, - - all: function(iterator) { - var result = true; - this.each(function(value, index) { - if (!(result &= (iterator || Prototype.K)(value, index))) - throw _break; - }); - return result; - }, - - any: function(iterator) { - var result = true; - this.each(function(value, index) { - if (result &= (iterator || Prototype.K)(value, index)) - throw _break; - }); - return result; - }, - - collect: function(iterator) { - var results = []; - this.each(function(value, index) { - results.push(iterator(value, index)); - }); - return results; - }, - - detect: function (iterator) { - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw _break; - } - }); - return result; - }, - - findAll: function(iterator) { - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(pattern, iterator) { - var results = []; - this.each(function(value, index) { - var stringValue = value.toString(); - if (stringValue.match(pattern)) - results.push((iterator || Prototype.K)(value, index)); - }) - return results; - }, - - include: function(object) { - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw _break; - } - }); - return found; - }, - - inject: function(memo, iterator) { - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.collect(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (value >= (result || value)) - result = value; - }); - return result; - }, - - min: function(iterator) { - var result; - this.each(function(value, index) { - value = (iterator || Prototype.K)(value, index); - if (value <= (result || value)) - result = value; - }); - return result; - }, - - partition: function(iterator) { - var trues = [], falses = []; - this.each(function(value, index) { - ((iterator || Prototype.K)(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value, index) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator) { - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator) { - return this.collect(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.collect(Prototype.K); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (typeof args.last() == 'function') - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - iterator(value = collections.pluck(index)); - return value; - }); - } -} - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray -}); - -$A = Array.from = function(iterable) { - var results = []; - for (var i = 0; i < iterable.length; i++) - results.push(iterable[i]); - return results; -} - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0; i < this.length; i++) - iterator(this[i]); - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - } -}); - -Object.extend(Array.prototype, Enumerable); - - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')}, - function() {return new XMLHttpRequest()} - ) || false; - } -} - -Ajax.Base = function() {}; -Ajax.Base.prototype = { - setOptions: function(options) { - this.options = { - method: 'post', - asynchronous: true, - parameters: '' - } - Object.extend(this.options, options || {}); - }, - - responseIsSuccess: function() { - return this.transport.status == undefined - || this.transport.status == 0 - || (this.transport.status >= 200 && this.transport.status < 300); - }, - - responseIsFailure: function() { - return !this.responseIsSuccess(); - } -} - -Ajax.Request = Class.create(); -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Request.prototype = Object.extend(new Ajax.Base(), { - initialize: function(url, options) { - this.transport = Ajax.getTransport(); - this.setOptions(options); - this.request(url); - }, - - request: function(url) { - var parameters = this.options.parameters || ''; - if (parameters.length > 0) parameters += '&_='; - - try { - if (this.options.method == 'get') - url += '?' + parameters; - - this.transport.open(this.options.method, url, - this.options.asynchronous); - - if (this.options.asynchronous) { - this.transport.onreadystatechange = this.onStateChange.bind(this); - setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10); - } - - this.setRequestHeaders(); - - var body = this.options.postBody ? this.options.postBody : parameters; - this.transport.send(this.options.method == 'post' ? body : null); - - } catch (e) { - } - }, - - setRequestHeaders: function() { - var requestHeaders = - ['X-Requested-With', 'XMLHttpRequest', - 'X-Prototype-Version', Prototype.Version]; - - if (this.options.method == 'post') { - requestHeaders.push('Content-type', - 'application/x-www-form-urlencoded'); - - /* Force "Connection: close" for Mozilla browsers to work around - * a bug where XMLHttpReqeuest sends an incorrect Content-length - * header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType) - requestHeaders.push('Connection', 'close'); - } - - if (this.options.requestHeaders) - requestHeaders.push.apply(requestHeaders, this.options.requestHeaders); - - for (var i = 0; i < requestHeaders.length; i += 2) - this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]); - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState != 1) - this.respondToReadyState(this.transport.readyState); - }, - - respondToReadyState: function(readyState) { - var event = Ajax.Request.Events[readyState]; - - if (event == 'Complete') - (this.options['on' + this.transport.status] - || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(this.transport); - - (this.options['on' + event] || Prototype.emptyFunction)(this.transport); - - /* Avoid memory leak in MSIE: clean up the oncomplete event handler */ - if (event == 'Complete') - this.transport.onreadystatechange = Prototype.emptyFunction; - } -}); - -Ajax.Updater = Class.create(); -Ajax.Updater.ScriptFragment = '(?:)((\n|.)*?)(?:<\/script>)'; - -Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { - initialize: function(container, url, options) { - this.containers = { - success: container.success ? $(container.success) : $(container), - failure: container.failure ? $(container.failure) : - (container.success ? null : $(container)) - } - - this.transport = Ajax.getTransport(); - this.setOptions(options); - - var onComplete = this.options.onComplete || Prototype.emptyFunction; - this.options.onComplete = (function() { - this.updateContent(); - onComplete(this.transport); - }).bind(this); - - this.request(url); - }, - - updateContent: function() { - var receiver = this.responseIsSuccess() ? - this.containers.success : this.containers.failure; - - var match = new RegExp(Ajax.Updater.ScriptFragment, 'img'); - var response = this.transport.responseText.replace(match, ''); - var scripts = this.transport.responseText.match(match); - - if (receiver) { - if (this.options.insertion) { - new this.options.insertion(receiver, response); - } else { - receiver.innerHTML = response; - } - } - - if (this.responseIsSuccess()) { - if (this.onComplete) - setTimeout((function() {this.onComplete( - this.transport)}).bind(this), 10); - } - - if (this.options.evalScripts && scripts) { - match = new RegExp(Ajax.Updater.ScriptFragment, 'im'); - setTimeout((function() { - for (var i = 0; i < scripts.length; i++) - eval(scripts[i].match(match)[1]); - }).bind(this), 10); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(); -Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { - initialize: function(container, url, options) { - this.setOptions(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = 1; - - this.updater = {}; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Ajax.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(request) { - if (this.options.decay) { - this.decay = (request.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = request.responseText; - } - this.timer = setTimeout(this.onTimerEvent.bind(this), - this.decay * this.frequency * 1000); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); - -document.getElementsByClassName = function(className) { - var children = document.getElementsByTagName('*') || document.all; - var elements = new Array(); - - for (var i = 0; i < children.length; i++) { - var child = children[i]; - var classNames = child.className.split(' '); - for (var j = 0; j < classNames.length; j++) { - if (classNames[j] == className) { - elements.push(child); - break; - } - } - } - - return elements; -} - -/*--------------------------------------------------------------------------*/ - -if (!window.Element) { - var Element = new Object(); -} - -Object.extend(Element, { - toggle: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = - (element.style.display == 'none' ? '' : 'none'); - } - }, - - hide: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = 'none'; - } - }, - - show: function() { - for (var i = 0; i < arguments.length; i++) { - var element = $(arguments[i]); - element.style.display = ''; - } - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - }, - - getHeight: function(element) { - element = $(element); - return element.offsetHeight; - }, - - hasClassName: function(element, className) { - element = $(element); - if (!element) - return; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] == className) - return true; - } - return false; - }, - - addClassName: function(element, className) { - element = $(element); - Element.removeClassName(element, className); - element.className += ' ' + className; - }, - - removeClassName: function(element, className) { - element = $(element); - if (!element) - return; - var newClassName = ''; - var a = element.className.split(' '); - for (var i = 0; i < a.length; i++) { - if (a[i] != className) { - if (i > 0) - newClassName += ' '; - newClassName += a[i]; - } - } - element.className = newClassName; - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - var element = $(element); - for (var i = 0; i < element.childNodes.length; i++) { - var node = element.childNodes[i]; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - Element.remove(node); - } - } -}); - -var Toggle = new Object(); -Toggle.display = Element.toggle; - -/*--------------------------------------------------------------------------*/ - -Abstract.Insertion = function(adjacency) { - this.adjacency = adjacency; -} - -Abstract.Insertion.prototype = { - initialize: function(element, content) { - this.element = $(element); - this.content = content; - - if (this.adjacency && this.element.insertAdjacentHTML) { - this.element.insertAdjacentHTML(this.adjacency, this.content); - } else { - this.range = this.element.ownerDocument.createRange(); - if (this.initializeRange) this.initializeRange(); - this.fragment = this.range.createContextualFragment(this.content); - this.insertContent(); - } - } -} - -var Insertion = new Object(); - -Insertion.Before = Class.create(); -Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { - initializeRange: function() { - this.range.setStartBefore(this.element); - }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, this.element); - } -}); - -Insertion.Top = Class.create(); -Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(true); - }, - - insertContent: function() { - this.element.insertBefore(this.fragment, this.element.firstChild); - } -}); - -Insertion.Bottom = Class.create(); -Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { - initializeRange: function() { - this.range.selectNodeContents(this.element); - this.range.collapse(this.element); - }, - - insertContent: function() { - this.element.appendChild(this.fragment); - } -}); - -Insertion.After = Class.create(); -Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { - initializeRange: function() { - this.range.setStartAfter(this.element); - }, - - insertContent: function() { - this.element.parentNode.insertBefore(this.fragment, - this.element.nextSibling); - } -}); - -var Field = { - clear: function() { - for (var i = 0; i < arguments.length; i++) - $(arguments[i]).value = ''; - }, - - focus: function(element) { - $(element).focus(); - }, - - present: function() { - for (var i = 0; i < arguments.length; i++) - if ($(arguments[i]).value == '') return false; - return true; - }, - - select: function(element) { - $(element).select(); - }, - - activate: function(element) { - $(element).focus(); - $(element).select(); - } -} - -/*--------------------------------------------------------------------------*/ - -var Form = { - serialize: function(form) { - var elements = Form.getElements($(form)); - var queryComponents = new Array(); - - for (var i = 0; i < elements.length; i++) { - var queryComponent = Form.Element.serialize(elements[i]); - if (queryComponent) - queryComponents.push(queryComponent); - } - - return queryComponents.join('&'); - }, - - getElements: function(form) { - var form = $(form); - var elements = new Array(); - - for (tagName in Form.Element.Serializers) { - var tagElements = form.getElementsByTagName(tagName); - for (var j = 0; j < tagElements.length; j++) - elements.push(tagElements[j]); - } - return elements; - }, - - getInputs: function(form, typeName, name) { - var form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) - return inputs; - - var matchingInputs = new Array(); - for (var i = 0; i < inputs.length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || - (name && input.name != name)) - continue; - matchingInputs.push(input); - } - - return matchingInputs; - }, - - disable: function(form) { - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - element.blur(); - element.disabled = 'true'; - } - }, - - enable: function(form) { - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - element.disabled = ''; - } - }, - - focusFirstElement: function(form) { - var form = $(form); - var elements = Form.getElements(form); - for (var i = 0; i < elements.length; i++) { - var element = elements[i]; - if (element.type != 'hidden' && !element.disabled) { - Field.activate(element); - break; - } - } - }, - - reset: function(form) { - $(form).reset(); - } -} - -Form.Element = { - serialize: function(element) { - var element = $(element); - var method = element.tagName.toLowerCase(); - var parameter = Form.Element.Serializers[method](element); - - if (parameter) - return encodeURIComponent(parameter[0]) + '=' + - encodeURIComponent(parameter[1]); - }, - - getValue: function(element) { - var element = $(element); - var method = element.tagName.toLowerCase(); - var parameter = Form.Element.Serializers[method](element); - - if (parameter) - return parameter[1]; - } -} - -Form.Element.Serializers = { - input: function(element) { - switch (element.type.toLowerCase()) { - case 'submit': - case 'hidden': - case 'password': - case 'text': - return Form.Element.Serializers.textarea(element); - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element); - } - return false; - }, - - inputSelector: function(element) { - if (element.checked) - return [element.name, element.value]; - }, - - textarea: function(element) { - return [element.name, element.value]; - }, - - select: function(element) { - var value = ''; - if (element.type == 'select-one') { - var index = element.selectedIndex; - if (index >= 0) - value = element.options[index].value || element.options[index].text; - } else { - value = new Array(); - for (var i = 0; i < element.length; i++) { - var opt = element.options[i]; - if (opt.selected) - value.push(opt.value || opt.text); - } - } - return [element.name, value]; - } -} - -/*--------------------------------------------------------------------------*/ - -var $F = Form.Element.getValue; - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = function() {} -Abstract.TimedObserver.prototype = { - initialize: function(element, frequency, callback) { - this.frequency = frequency; - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - this.registerCallback(); - }, - - registerCallback: function() { - setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - onTimerEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - } -} - -Form.Element.Observer = Class.create(); -Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(); -Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = function() {} -Abstract.EventObserver.prototype = { - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - var elements = Form.getElements(this.element); - for (var i = 0; i < elements.length; i++) - this.registerCallback(elements[i]); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - element.target = this; - element.prev_onclick = element.onclick || Prototype.emptyFunction; - element.onclick = function() { - this.prev_onclick(); - this.target.onElementEvent(); - } - break; - case 'password': - case 'text': - case 'textarea': - case 'select-one': - case 'select-multiple': - element.target = this; - element.prev_onchange = element.onchange || Prototype.emptyFunction; - element.onchange = function() { - this.prev_onchange(); - this.target.onElementEvent(); - } - break; - } - } - } -} - -Form.Element.EventObserver = Class.create(); -Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(); -Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { - getValue: function() { - return Form.serialize(this.element); - } -}); - - -if (!window.Event) { - var Event = new Object(); -} - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - - element: function(event) { - return event.target || event.srcElement; - }, - - isLeftClick: function(event) { - return (((event.which) && (event.which == 1)) || - ((event.button) && (event.button == 1))); - }, - - pointerX: function(event) { - return event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)); - }, - - pointerY: function(event) { - return event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)); - }, - - stop: function(event) { - if (event.preventDefault) { - event.preventDefault(); - event.stopPropagation(); - } else { - event.returnValue = false; - } - }, - - // find the first node with the given tagName, starting from the - // node the event was triggered on; traverses the DOM upwards - findElement: function(event, tagName) { - var element = Event.element(event); - while (element.parentNode && (!element.tagName || - (element.tagName.toUpperCase() != tagName.toUpperCase()))) - element = element.parentNode; - return element; - }, - - observers: false, - - _observeAndCache: function(element, name, observer, useCapture) { - if (!this.observers) this.observers = []; - if (element.addEventListener) { - this.observers.push([element, name, observer, useCapture]); - element.addEventListener(name, observer, useCapture); - } else if (element.attachEvent) { - this.observers.push([element, name, observer, useCapture]); - element.attachEvent('on' + name, observer); - } - }, - - unloadCache: function() { - if (!Event.observers) return; - for (var i = 0; i < Event.observers.length; i++) { - Event.stopObserving.apply(this, Event.observers[i]); - Event.observers[i][0] = null; - } - Event.observers = false; - }, - - observe: function(element, name, observer, useCapture) { - var element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - || element.attachEvent)) - name = 'keydown'; - - this._observeAndCache(element, name, observer, useCapture); - }, - - stopObserving: function(element, name, observer, useCapture) { - var element = $(element); - useCapture = useCapture || false; - - if (name == 'keypress' && - ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - || element.detachEvent)) - name = 'keydown'; - - if (element.removeEventListener) { - element.removeEventListener(name, observer, useCapture); - } else if (element.detachEvent) { - element.detachEvent('on' + name, observer); - } - } -}); - -/* prevent memory leaks in IE */ -Event.observe(window, 'unload', Event.unloadCache, false); - -var Position = { - - // set to true if needed, warning: firefox performance problems - // NOT neeeded for page scrolling, only if draggable contained in - // scrollable elements - includeScrollOffsets: false, - - // must be called before calling withinIncludingScrolloffset, every time the - // page is scrolled - prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft - || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop - || 0; - }, - - realOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return [valueL, valueT]; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return [valueL, valueT]; - }, - - // caches x/y coordinate pair to use with overlap - within: function(element, x, y) { - if (this.includeScrollOffsets) - return this.withinIncludingScrolloffsets(element, x, y); - this.xcomp = x; - this.ycomp = y; - this.offset = this.cumulativeOffset(element); - - return (y >= this.offset[1] && - y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && - x < this.offset[0] + element.offsetWidth); - }, - - withinIncludingScrolloffsets: function(element, x, y) { - var offsetcache = this.realOffset(element); - - this.xcomp = x + offsetcache[0] - this.deltaX; - this.ycomp = y + offsetcache[1] - this.deltaY; - this.offset = this.cumulativeOffset(element); - - return (this.ycomp >= this.offset[1] && - this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && - this.xcomp < this.offset[0] + element.offsetWidth); - }, - - // within must be called directly before - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / - element.offsetHeight; - if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / - element.offsetWidth; - }, - - clone: function(source, target) { - source = $(source); - target = $(target); - target.style.position = 'absolute'; - var offsets = this.cumulativeOffset(source); - target.style.top = offsets[1] + 'px'; - target.style.left = offsets[0] + 'px'; - target.style.width = source.offsetWidth + 'px'; - target.style.height = source.offsetHeight + 'px'; - } -} diff --git a/solr/site-src/src/documentation/skins/common/skinconf.xsl b/solr/site-src/src/documentation/skins/common/skinconf.xsl deleted file mode 100644 index 5d1132086f1..00000000000 --- a/solr/site-src/src/documentation/skins/common/skinconf.xsl +++ /dev/null @@ -1,238 +0,0 @@ - - - - - - - true - - - true - - - true - - - true - - - true - - - false - - - false - - - true - - - .at. - - - true - - - - - - - - - - - - - - - Page 1 - - - - - true - - - - - - - - - - Built with Apache Forrest - http://forrest.apache.org/ - images/built-with-forrest-button.png - 88 - 31 - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_de.xml b/solr/site-src/src/documentation/skins/common/translations/CommonMessages_de.xml deleted file mode 100644 index bc461196e64..00000000000 --- a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_de.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Schriftgrösse: - Zuletzt veröffentlicht: - Suche: - Suche auf der Seite mit - diff --git a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_en_US.xml b/solr/site-src/src/documentation/skins/common/translations/CommonMessages_en_US.xml deleted file mode 100644 index 88dfe143118..00000000000 --- a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_en_US.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Font size: - Last Published: - Search - Search site with - diff --git a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_es.xml b/solr/site-src/src/documentation/skins/common/translations/CommonMessages_es.xml deleted file mode 100644 index 63be6712b30..00000000000 --- a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_es.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Tamaño del texto: - Fecha de publicación: - Buscar - Buscar en - diff --git a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_fr.xml b/solr/site-src/src/documentation/skins/common/translations/CommonMessages_fr.xml deleted file mode 100644 index 622569a4ffa..00000000000 --- a/solr/site-src/src/documentation/skins/common/translations/CommonMessages_fr.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - Taille : - Dernière publication : - Rechercher - Rechercher sur le site avec - diff --git a/solr/site-src/src/documentation/skins/common/xslt/fo/document-to-fo.xsl b/solr/site-src/src/documentation/skins/common/xslt/fo/document-to-fo.xsl deleted file mode 100644 index 6bae24b043f..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/fo/document-to-fo.xsl +++ /dev/null @@ -1,1014 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 841mm - 594mm - 420mm - 297mm - 210mm - 148mm - 7.25in - 8.5in - 11in - 8.5in - 8.5in - 8.5in - 11in - 8.5in - - - - - 1189mm - 841mm - 594mm - 420mm - 297mm - 210mm - 10.5in - 13in - 17in - 14in - 10.83in - 17in - 11in - - - - - - - - - - 1189mm - 841mm - 594mm - 420mm - 297mm - 210mm - 10.5in - 13in - 17in - 14in - 10.83in - 17in - 11in - - - - - 841mm - 594mm - 420mm - 297mm - 210mm - 148mm - 7.25in - 8.5in - 11in - 8.5in - 8.5in - 8.5in - 11in - 8.5in - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - start - - - - - - - - - - - - - - - end - - - - - - - - - - - - - - start - - - - - - - - - - - - - - - - - - - - - - - - - NOTICE: - - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - -. - - - - - -pt - - - - - - -   - - - - - - - - - - - - 0 - - - - - - - - - - - by - - - , - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6pt" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 6pt - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Warning: - - - - - - - - - - - - - - - - - Note: - - - - - - - - - - - - - FIXME (): - - - - - - - - - - - - - - - - - - - () - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - in - - - - - - - - - - - - - - - - - - - - Table - - -: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Table of contents - - - - page - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [] - - - - - - - - - - - - page - - - - page - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/fo/footerinfo.xsl b/solr/site-src/src/documentation/skins/common/xslt/fo/footerinfo.xsl deleted file mode 100644 index c5ba5c9460e..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/fo/footerinfo.xsl +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - -Copyright © -   - All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/fo/pdfoutline.xsl b/solr/site-src/src/documentation/skins/common/xslt/fo/pdfoutline.xsl deleted file mode 100644 index acdc6ac9cf1..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/fo/pdfoutline.xsl +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/book-to-menu.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/book-to-menu.xsl deleted file mode 100644 index 228b57e5c4d..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/book-to-menu.xsl +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  • - - - - - - - - -
  • -
    - - - - - -
    diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/document-to-html.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/document-to-html.xsl deleted file mode 100644 index d4496e07a7e..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/document-to-html.xsl +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -
    - - - - -
    - -

    - -

    -
    -
    - -

    - -

    -
    - - - - -
    - - -; - - -
    -
    -
    - -
    - - - - - - - - - - - - - - - - ^ - - - - - - - -15 - - -0 - - - -
    - -
    - - - -
    - - - - - -
    - - - - - - Note - Warning - Fixme () - -
    -
    - -
    -
    -
    - -
    - -Notice: - -
    -
    - - - - _top - - - _blank - - - - - - - - - - - - - - -
    - - -
    -
    - - -
    -
    -      
    -      
    -
    -    
    -
    - - - - - - - - - - - - - - codefrag - - - - - - - -
    - - - - -
    -
    - - - - - - - - -
    -
    - - - - - - - -
    - -
    - - - - - - - - - by - , - - - - - - - - - - - - - - version - - - - - - - - - v - - - - - - - - -

    - -Type: - -

    -
    - -

    - -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/dotdots.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/dotdots.xsl deleted file mode 100644 index 2ff9d1f3266..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/dotdots.xsl +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - -../ - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/pathutils.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/pathutils.xsl deleted file mode 100644 index c1639a4b4f2..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/pathutils.xsl +++ /dev/null @@ -1,231 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/renderlogo.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/renderlogo.xsl deleted file mode 100644 index 698156b9ba2..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/renderlogo.xsl +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - {$name} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl deleted file mode 100644 index 2f8b15cd399..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/site-to-xhtml.xsl +++ /dev/null @@ -1,388 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > - - - > - - - - - - - - - - - - - - - - - - - - - - - - Valid HTML 4.01! - - - - - - - Valid HTML 4.01! - Valid CSS! - - - - - - - PDF -
    - PDF
    - -
    -
    - - - - - TXT -
    - TXT
    - -
    -
    - - - - - POD -
    - POD
    - -
    -
    - - - - - XML -
    - XML
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      - -
    • - - - - - -
    • -
      -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - - - - - - - - - - - -
    -
    - - - - - - - - - - 2005 - yyyy - - - - - - - - - - - - - - -
    diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/split.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/split.xsl deleted file mode 100644 index 14c88e72cb4..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/split.xsl +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - 40 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/strip_namespaces.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/strip_namespaces.xsl deleted file mode 100644 index 47dec6a1788..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/strip_namespaces.xsl +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/tab-to-menu.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/tab-to-menu.xsl deleted file mode 100644 index 257d2919f70..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/tab-to-menu.xsl +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - | - - - - - - - - | - - - - - - - - - - - - - - - - - - - - - - -
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/solr/site-src/src/documentation/skins/common/xslt/html/tabutils.xsl b/solr/site-src/src/documentation/skins/common/xslt/html/tabutils.xsl deleted file mode 100644 index 0caaaf4c81f..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/html/tabutils.xsl +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - -/ - - - -/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/common/xslt/svg/document-to-svg.xsl b/solr/site-src/src/documentation/skins/common/xslt/svg/document-to-svg.xsl deleted file mode 100644 index 08ca5ba1b7a..00000000000 --- a/solr/site-src/src/documentation/skins/common/xslt/svg/document-to-svg.xsl +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/lucene/css/basic.css b/solr/site-src/src/documentation/skins/lucene/css/basic.css deleted file mode 100644 index 4ed58b99ae7..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/css/basic.css +++ /dev/null @@ -1,166 +0,0 @@ -/* -* 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. -*/ -/** - * General - */ - -img { border: 0; } - -#content table { - border: 0; - width: 100%; -} -/*Hack to get IE to render the table at 100%*/ -* html #content table { margin-left: -3px; } - -#content th, -#content td { - margin: 0; - padding: 0; - vertical-align: top; -} - -.clearboth { - clear: both; -} - -.note, .warning, .fixme { - border: solid black 1px; - margin: 1em 3em; -} - -.note .label { - background: #369; - color: white; - font-weight: bold; - padding: 5px 10px; -} -.note .content { - background: #F0F0FF; - color: black; - line-height: 120%; - font-size: 90%; - padding: 5px 10px; -} -.warning .label { - background: #C00; - color: white; - font-weight: bold; - padding: 5px 10px; -} -.warning .content { - background: #FFF0F0; - color: black; - line-height: 120%; - font-size: 90%; - padding: 5px 10px; -} -.fixme .label { - background: #C6C600; - color: black; - font-weight: bold; - padding: 5px 10px; -} -.fixme .content { - padding: 5px 10px; -} - -/** - * Typography - */ - -body { - font-family: verdana, "Trebuchet MS", arial, helvetica, sans-serif; - font-size: 100%; -} - -#content { - font-family: Georgia, Palatino, Times, serif; - font-size: 95%; -} -#tabs { - font-size: 70%; -} -#menu { - font-size: 80%; -} -#footer { - font-size: 70%; -} - -h1, h2, h3, h4, h5, h6 { - font-family: "Trebuchet MS", verdana, arial, helvetica, sans-serif; - font-weight: bold; - margin-top: 1em; - margin-bottom: .5em; -} - -h1 { - margin-top: 0; - margin-bottom: 1em; - font-size: 1.4em; -} -#content h1 { - font-size: 160%; - margin-bottom: .5em; -} -#menu h1 { - margin: 0; - padding: 10px; - background: #336699; - color: white; -} -h2 { font-size: 120%; } -h3 { font-size: 100%; } -h4 { font-size: 90%; } -h5 { font-size: 80%; } -h6 { font-size: 75%; } - -p { - line-height: 120%; - text-align: left; - margin-top: .5em; - margin-bottom: 1em; -} - -#content li, -#content th, -#content td, -#content li ul, -#content li ol{ - margin-top: .5em; - margin-bottom: .5em; -} - - -#content li li, -#minitoc-area li{ - margin-top: 0em; - margin-bottom: 0em; -} - -#content .attribution { - text-align: right; - font-style: italic; - font-size: 85%; - margin-top: 1em; -} - -.codefrag { - font-family: "Courier New", Courier, monospace; - font-size: 110%; -} diff --git a/solr/site-src/src/documentation/skins/lucene/css/print.css b/solr/site-src/src/documentation/skins/lucene/css/print.css deleted file mode 100644 index 8916b9fc01e..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/css/print.css +++ /dev/null @@ -1,54 +0,0 @@ -/* -* 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. -*/ -body { - font-family: Georgia, Palatino, serif; - font-size: 12pt; - background: white; -} - -#tabs, -#menu, -#content .toc { - display: none; -} - -#content { - width: auto; - padding: 0; - float: none !important; - color: black; - background: inherit; -} - -a:link, a:visited { - color: #336699; - background: inherit; - text-decoration: underline; -} - -#top .logo { - padding: 0; - margin: 0 0 2em 0; -} - -#footer { - margin-top: 4em; -} - -acronym { - border: 0; -} diff --git a/solr/site-src/src/documentation/skins/lucene/css/profile.css.xslt b/solr/site-src/src/documentation/skins/lucene/css/profile.css.xslt deleted file mode 100644 index a1dcb23e49c..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/css/profile.css.xslt +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - -#top { background-color: ;} - - -#top .header .current { background-color: ;} -#top .header .current a:link { color: ; } -#top .header .current a:visited { color: ; } -#top .header .current a:hover { color: ; } - - -#tabs li { background-color: ;} -#tabs li a:link { color: ; } -#tabs li a:visited { color: ; } -#tabs li a:hover { color: ; } - - -#level2tabs a.selected { background-color: ;} -#level2tabs a:link { color: ; } -#level2tabs a:visited { color: ; } -#level2tabs a:hover { color: ; } - - -#level2tabs { background-color: ;} -#level2tabs a.unselected:link { color: ; } -#level2tabs a.unselected:visited { color: ; } -#level2tabs a.unselected:hover { color: ; } - - -.heading { background-color: ;} - - -.boxed { background-color: ;} -.underlined_5 {border-bottom: solid 5px ;} -.underlined_10 {border-bottom: solid 10px ;} -table caption { -background-color: ; -color: ; -} - - -#feedback { -color: ; -background: ; -text-align: ; -} -#feedback #feedbackto { -color: ; -} - - -#main .breadtrail { -background: ; -color: ; -} -#main .breadtrail a:link { color: ; } -#main .breadtrail a:visited { color: ; } -#main .breadtrail a:hover { color: ; } -#top .breadtrail { -background: ; -color: ; -} -#top .breadtrail a:link { color: ; } -#top .breadtrail a:visited { color: ; } -#top .breadtrail a:hover { color: ; } - - - -#publishedStrip { -color: ; -background: ; -} - - - -#publishedStrip { -color: ; -background: ; -} - - -#menu .menupagetitle { background-color: ; - color: ;} - - -#menu { border-color: ;} -#menu .menupagetitle { border-color: ;} -#menu .menupageitemgroup { border-color: ;} - - -#menu { background-color: ;} -#menu { color: ;} -#menu a:link { color: ;} -#menu a:visited { color: ;} -#menu a:hover { -background-color: ; -color: ;} - - -#menu .menupageitemgroup { -background-color: ; -} -#menu .menupageitem { -color: ; -} -#menu .menupageitem a:link { color: ;} -#menu .menupageitem a:visited { color: ;} -#menu .menupageitem a:hover { -background-color: ; -color: ; -} - - -#menu h1 { -color: ; -background-color: ; -} - - -#top .searchbox { -background-color: ; -color: ; -} - - -body{ -background-color: ; -color: ; -} -a:link { color:} -a:visited { color:} -a:hover { color:} - - - -#footer { background-color: ;} - - - -.highlight { background-color: ;} - - -.fixme { border-color: ;} - - -.note { border-color: ;} - - -.warning { border-color: ;} - - -.code { border-color: ;} - - -.ForrestTable { background-color: ;} - - -.ForrestTable td { background-color: ;} - - diff --git a/solr/site-src/src/documentation/skins/lucene/css/screen.css b/solr/site-src/src/documentation/skins/lucene/css/screen.css deleted file mode 100644 index 4e2e040c84a..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/css/screen.css +++ /dev/null @@ -1,587 +0,0 @@ -/* -* 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. -*/ -body { margin: 0px 0px 0px 0px; font-family: Verdana, Helvetica, sans-serif; } - -h1 { font-size : 160%; margin: 0px 0px 0px 0px; padding: 0px; } -h2 { font-size : 140%; margin: 1em 0px 0.8em 0px; padding: 0px; font-weight : bold;} -h3 { font-size : 130%; margin: 0.8em 0px 0px 0px; padding: 0px; font-weight : bold; } -.h3 { margin: 22px 0px 3px 0px; } -h4 { font-size : 120%; margin: 0.7em 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } -.h4 { margin: 18px 0px 0px 0px; } -h4.faq { font-size : 120%; margin: 18px 0px 0px 0px; padding: 0px; font-weight : bold; text-align: left; } -h5 { font-size : 100%; margin: 14px 0px 0px 0px; padding: 0px; font-weight : normal; text-align: left; } - -/** -* table -*/ -table .title { background-color: #000000; } -.ForrestTable { - color: #ffffff; - background-color: #7099C5; - width: 100%; - font-size : 100%; - empty-cells: show; -} -table caption { - padding-left: 5px; - color: white; - text-align: left; - font-weight: bold; - background-color: #000000; -} -.ForrestTable td { - color: black; - background-color: #f0f0ff; -} -.ForrestTable th { text-align: center; } -/** - * Page Header - */ - -#top { - position: relative; - float: left; - width: 100%; - background: #294563; /* if you want a background in the header, put it here */ -} - -#top .breadtrail { - background: #CFDCED; - color: black; - border-bottom: solid 1px white; - padding: 3px 10px; - font-size: 75%; -} -#top .breadtrail a { color: black; } - -#top .header { - float: left; - width: 100%; - background: url("images/header_white_line.gif") repeat-x bottom; -} - -#top .grouplogo { - padding: 7px 0 10px 10px; - float: left; - text-align: left; -} -#top .projectlogo { - padding: 7px 0 10px 10px; - float: left; - width: 33%; - text-align: right; -} -#top .projectlogoA1 { - padding: 7px 0 10px 10px; - float: right; -} -html>body #top .searchbox { - bottom: 0px; -} -#top .searchbox { - position: absolute; - right: 10px; - height: 28px; - font-size: 70%; - white-space: nowrap; - text-align: right; - color: white; - background-color: #000000; - z-index:0; - background-image: url(images/rc-t-l-5-1header-2searchbox-3searchbox.png); - background-repeat: no-repeat; - background-position: top left; - bottom: -1px; /* compensate for IE rendering issue */ -} - -#top .searchbox form { - padding: 5px 10px; - margin: 0; -} -#top .searchbox p { - padding: 0 0 2px 0; - margin: 0; -} -#top .searchbox input { - font-size: 100%; -} - -#tabs { - clear: both; - padding-left: 10px; - margin: 0; - list-style: none; -} -/* background: #CFDCED url("images/tab-right.gif") no-repeat right top;*/ -#tabs li { - float: left; - background-image: url(images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png); - background-repeat: no-repeat; - background-position: top right; - background-color: #000000; - margin: 0 3px 0 0; - padding: 0; -} - -/*background: url("images/tab-left.gif") no-repeat left top;*/ -#tabs li a { - float: left; - display: block; - font-family: verdana, arial, sans-serif; - text-decoration: none; - color: black; - white-space: nowrap; - background-image: url(images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png); - background-repeat: no-repeat; - background-position: top left; - padding: 5px 15px 4px; - width: .1em; /* IE/Win fix */ -} - -#tabs li a:hover { - - cursor: pointer; - text-decoration:underline; -} - -#tabs > li a { width: auto; } /* Rest of IE/Win fix */ - -/* Commented Backslash Hack hides rule from IE5-Mac \*/ -#tabs a { float: none; } -/* End IE5-Mac hack */ - -#top .header .current { - background-color: #4C6C8F; - background-image: url(images/rc-t-r-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top right; -} -#top .header .current a { - font-weight: bold; - padding-bottom: 5px; - color: white; - background-image: url(images/rc-t-l-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top left; -} -#publishedStrip { - padding-right: 10px; - padding-left: 20px; - padding-top: 3px; - padding-bottom:3px; - color: #ffffff; - font-size : 60%; - font-weight: bold; - background-color: #4C6C8F; - text-align:right; -} - -#level2tabs { -margin: 0; -float:left; -position:relative; - -} - - - -#level2tabs a:hover { - - cursor: pointer; - text-decoration:underline; - -} - -#level2tabs a{ - - cursor: pointer; - text-decoration:none; - background-image: url('images/chapter.gif'); - background-repeat: no-repeat; - background-position: center left; - padding-left: 6px; - margin-left: 6px; -} - -/* -* border-top: solid #4C6C8F 15px; -*/ -#main { - position: relative; - background: white; - clear:both; -} -#main .breadtrail { - clear:both; - position: relative; - background: #CFDCED; - color: black; - border-bottom: solid 1px black; - border-top: solid 1px black; - padding: 0px 180px; - font-size: 75%; - z-index:10; -} -/** -* Round corner -*/ -#roundtop { - background-image: url(images/rc-t-r-15-1body-2menu-3menu.png); - background-repeat: no-repeat; - background-position: top right; -} - -#roundbottom { - background-image: url(images/rc-b-r-15-1body-2menu-3menu.png); - background-repeat: no-repeat; - background-position: top right; -} - -img.corner { - width: 15px; - height: 15px; - border: none; - display: block !important; -} - -.roundtopsmall { - background-image: url(images/rc-t-r-5-1header-2searchbox-3searchbox.png); - background-repeat: no-repeat; - background-position: top right; -} - -#roundbottomsmall { - background-image: url(images/rc-b-r-5-1header-2tab-selected-3tab-selected.png); - background-repeat: no-repeat; - background-position: top right; -} - -img.cornersmall { - width: 5px; - height: 5px; - border: none; - display: block !important; -} -/** - * Side menu - */ -#menu a { font-weight: normal; text-decoration: none;} -#menu a:visited { font-weight: normal; } -#menu a:active { font-weight: normal; } -#menu a:hover { font-weight: normal; text-decoration:underline;} - -#menuarea { width:10em;} -#menu { - position: relative; - float: left; - width: 160px; - padding-top: 0px; - top:-18px; - left:10px; - z-index: 20; - background-color: #f90; - font-size : 70%; - -} - -.menutitle { - cursor:pointer; - padding: 3px 12px; - margin-left: 10px; - background-image: url('images/chapter.gif'); - background-repeat: no-repeat; - background-position: center left; - font-weight : bold; - - -} - -.menutitle:hover{text-decoration:underline;cursor: pointer;} - -#menu .menuitemgroup { - margin: 0px 0px 6px 8px; - padding: 0px; - font-weight : bold; } - -#menu .selectedmenuitemgroup{ - margin: 0px 0px 0px 8px; - padding: 0px; - font-weight : normal; - - } - -#menu .menuitem { - padding: 2px 0px 1px 13px; - background-image: url('images/page.gif'); - background-repeat: no-repeat; - background-position: center left; - font-weight : normal; - margin-left: 10px; -} - -#menu .menupage { - margin: 2px 0px 1px 10px; - padding: 0px 3px 0px 12px; - background-image: url('images/page.gif'); - background-repeat: no-repeat; - background-position: center left; - font-style : normal; -} -#menu .menupagetitle { - padding: 0px 0px 0px 1px; - font-style : normal; - border-style: solid; - border-width: 1px; - margin-right: 10px; - -} -#menu .menupageitemgroup { - padding: 3px 0px 4px 6px; - font-style : normal; - border-bottom: 1px solid ; - border-left: 1px solid ; - border-right: 1px solid ; - margin-right: 10px; -} -#menu .menupageitem { - font-style : normal; - font-weight : normal; - border-width: 0px; - font-size : 90%; -} -#menu #credit { - text-align: center; -} -#menu #credit2 { - text-align: center; - padding: 3px 3px 3px 3px; - background-color: #ffffff; -} -#menu .searchbox { - text-align: center; -} -#menu .searchbox form { - padding: 3px 3px; - margin: 0; -} -#menu .searchbox input { - font-size: 100%; -} - -#content { - padding: 20px 20px 20px 180px; - margin: 0; - font : small Verdana, Helvetica, sans-serif; - font-size : 80%; -} - -#content ul { - margin: 0; - padding: 0 25px; -} -#content li { - padding: 0 5px; -} -#feedback { - color: black; - background: #CFDCED; - text-align:center; - margin-top: 5px; -} -#feedback #feedbackto { - font-size: 90%; - color: black; -} -#footer { - clear: both; - position: relative; /* IE bugfix (http://www.dracos.co.uk/web/css/ie6floatbug/) */ - width: 100%; - background: #CFDCED; - border-top: solid 1px #4C6C8F; - color: black; -} -#footer .copyright { - position: relative; /* IE bugfix cont'd */ - padding: 5px; - margin: 0; - width: 45%; -} -#footer .lastmodified { - position: relative; /* IE bugfix cont'd */ - float: right; - width: 45%; - padding: 5px; - margin: 0; - text-align: right; -} -#footer a { color: white; } - -#footer #logos { - text-align: left; -} - - -/** - * Misc Styles - */ - -acronym { cursor: help; } -.boxed { background-color: #a5b6c6;} -.underlined_5 {border-bottom: solid 5px #4C6C8F;} -.underlined_10 {border-bottom: solid 10px #4C6C8F;} -/* ==================== snail trail ============================ */ - -.trail { - position: relative; /* IE bugfix cont'd */ - font-size: 70%; - text-align: right; - float: right; - margin: -10px 5px 0px 5px; - padding: 0; -} - -#motd-area { - position: relative; /* IE bugfix cont'd */ - float: right; - width: 35%; - background-color: #f0f0ff; - border-top: solid 1px #4C6C8F; - border-bottom: solid 1px #4C6C8F; - margin-bottom: 15px; - margin-left: 15px; - margin-right: 10%; - padding-bottom: 5px; - padding-top: 5px; -} - -#minitoc-area { - border-top: solid 1px #4C6C8F; - border-bottom: solid 1px #4C6C8F; - margin: 15px 10% 5px 15px; - /* margin-bottom: 15px; - margin-left: 15px; - margin-right: 10%;*/ - padding-bottom: 7px; - padding-top: 5px; -} -.minitoc { - list-style-image: url('images/current.gif'); - font-weight: normal; -} - -li p { - margin: 0; - padding: 0; -} - -.pdflink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.pdflink br { - margin-top: -10px; - padding-left: 1px; -} -.pdflink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.pdflink img { - display: block; - height: 16px; - width: 16px; -} -.xmllink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.xmllink br { - margin-top: -10px; - padding-left: 1px; -} -.xmllink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.xmllink img { - display: block; - height: 16px; - width: 16px; -} -.podlink { - position: relative; /* IE bugfix cont'd */ - float: right; - margin: 0px 5px; - padding: 0; -} -.podlink br { - margin-top: -10px; - padding-left: 1px; -} -.podlink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} - -.podlink img { - display: block; - height: 16px; - width: 16px; -} - -.printlink { - position: relative; /* IE bugfix cont'd */ - float: right; -} -.printlink br { - margin-top: -10px; - padding-left: 1px; -} -.printlink a { - display: block; - font-size: 70%; - text-align: center; - margin: 0; - padding: 0; -} -.printlink img { - display: block; - height: 16px; - width: 16px; -} - -p.instruction { - display: list-item; - list-style-image: url('../images/instruction_arrow.png'); - list-style-position: outside; - margin-left: 2em; -} diff --git a/solr/site-src/src/documentation/skins/lucene/images/chapter.gif b/solr/site-src/src/documentation/skins/lucene/images/chapter.gif deleted file mode 100755 index d3d8245d0c7..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/chapter.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/chapter_open.gif b/solr/site-src/src/documentation/skins/lucene/images/chapter_open.gif deleted file mode 100755 index eecce18b50a..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/chapter_open.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/current.gif b/solr/site-src/src/documentation/skins/lucene/images/current.gif deleted file mode 100755 index fd82c082012..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/current.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/error.png b/solr/site-src/src/documentation/skins/lucene/images/error.png deleted file mode 100755 index b4fe06e3709..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/error.png and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/header_white_line.gif b/solr/site-src/src/documentation/skins/lucene/images/header_white_line.gif deleted file mode 100755 index 369cae8dcf2..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/header_white_line.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/info.png b/solr/site-src/src/documentation/skins/lucene/images/info.png deleted file mode 100755 index 2e53447e8c2..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/info.png and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/instruction_arrow.png b/solr/site-src/src/documentation/skins/lucene/images/instruction_arrow.png deleted file mode 100755 index 0fbc72452bf..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/instruction_arrow.png and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/label.gif b/solr/site-src/src/documentation/skins/lucene/images/label.gif deleted file mode 100755 index c83a3893c55..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/label.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/page.gif b/solr/site-src/src/documentation/skins/lucene/images/page.gif deleted file mode 100755 index a144d3295be..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/page.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/pdfdoc.gif b/solr/site-src/src/documentation/skins/lucene/images/pdfdoc.gif deleted file mode 100755 index 00dee28aad4..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/pdfdoc.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/printer.gif b/solr/site-src/src/documentation/skins/lucene/images/printer.gif deleted file mode 100755 index 5021187b064..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/printer.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/success.png b/solr/site-src/src/documentation/skins/lucene/images/success.png deleted file mode 100755 index 96fcfea3225..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/success.png and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/warning.png b/solr/site-src/src/documentation/skins/lucene/images/warning.png deleted file mode 100755 index b81b2ce8a12..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/warning.png and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/images/xmldoc.gif b/solr/site-src/src/documentation/skins/lucene/images/xmldoc.gif deleted file mode 100755 index ca1224f61f1..00000000000 Binary files a/solr/site-src/src/documentation/skins/lucene/images/xmldoc.gif and /dev/null differ diff --git a/solr/site-src/src/documentation/skins/lucene/note.txt b/solr/site-src/src/documentation/skins/lucene/note.txt deleted file mode 100644 index d34c8db5ef4..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/note.txt +++ /dev/null @@ -1,50 +0,0 @@ -Notes for developer: - ---Legend------------------- -TODO -> blocker -DONE -> blocker -ToDo -> enhancement bug -done -> enhancement bug - ---Issues------------------- -- the corner images should be rendered through svg with the header color. --> DONE --> ToDo: get rid of the images and use only divs! - -- the menu points should be displayed "better". --> DONE --- Use the krysalis-site menu approach for the overall menu display. --> DONE --- Use the old lenya innermenu approch to further enhance the menu . --> DONE - -- the content area needs some attention. --> DONE --- introduce the heading scheme from krysalis () --> DONE --> ToDo: make box with round corners --> done: make underlined with variable border height --> ToDo: make underline with bottom round corner --- introduce the toc for each html-page --> DONE --- introduce the external-link-images. --> DONE - -- the publish note should be where now only a border is. -Like
    --> DONE -, but make it configurable. --> DONE -- footer needs some attention --> DONE --- the footer do not have the color profile! Enable it! --> DONE --- the footer should as well contain a feedback link. -See http://issues.apache.org/eyebrowse/ReadMsg?listName=forrest-user@xml.apache.org&msgNo=71 --> DONE - -- introduce credits alternativ location --> DONE - -- border for published / breadtrail / menu /tab divs --> ToDo \ No newline at end of file diff --git a/solr/site-src/src/documentation/skins/lucene/skinconf.xsl b/solr/site-src/src/documentation/skins/lucene/skinconf.xsl deleted file mode 100644 index 2f8df12903c..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/skinconf.xsl +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/solr/site-src/src/documentation/skins/lucene/xslt/fo/document-to-fo.xsl b/solr/site-src/src/documentation/skins/lucene/xslt/fo/document-to-fo.xsl deleted file mode 100644 index 0fb2a23731b..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/xslt/fo/document-to-fo.xsl +++ /dev/null @@ -1,22 +0,0 @@ - - - - - diff --git a/solr/site-src/src/documentation/skins/lucene/xslt/html/book-to-menu.xsl b/solr/site-src/src/documentation/skins/lucene/xslt/html/book-to-menu.xsl deleted file mode 100644 index 5242fc85152..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/xslt/html/book-to-menu.xsl +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - -
  • - -

    -
      - -
  • -
    - - -
  • -
    - -
    - -
    -
    - - - - - -
    diff --git a/solr/site-src/src/documentation/skins/lucene/xslt/html/document-to-html.xsl b/solr/site-src/src/documentation/skins/lucene/xslt/html/document-to-html.xsl deleted file mode 100644 index 2d337bf0d60..00000000000 --- a/solr/site-src/src/documentation/skins/lucene/xslt/html/document-to-html.xsl +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - -
    -