http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctColumnsReducer2.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctColumnsReducer2.java b/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctColumnsReducer2.java deleted file mode 100644 index b5aeef6..0000000 --- a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctColumnsReducer2.java +++ /dev/null @@ -1,254 +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. -*/ - -package org.apache.kylin.engine.mr.steps.fdc2; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.io.IOUtils; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FSDataOutputStream; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.io.NullWritable; -import org.apache.hadoop.io.Text; -import org.apache.kylin.common.KylinConfig; -import org.apache.kylin.common.util.ByteArray; -import org.apache.kylin.common.util.Bytes; -import org.apache.kylin.cube.CubeInstance; -import org.apache.kylin.cube.CubeManager; -import org.apache.kylin.cube.model.CubeDesc; -import org.apache.kylin.engine.mr.KylinReducer; -import org.apache.kylin.engine.mr.common.AbstractHadoopJob; -import org.apache.kylin.engine.mr.common.BatchConstants; -import org.apache.kylin.engine.mr.common.CubeStatsWriter; -import org.apache.kylin.measure.hllc.HyperLogLogPlusCounter; -import org.apache.kylin.metadata.model.TblColRef; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - */ -public class FactDistinctColumnsReducer2 extends KylinReducer<SelfDefineSortableKey, Text, NullWritable, Text> { - - private List<TblColRef> columnList; - private String statisticsOutput = null; - private List<Long> baseCuboidRowCountInMappers; - protected Map<Long, HyperLogLogPlusCounter> cuboidHLLMap = null; - protected long baseCuboidId; - protected CubeDesc cubeDesc; - private long totalRowsBeforeMerge = 0; - private int samplingPercentage; - private List<ByteArray> colValues; - private TblColRef col = null; - private boolean isStatistics = false; - private boolean isPartitionCol = false; - private KylinConfig cubeConfig; - protected static final Logger logger = LoggerFactory.getLogger(FactDistinctColumnsReducer2.class); - - @Override - protected void setup(Context context) throws IOException { - super.bindCurrentConfiguration(context.getConfiguration()); - - Configuration conf = context.getConfiguration(); - KylinConfig config = AbstractHadoopJob.loadKylinPropsAndMetadata(); - String cubeName = conf.get(BatchConstants.CFG_CUBE_NAME); - CubeInstance cube = CubeManager.getInstance(config).getCube(cubeName); - cubeConfig = cube.getConfig(); - cubeDesc = cube.getDescriptor(); - columnList = CubeManager.getInstance(config).getAllDictColumnsOnFact(cubeDesc); - - boolean collectStatistics = Boolean.parseBoolean(conf.get(BatchConstants.CFG_STATISTICS_ENABLED)); - int numberOfTasks = context.getNumReduceTasks(); - int taskId = context.getTaskAttemptID().getTaskID().getId(); - - if (collectStatistics && (taskId == numberOfTasks - 1)) { - // hll - isStatistics = true; - statisticsOutput = conf.get(BatchConstants.CFG_STATISTICS_OUTPUT); - baseCuboidRowCountInMappers = Lists.newArrayList(); - cuboidHLLMap = Maps.newHashMap(); - samplingPercentage = Integer.parseInt(context.getConfiguration().get(BatchConstants.CFG_STATISTICS_SAMPLING_PERCENT)); - } else if (collectStatistics && (taskId == numberOfTasks - 2)) { - // partition col - isStatistics = false; - isPartitionCol = true; - col = cubeDesc.getModel().getPartitionDesc().getPartitionDateColumnRef(); - colValues = Lists.newLinkedList(); - } else { - // col - isStatistics = false; - isPartitionCol = false; - col = columnList.get(taskId); - colValues = Lists.newLinkedList(); - } - } - - @Override - protected void doReduce(SelfDefineSortableKey skey, Iterable<Text> values, Context context) throws IOException, InterruptedException { - Text key = skey.getText(); - if (isStatistics == true) { - // for hll - long cuboidId = Bytes.toLong(key.getBytes(), 1, Bytes.SIZEOF_LONG); - for (Text value : values) { - HyperLogLogPlusCounter hll = new HyperLogLogPlusCounter(cubeConfig.getCubeStatsHLLPrecision()); - ByteBuffer bf = ByteBuffer.wrap(value.getBytes(), 0, value.getLength()); - hll.readRegisters(bf); - - totalRowsBeforeMerge += hll.getCountEstimate(); - - if (cuboidId == baseCuboidId) { - baseCuboidRowCountInMappers.add(hll.getCountEstimate()); - } - - if (cuboidHLLMap.get(cuboidId) != null) { - cuboidHLLMap.get(cuboidId).merge(hll); - } else { - cuboidHLLMap.put(cuboidId, hll); - } - } - } else if (isPartitionCol == true) { - // for partition col min/max value - ByteArray value = new ByteArray(Bytes.copy(key.getBytes(), 1, key.getLength() - 1)); - if (colValues.size() > 1) { - colValues.set(1, value); - } else { - colValues.add(value); - } - } else { - colValues.add(new ByteArray(Bytes.copy(key.getBytes(), 1, key.getLength() - 1))); - if (colValues.size() == 1000000) { //spill every 1 million - logger.info("spill values to disk..."); - outputDistinctValues(col, colValues, context); - colValues.clear(); - } - } - } - - private void outputDistinctValues(TblColRef col, Collection<ByteArray> values, Context context) throws IOException { - final Configuration conf = context.getConfiguration(); - final FileSystem fs = FileSystem.get(conf); - final String outputPath = conf.get(BatchConstants.CFG_OUTPUT_PATH); - final Path outputFile = new Path(outputPath, col.getName()); - - FSDataOutputStream out = null; - try { - if (fs.exists(outputFile)) { - out = fs.append(outputFile); - logger.info("append file " + outputFile); - } else { - out = fs.create(outputFile); - logger.info("create file " + outputFile); - } - - for (ByteArray value : values) { - out.write(value.array(), value.offset(), value.length()); - out.write('\n'); - } - } finally { - IOUtils.closeQuietly(out); - } - } - - @Override - protected void doCleanup(Context context) throws IOException, InterruptedException { - - if (isStatistics == false) { - if (colValues.size() > 0) { - outputDistinctValues(col, colValues, context); - colValues.clear(); - } - } else { - //output the hll info; - long grandTotal = 0; - for (HyperLogLogPlusCounter hll : cuboidHLLMap.values()) { - grandTotal += hll.getCountEstimate(); - } - double mapperOverlapRatio = grandTotal == 0 ? 0 : (double) totalRowsBeforeMerge / grandTotal; - - int mapperNumber = baseCuboidRowCountInMappers.size(); - - writeMapperAndCuboidStatistics(context); // for human check - CubeStatsWriter.writeCuboidStatistics(context.getConfiguration(), new Path(statisticsOutput), // - cuboidHLLMap, samplingPercentage, mapperNumber, mapperOverlapRatio); - } - } - - private void writeMapperAndCuboidStatistics(Context context) throws IOException { - Configuration conf = context.getConfiguration(); - FileSystem fs = FileSystem.get(conf); - FSDataOutputStream out = fs.create(new Path(statisticsOutput, BatchConstants.CFG_STATISTICS_CUBE_ESTIMATION_FILENAME)); - - try { - String msg; - - List<Long> allCuboids = Lists.newArrayList(); - allCuboids.addAll(cuboidHLLMap.keySet()); - Collections.sort(allCuboids); - - msg = "Total cuboid number: \t" + allCuboids.size(); - writeLine(out, msg); - msg = "Samping percentage: \t" + samplingPercentage; - writeLine(out, msg); - - writeLine(out, "The following statistics are collected based on sampling data."); - writeLine(out, "Number of Mappers: " + baseCuboidRowCountInMappers.size()); - for (int i = 0; i < baseCuboidRowCountInMappers.size(); i++) { - if (baseCuboidRowCountInMappers.get(i) > 0) { - msg = "Base Cuboid in Mapper " + i + " row count: \t " + baseCuboidRowCountInMappers.get(i); - writeLine(out, msg); - } - } - - long grantTotal = 0; - for (long i : allCuboids) { - grantTotal += cuboidHLLMap.get(i).getCountEstimate(); - msg = "Cuboid " + i + " row count is: \t " + cuboidHLLMap.get(i).getCountEstimate(); - writeLine(out, msg); - } - - msg = "Sum of all the cube segments (before merge) is: \t " + totalRowsBeforeMerge; - writeLine(out, msg); - - msg = "After merge, the cube has row count: \t " + grantTotal; - writeLine(out, msg); - - if (grantTotal > 0) { - msg = "The mapper overlap ratio is: \t" + totalRowsBeforeMerge / grantTotal; - writeLine(out, msg); - } - - } finally { - IOUtils.closeQuietly(out); - } - } - - private void writeLine(FSDataOutputStream out, String msg) throws IOException { - out.write(msg.getBytes()); - out.write('\n'); - - } - -}
http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctHiveColumnsMapper2.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctHiveColumnsMapper2.java b/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctHiveColumnsMapper2.java deleted file mode 100644 index bdf631d..0000000 --- a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/FactDistinctHiveColumnsMapper2.java +++ /dev/null @@ -1,232 +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. -*/ - -package org.apache.kylin.engine.mr.steps.fdc2; - -import com.google.common.collect.Lists; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; -import org.apache.hadoop.io.Text; -import org.apache.kylin.common.util.ByteArray; -import org.apache.kylin.common.util.Bytes; -import org.apache.kylin.cube.cuboid.CuboidScheduler; -import org.apache.kylin.engine.mr.common.BatchConstants; -import org.apache.kylin.measure.BufferedMeasureCodec; -import org.apache.kylin.measure.hllc.HyperLogLogPlusCounter; -import org.apache.kylin.metadata.datatype.DataType; -import org.apache.kylin.metadata.model.TblColRef; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.Collection; -import java.util.List; - -/** - */ -public class FactDistinctHiveColumnsMapper2<KEYIN> extends FactDistinctColumnsMapperBase2<KEYIN, Object> { - - protected boolean collectStatistics = false; - protected CuboidScheduler cuboidScheduler = null; - protected int nRowKey; - private Integer[][] allCuboidsBitSet = null; - private HyperLogLogPlusCounter[] allCuboidsHLL = null; - private Long[] cuboidIds; - private HashFunction hf = null; - private int rowCount = 0; - private int samplingPercentage; - private ByteArray[] row_hashcodes = null; - private ByteBuffer keyBuffer; - private static final Text EMPTY_TEXT = new Text(); - public static final byte MARK_FOR_PARTITION_COL = (byte) 0xFE; - public static final byte MARK_FOR_HLL = (byte) 0xFF; - - private int partitionColumnIndex = -1; - private boolean needFetchPartitionCol = true; - - @Override - protected void setup(Context context) throws IOException { - super.setup(context); - keyBuffer = ByteBuffer.allocate(4096); - collectStatistics = Boolean.parseBoolean(context.getConfiguration().get(BatchConstants.CFG_STATISTICS_ENABLED)); - if (collectStatistics) { - samplingPercentage = Integer.parseInt(context.getConfiguration().get(BatchConstants.CFG_STATISTICS_SAMPLING_PERCENT)); - cuboidScheduler = new CuboidScheduler(cubeDesc); - nRowKey = cubeDesc.getRowkey().getRowKeyColumns().length; - - List<Long> cuboidIdList = Lists.newArrayList(); - List<Integer[]> allCuboidsBitSetList = Lists.newArrayList(); - addCuboidBitSet(baseCuboidId, allCuboidsBitSetList, cuboidIdList); - - allCuboidsBitSet = allCuboidsBitSetList.toArray(new Integer[cuboidIdList.size()][]); - cuboidIds = cuboidIdList.toArray(new Long[cuboidIdList.size()]); - - allCuboidsHLL = new HyperLogLogPlusCounter[cuboidIds.length]; - for (int i = 0; i < cuboidIds.length; i++) { - allCuboidsHLL[i] = new HyperLogLogPlusCounter(cubeDesc.getConfig().getCubeStatsHLLPrecision()); - } - - hf = Hashing.murmur3_32(); - row_hashcodes = new ByteArray[nRowKey]; - for (int i = 0; i < nRowKey; i++) { - row_hashcodes[i] = new ByteArray(); - } - - TblColRef partitionColRef = cubeDesc.getModel().getPartitionDesc().getPartitionDateColumnRef(); - if (partitionColRef != null) { - partitionColumnIndex = intermediateTableDesc.getColumnIndex(partitionColRef); - } - - // check whether need fetch the partition col values - if (partitionColumnIndex < 0) { - // if partition col not on cube, no need - needFetchPartitionCol = false; - } else { - for (int x : dictionaryColumnIndex) { - if (x == partitionColumnIndex) { - // if partition col already build dict, no need - needFetchPartitionCol = false; - break; - } - } - } - - } - } - - private void addCuboidBitSet(long cuboidId, List<Integer[]> allCuboidsBitSet, List<Long> allCuboids) { - allCuboids.add(cuboidId); - Integer[] indice = new Integer[Long.bitCount(cuboidId)]; - - long mask = Long.highestOneBit(baseCuboidId); - int position = 0; - for (int i = 0; i < nRowKey; i++) { - if ((mask & cuboidId) > 0) { - indice[position] = i; - position++; - } - mask = mask >> 1; - } - - allCuboidsBitSet.add(indice); - Collection<Long> children = cuboidScheduler.getSpanningCuboid(cuboidId); - for (Long childId : children) { - addCuboidBitSet(childId, allCuboidsBitSet, allCuboids); - } - } - - @Override - public void doMap(KEYIN key, Object record, Context context) throws IOException, InterruptedException { - String[] row = flatTableInputFormat.parseMapperInput(record); - - keyBuffer.clear(); - try { - for (int i = 0; i < factDictCols.size(); i++) { - String fieldValue = row[dictionaryColumnIndex[i]]; - if (fieldValue == null) - continue; - int offset = keyBuffer.position(); - keyBuffer.put(Bytes.toBytes(i)[3]); // one byte is enough - keyBuffer.put(Bytes.toBytes(fieldValue)); - outputKey.set(keyBuffer.array(), offset, keyBuffer.position() - offset); - sortableKey.setText(outputKey); - //judge type - DataType type = factDictCols.get(i).getType(); - if (!type.isNumberFamily()) { - sortableKey.setTypeId((byte) TypeFlag.NONE_NUMERIC_TYPE.ordinal()); - } else if (type.isIntegerFamily()) { - sortableKey.setTypeId((byte) TypeFlag.INTEGER_FAMILY_TYPE.ordinal()); - } else { - sortableKey.setTypeId((byte) TypeFlag.DOUBLE_FAMILY_TYPE.ordinal()); - } - context.write(sortableKey, EMPTY_TEXT); - } - } catch (Exception ex) { - handleErrorRecord(row, ex); - } - - if (collectStatistics) { - if (rowCount < samplingPercentage) { - putRowKeyToHLL(row); - } - - if (needFetchPartitionCol == true) { - String fieldValue = row[partitionColumnIndex]; - if (fieldValue != null) { - int offset = keyBuffer.position(); - keyBuffer.put(MARK_FOR_PARTITION_COL); - keyBuffer.put(Bytes.toBytes(fieldValue)); - outputKey.set(keyBuffer.array(), offset, keyBuffer.position() - offset); - sortableKey.setText(outputKey); - sortableKey.setTypeId((byte) 0); - context.write(sortableKey, EMPTY_TEXT); - } - } - } - - if (rowCount++ == 100) - rowCount = 0; - } - - private void putRowKeyToHLL(String[] row) { - - //generate hash for each row key column - for (int i = 0; i < nRowKey; i++) { - Hasher hc = hf.newHasher(); - String colValue = row[intermediateTableDesc.getRowKeyColumnIndexes()[i]]; - if (colValue != null) { - row_hashcodes[i].set(hc.putString(colValue).hash().asBytes()); - } else { - row_hashcodes[i].set(hc.putInt(0).hash().asBytes()); - } - } - - // user the row key column hash to get a consolidated hash for each cuboid - for (int i = 0, n = allCuboidsBitSet.length; i < n; i++) { - Hasher hc = hf.newHasher(); - for (int position = 0; position < allCuboidsBitSet[i].length; position++) { - hc.putBytes(row_hashcodes[allCuboidsBitSet[i][position]].array()); - } - - allCuboidsHLL[i].add(hc.hash().asBytes()); - } - } - - @Override - protected void doCleanup(Context context) throws IOException, InterruptedException { - if (collectStatistics) { - ByteBuffer hllBuf = ByteBuffer.allocate(BufferedMeasureCodec.DEFAULT_BUFFER_SIZE); - // output each cuboid's hll to reducer, key is 0 - cuboidId - HyperLogLogPlusCounter hll; - for (int i = 0; i < cuboidIds.length; i++) { - hll = allCuboidsHLL[i]; - - keyBuffer.clear(); - keyBuffer.put(MARK_FOR_HLL); // one byte - keyBuffer.putLong(cuboidIds[i]); - outputKey.set(keyBuffer.array(), 0, keyBuffer.position()); - sortableKey.setText(outputKey); - sortableKey.setTypeId((byte) 0); - hllBuf.clear(); - hll.writeRegisters(hllBuf); - outputValue.set(hllBuf.array(), 0, hllBuf.position()); - context.write(sortableKey, outputValue); - } - } - } -} http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/SelfDefineSortableKey.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/SelfDefineSortableKey.java b/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/SelfDefineSortableKey.java deleted file mode 100644 index a3351fa..0000000 --- a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/SelfDefineSortableKey.java +++ /dev/null @@ -1,131 +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. -*/ -package org.apache.kylin.engine.mr.steps.fdc2; - - -import org.apache.hadoop.io.Text; -import org.apache.hadoop.io.WritableComparable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - - -/** - * Created by xiefan on 16-11-1. - */ -public class SelfDefineSortableKey implements WritableComparable<SelfDefineSortableKey> { - - private byte typeId; //non-numeric(0000 0000) int(0000 0001) other numberic(0000 0010) - - private Text text; - - private static final Logger logger = LoggerFactory.getLogger(SelfDefineSortableKey.class); - - public SelfDefineSortableKey() { - } - - public SelfDefineSortableKey(byte typeId, Text text) { - this.typeId = typeId; - this.text = text; - } - - @Override - public int compareTo(SelfDefineSortableKey o) { - if (!o.isNumberFamily()) { - return this.text.compareTo(o.text); - } else { - byte[] data1 = this.text.getBytes(); - byte[] data2 = o.text.getBytes(); - String str1 = new String(data1, 1, data1.length - 1); - String str2 = new String(data2, 1, data2.length - 1); - if (str1 == null || str1.equals("") || str2 == null || str2.equals("")) { - //should not achieve here - logger.error("none numeric value!"); - return 0; - } - if (o.isIntegerFamily()) { //integer type - try { - Long num1 = Long.parseLong(str1); - Long num2 = Long.parseLong(str2); - return num1.compareTo(num2); - } catch (NumberFormatException e) { - System.out.println("NumberFormatException when parse integer family number.str1:" + str1 + " str2:" + str2); - logger.error("NumberFormatException when parse integer family number.str1:" + str1 + " str2:" + str2); - e.printStackTrace(); - return 0; - } - } else { //other numeric type - try { - Double num1 = Double.parseDouble(str1); - Double num2 = Double.parseDouble(str2); - return num1.compareTo(num2); - } catch (NumberFormatException e) { - System.out.println("NumberFormatException when parse double family number.str1:" + str1 + " str2:" + str2); - logger.error("NumberFormatException when parse doul family number.str1:" + str1 + " str2:" + str2); - //e.printStackTrace(); - return 0; - } - } - } - } - - @Override - public void write(DataOutput dataOutput) throws IOException { - dataOutput.writeByte(typeId); - text.write(dataOutput); - } - - @Override - public void readFields(DataInput dataInput) throws IOException { - this.typeId = dataInput.readByte(); - this.text = new Text(); - text.readFields(dataInput); - } - - public short getTypeId() { - return typeId; - } - - public Text getText() { - return text; - } - - public boolean isNumberFamily() { - if (typeId == TypeFlag.NONE_NUMERIC_TYPE.ordinal()) return false; - return true; - } - - public boolean isIntegerFamily() { - return (typeId == TypeFlag.INTEGER_FAMILY_TYPE.ordinal()); - } - - public boolean isOtherNumericFamily() { - return (typeId == TypeFlag.DOUBLE_FAMILY_TYPE.ordinal()); - } - - public void setTypeId(byte typeId) { - this.typeId = typeId; - } - - public void setText(Text text) { - this.text = text; - } -} http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/TypeFlag.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/TypeFlag.java b/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/TypeFlag.java deleted file mode 100644 index c69acfd..0000000 --- a/engine-mr/src/main/java/org/apache/kylin/engine/mr/steps/fdc2/TypeFlag.java +++ /dev/null @@ -1,28 +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. -*/ - -package org.apache.kylin.engine.mr.steps.fdc2; - -/** - * Created by xiefan on 16-11-2. - */ -public enum TypeFlag { - NONE_NUMERIC_TYPE, - INTEGER_FAMILY_TYPE, - DOUBLE_FAMILY_TYPE -} http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/test/java/org/apache/kylin/engine/mr/SortedColumnReaderTest.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/test/java/org/apache/kylin/engine/mr/SortedColumnReaderTest.java b/engine-mr/src/test/java/org/apache/kylin/engine/mr/SortedColumnReaderTest.java new file mode 100644 index 0000000..2f2170c --- /dev/null +++ b/engine-mr/src/test/java/org/apache/kylin/engine/mr/SortedColumnReaderTest.java @@ -0,0 +1,312 @@ +/* + * 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.kylin.engine.mr; + +import org.apache.kylin.dict.ByteComparator; +import org.apache.kylin.dict.BytesConverter; +import org.apache.kylin.dict.DictionaryManager; +import org.apache.kylin.dict.IDictionaryValueEnumerator; +import org.apache.kylin.dict.StringBytesConverter; +import org.apache.kylin.dict.TableColumnValueEnumerator; +import org.apache.kylin.metadata.datatype.DataType; +import org.apache.kylin.source.ReadableTable; +import org.junit.Ignore; +import org.junit.Test; +import org.omg.Messaging.SYNC_WITH_TRANSPORT; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Random; +import java.util.UUID; +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +/** + * Created by xiefan on 16-11-14. + */ +public class SortedColumnReaderTest { + + @Test + public void testReadStringMultiFile() throws Exception{ + String dirPath = "src/test/resources/multi_file_str"; + StringBytesConverter converter = new StringBytesConverter(); + ArrayList<String> correctAnswer = readAllFiles(dirPath); + Collections.sort(correctAnswer, new ByteComparator<String>(new StringBytesConverter())); + SortedColumn column = new SortedColumn(dirPath + "/",DataType.getType("varchar")); + IDictionaryValueEnumerator e = new TableColumnValueEnumerator(column.getReader(),-1); + ArrayList<String> output = new ArrayList<>(); + while(e.moveNext()){ + output.add(new String(e.current())); + } + System.out.println(correctAnswer.size()); + assertTrue(correctAnswer.size() == output.size()); + for(int i=0;i<correctAnswer.size();i++){ + assertEquals(correctAnswer.get(i),output.get(i)); + } + } + + + @Ignore + @Test + public void createStringTestFiles() throws Exception{ + String dirPath = "src/test/resources/multi_file_str"; + String prefix = "src/test/resources/multi_file_str/data_"; + ArrayList<String> data = new ArrayList<>(); + int num = 10000; + for(int i=0;i<num;i++){ + UUID uuid = UUID.randomUUID(); + data.add(uuid.toString()); + } + Collections.sort(data,new ByteComparator<String>(new StringBytesConverter())); + Random rand = new Random(System.currentTimeMillis()); + ArrayList<File> allFiles = new ArrayList<>(); + int fileNum = 5; + for(int i=0;i<fileNum;i++){ + File f = new File(prefix + i); + if(!f.exists()) + f.createNewFile(); + allFiles.add(f); + } + ArrayList<BufferedWriter> bws = new ArrayList<>(); + for(File f : allFiles){ + bws.add(new BufferedWriter(new FileWriter(f))); + } + System.out.println(data.size()); + for(String str : data){ + int fileId = rand.nextInt(fileNum); + BufferedWriter bw = bws.get(fileId); + bw.write(str); + bw.newLine(); + } + for(BufferedWriter bw : bws) + { + bw.flush(); + bw.close(); + } + File dir = new File(dirPath); + File[] files = dir.listFiles(); + for(File file : files){ + System.out.println("file:"+file.getAbsolutePath()+" size:"+file.length()); + } + } + + @Test + public void testReadIntegerMultiFiles() throws Exception{ + String dirPath = "src/test/resources/multi_file_int"; + ArrayList<String> correctAnswer = readAllFiles(dirPath); + Collections.sort(correctAnswer, new Comparator<String>() { + @Override + public int compare(String o1, String o2) { + try{ + Long l1 = Long.parseLong(o1); + Long l2 = Long.parseLong(o2); + return l1.compareTo(l2); + }catch (NumberFormatException e){ + e.printStackTrace(); + return 0; + } + } + }); + SortedColumn column = new SortedColumn(dirPath + "/",DataType.getType("long")); + IDictionaryValueEnumerator e = new TableColumnValueEnumerator(column.getReader(),-1); + ArrayList<String> output = new ArrayList<>(); + while(e.moveNext()){ + System.out.println(new String(e.current())); + output.add(new String(e.current())); + } + System.out.println(correctAnswer.size()); + assertTrue(correctAnswer.size() == output.size()); + for(int i=0;i<correctAnswer.size();i++){ + assertEquals(correctAnswer.get(i),output.get(i)); + } + } + + @Test + public void testEmptyDir() throws Exception{ + String dirPath = "src/test/resources/empty_dir"; + SortedColumn column = new SortedColumn(dirPath + "/",DataType.getType("varchar")); + IDictionaryValueEnumerator e = new TableColumnValueEnumerator(column.getReader(),-1); + ArrayList<String> output = new ArrayList<>(); + while(e.moveNext()){ + System.out.println(new String(e.current())); + output.add(new String(e.current())); + } + System.out.println(output.size()); + } + + @Test + public void testEmptyFile() throws Exception{ + String dirPath = "src/test/resources/multi_file_empty_file"; + ArrayList<String> correctAnswer = readAllFiles(dirPath); + final BytesConverter<String> converter = new StringBytesConverter(); + Collections.sort(correctAnswer, new ByteComparator<String>(new StringBytesConverter())); + System.out.println("correct answer:"+correctAnswer); + SortedColumn column = new SortedColumn(dirPath + "/",DataType.getType("varchar")); + IDictionaryValueEnumerator e = new TableColumnValueEnumerator(column.getReader(),-1); + ArrayList<String> output = new ArrayList<>(); + while(e.moveNext()){ + output.add(new String(e.current())); + } + System.out.println(correctAnswer.size()); + assertTrue(correctAnswer.size() == output.size()); + for(int i=0;i<correctAnswer.size();i++){ + assertEquals(correctAnswer.get(i),output.get(i)); + } + } + + + @Ignore + @Test + public void createIntegerTestFiles() throws Exception{ + String dirPath = "src/test/resources/multi_file_int"; + String prefix = "src/test/resources/multi_file_int/data_"; + Random rand = new Random(System.currentTimeMillis()); + ArrayList<String> data = new ArrayList<>(); + int num = 10000; + for(int i=0;i<num;i++){ + data.add(i+""); + } + ArrayList<File> allFiles = new ArrayList<>(); + int fileNum = 5; + for(int i=0;i<fileNum;i++){ + File f = new File(prefix + i); + if(!f.exists()) + f.createNewFile(); + allFiles.add(f); + } + ArrayList<BufferedWriter> bws = new ArrayList<>(); + for(File f : allFiles){ + bws.add(new BufferedWriter(new FileWriter(f))); + } + System.out.println(data.size()); + for(String str : data){ + int fileId = rand.nextInt(fileNum); + BufferedWriter bw = bws.get(fileId); + bw.write(str); + bw.newLine(); + } + for(BufferedWriter bw : bws) + { + bw.flush(); + bw.close(); + } + File dir = new File(dirPath); + File[] files = dir.listFiles(); + for(File file : files){ + System.out.println("file:"+file.getAbsolutePath()+" size:"+file.length()); + } + } + + @Test + public void testReadDoubleMultiFiles() throws Exception{ + String dirPath = "src/test/resources/multi_file_double"; + ArrayList<String> correctAnswer = readAllFiles(dirPath); + Collections.sort(correctAnswer, new Comparator<String>() { + @Override + public int compare(String o1, String o2) { + try{ + Double d1 = Double.parseDouble(o1); + Double d2 = Double.parseDouble(o2); + return d1.compareTo(d2); + }catch (NumberFormatException e){ + e.printStackTrace(); + return 0; + } + } + }); + SortedColumn column = new SortedColumn(dirPath + "/",DataType.getType("double")); + IDictionaryValueEnumerator e = new TableColumnValueEnumerator(column.getReader(),-1); + ArrayList<String> output = new ArrayList<>(); + while(e.moveNext()){ + System.out.println(new String(e.current())); + output.add(new String(e.current())); + } + System.out.println(correctAnswer.size()); + assertTrue(correctAnswer.size() == output.size()); + for(int i=0;i<correctAnswer.size();i++){ + assertEquals(correctAnswer.get(i),output.get(i)); + } + } + + + @Ignore + @Test + public void createDoubleTestFiles() throws Exception{ + String dirPath = "src/test/resources/multi_file_double"; + String prefix = "src/test/resources/multi_file_double/data_"; + Random rand = new Random(System.currentTimeMillis()); + ArrayList<String> data = new ArrayList<>(); + int num = 10000; + double k = 0.0; + for(int i=0;i<num;i++){ + data.add(k+""); + k+=0.52; + } + ArrayList<File> allFiles = new ArrayList<>(); + int fileNum = 5; + for(int i=0;i<fileNum;i++){ + File f = new File(prefix + i); + if(!f.exists()) + f.createNewFile(); + allFiles.add(f); + } + ArrayList<BufferedWriter> bws = new ArrayList<>(); + for(File f : allFiles){ + bws.add(new BufferedWriter(new FileWriter(f))); + } + System.out.println(data.size()); + for(String str : data){ + int fileId = rand.nextInt(fileNum); + BufferedWriter bw = bws.get(fileId); + bw.write(str); + bw.newLine(); + } + for(BufferedWriter bw : bws) + { + bw.flush(); + bw.close(); + } + File dir = new File(dirPath); + File[] files = dir.listFiles(); + for(File file : files){ + System.out.println("file:"+file.getAbsolutePath()+" size:"+file.length()); + } + } + + private ArrayList<String> readAllFiles(String dirPath) throws Exception{ + ArrayList<String> result = new ArrayList<>(); + File dir = new File(dirPath); + for(File f : dir.listFiles()){ + BufferedReader br = new BufferedReader(new FileReader(f)); + String str = br.readLine(); + while(str != null){ + result.add(str); + str = br.readLine(); + } + } + return result; + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/NumberDictionaryForestTest.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/NumberDictionaryForestTest.java b/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/NumberDictionaryForestTest.java index 47b10e8..677e386 100644 --- a/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/NumberDictionaryForestTest.java +++ b/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/NumberDictionaryForestTest.java @@ -23,8 +23,7 @@ import org.apache.kylin.dict.NumberDictionaryBuilder; import org.apache.kylin.dict.NumberDictionaryForestBuilder; import org.apache.kylin.dict.StringBytesConverter; import org.apache.kylin.dict.TrieDictionaryForest; -import org.apache.kylin.engine.mr.steps.fdc2.SelfDefineSortableKey; -import org.apache.kylin.engine.mr.steps.fdc2.TypeFlag; +import org.junit.Ignore; import org.junit.Test; /** @@ -91,6 +90,7 @@ public class NumberDictionaryForestTest { } } + @Test public void testVerySmallDouble() { List<String> testData = new ArrayList<>(); @@ -151,6 +151,19 @@ public class NumberDictionaryForestTest { } } + @Ignore + @Test + public void testDecimalsWithBeginZero(){ + List<String> testData = new ArrayList<>(); + testData.add("000000000000000000000000000.4868"); + testData.add("00000000000000000000000000000000000000"); + NumberDictionaryForestBuilder b = new NumberDictionaryForestBuilder(); + for (String str : testData) + b.addValue(str); + TrieDictionaryForest<String> dict = b.build(); + dict.dump(System.out); + } + private static TrieDictionaryForest<String> testSerialize(TrieDictionaryForest<String> dict) { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/SelfDefineSortableKeyTest.java ---------------------------------------------------------------------- diff --git a/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/SelfDefineSortableKeyTest.java b/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/SelfDefineSortableKeyTest.java index 81aa836..b03514c 100644 --- a/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/SelfDefineSortableKeyTest.java +++ b/engine-mr/src/test/java/org/apache/kylin/engine/mr/steps/SelfDefineSortableKeyTest.java @@ -12,8 +12,6 @@ import java.util.UUID; import org.apache.hadoop.io.Text; import org.apache.kylin.common.util.Bytes; -import org.apache.kylin.engine.mr.steps.fdc2.SelfDefineSortableKey; -import org.apache.kylin.engine.mr.steps.fdc2.TypeFlag; import org.junit.Test; /** http://git-wip-us.apache.org/repos/asf/kylin/blob/2b7fe610/engine-mr/src/test/resources/multi_file_double/data_0 ---------------------------------------------------------------------- diff --git a/engine-mr/src/test/resources/multi_file_double/data_0 b/engine-mr/src/test/resources/multi_file_double/data_0 new file mode 100644 index 0000000..e3da559 --- /dev/null +++ b/engine-mr/src/test/resources/multi_file_double/data_0 @@ -0,0 +1,2006 @@ +0.0 +2.6 +3.12 +5.199999999999999 +7.279999999999998 +7.799999999999997 +10.399999999999995 +11.439999999999994 +11.959999999999994 +16.63999999999999 +17.15999999999999 +20.279999999999987 +22.879999999999985 +24.439999999999984 +25.479999999999983 +25.999999999999982 +31.719999999999978 +32.23999999999998 +38.48000000000002 +39.00000000000002 +40.56000000000003 +41.080000000000034 +41.60000000000004 +42.64000000000004 +46.80000000000007 +48.88000000000008 +50.44000000000009 +50.96000000000009 +51.480000000000096 +53.040000000000106 +54.600000000000115 +56.160000000000124 +59.800000000000146 +61.88000000000016 +65.00000000000017 +68.12000000000015 +68.64000000000014 +73.8400000000001 +75.40000000000009 +76.44000000000008 +80.60000000000005 +81.12000000000005 +82.16000000000004 +86.32000000000001 +90.99999999999997 +94.63999999999994 +96.71999999999993 +97.23999999999992 +99.8399999999999 +102.95999999999988 +106.07999999999986 +115.43999999999978 +117.51999999999977 +119.59999999999975 +120.11999999999975 +123.23999999999972 +126.8799999999997 +128.9599999999997 +129.4799999999997 +129.99999999999972 +131.03999999999974 +134.1599999999998 +135.71999999999983 +144.56 +145.60000000000002 +150.28000000000011 +151.84000000000015 +152.88000000000017 +153.9200000000002 +157.56000000000026 +159.6400000000003 +161.20000000000033 +165.3600000000004 +166.92000000000044 +172.64000000000055 +173.68000000000058 +175.2400000000006 +178.36000000000067 +179.9200000000007 +180.4400000000007 +181.48000000000073 +183.56000000000077 +184.08000000000078 +185.1200000000008 +186.16000000000082 +190.8400000000009 +196.04000000000102 +197.60000000000105 +198.64000000000107 +201.24000000000112 +203.84000000000117 +213.20000000000135 +214.24000000000137 +215.2800000000014 +219.9600000000015 +222.04000000000153 +223.08000000000155 +224.64000000000158 +225.1600000000016 +228.80000000000166 +229.84000000000168 +230.8800000000017 +239.20000000000186 +241.2800000000019 +243.88000000000196 +244.40000000000197 +247.00000000000202 +250.12000000000208 +251.1600000000021 +254.80000000000217 +255.32000000000218 +256.3600000000022 +260.00000000000205 +266.2400000000018 +269.3600000000017 +269.8800000000017 +271.44000000000165 +275.6000000000015 +277.16000000000145 +277.6800000000014 +280.28000000000134 +281.3200000000013 +286.00000000000114 +286.5200000000011 +293.80000000000086 +294.8400000000008 +296.4000000000008 +300.04000000000065 +301.6000000000006 +303.6800000000005 +304.2000000000005 +304.7200000000005 +309.9200000000003 +310.96000000000026 +312.5200000000002 +315.6400000000001 +317.20000000000005 +323.9599999999998 +326.03999999999974 +326.5599999999997 +338.5199999999993 +339.0399999999993 +341.1199999999992 +341.6399999999992 +346.839999999999 +348.39999999999895 +349.9599999999989 +350.4799999999989 +355.1599999999987 +357.23999999999864 +358.2799999999986 +361.9199999999985 +366.5999999999983 +369.7199999999982 +376.99999999999795 +378.5599999999979 +385.31999999999766 +386.3599999999976 +393.1199999999974 +393.63999999999737 +394.15999999999735 +396.75999999999726 +397.7999999999972 +401.9599999999971 +402.99999999999704 +403.519999999997 +406.6399999999969 +409.2399999999968 +410.2799999999968 +412.3599999999967 +413.91999999999666 +415.9999999999966 +417.55999999999653 +420.6799999999964 +422.23999999999637 +422.75999999999635 +426.3999999999962 +427.4399999999962 +428.99999999999613 +429.5199999999961 +434.19999999999595 +434.71999999999593 +435.2399999999959 +437.31999999999584 +443.03999999999564 +443.5599999999956 +444.0799999999956 +446.15999999999553 +447.7199999999955 +451.35999999999535 +453.95999999999526 +454.47999999999524 +454.9999999999952 +461.239999999995 +462.79999999999495 +463.31999999999493 +463.8399999999949 +464.3599999999949 +471.11999999999466 +471.63999999999464 +476.3199999999945 +478.9199999999944 +480.47999999999433 +480.9999999999943 +483.07999999999424 +484.6399999999942 +485.15999999999417 +487.2399999999941 +488.27999999999406 +495.0399999999938 +497.11999999999375 +500.23999999999364 +500.7599999999936 +501.2799999999936 +506.4799999999934 +506.9999999999934 +508.03999999999337 +508.55999999999335 +509.07999999999333 +510.1199999999933 +511.15999999999326 +516.3599999999931 +516.8799999999931 +517.919999999993 +519.479999999993 +522.0799999999929 +522.5999999999929 +530.9199999999926 +532.4799999999925 +537.6799999999923 +541.3199999999922 +543.9199999999921 +545.4799999999921 +545.999999999992 +549.1199999999919 +553.2799999999918 +553.7999999999918 +554.8399999999917 +561.0799999999915 +561.5999999999915 +565.2399999999914 +568.8799999999912 +571.4799999999912 +579.2799999999909 +579.7999999999909 +580.3199999999908 +587.0799999999906 +592.2799999999904 +593.3199999999904 +594.3599999999904 +595.3999999999903 +603.71999999999 +606.3199999999899 +607.3599999999899 +608.3999999999899 +608.9199999999898 +609.9599999999898 +615.6799999999896 +617.7599999999895 +618.2799999999895 +621.9199999999894 +625.5599999999893 +626.0799999999892 +630.2399999999891 +635.4399999999889 +635.9599999999889 +636.4799999999889 +638.5599999999888 +640.1199999999887 +641.6799999999887 +643.2399999999886 +648.9599999999884 +651.0399999999884 +657.2799999999881 +659.3599999999881 +666.1199999999878 +674.4399999999875 +683.2799999999872 +684.3199999999872 +684.8399999999872 +686.3999999999871 +687.4399999999871 +688.999999999987 +691.079999999987 +696.7999999999868 +697.8399999999867 +699.3999999999867 +700.4399999999866 +703.5599999999865 +705.6399999999865 +707.1999999999864 +711.8799999999862 +715.5199999999861 +717.0799999999861 +719.159999999986 +721.2399999999859 +723.8399999999858 +728.5199999999857 +730.0799999999856 +732.6799999999855 +733.7199999999855 +734.7599999999854 +738.9199999999853 +741.5199999999852 +748.279999999985 +750.8799999999849 +751.3999999999849 +755.5599999999847 +759.7199999999846 +762.3199999999845 +762.8399999999845 +764.9199999999844 +769.5999999999842 +776.879999999984 +777.9199999999839 +779.9999999999839 +781.0399999999838 +783.6399999999837 +787.7999999999836 +790.3999999999835 +792.4799999999834 +793.5199999999834 +794.5599999999833 +795.0799999999833 +797.1599999999833 +803.399999999983 +809.6399999999828 +810.6799999999828 +816.3999999999826 +818.9999999999825 +819.5199999999825 +822.6399999999824 +823.6799999999823 +825.7599999999823 +827.8399999999822 +831.479999999982 +834.079999999982 +834.599999999982 +835.6399999999819 +838.2399999999818 +845.5199999999816 +846.0399999999815 +847.5999999999815 +848.1199999999815 +852.2799999999813 +853.8399999999813 +856.4399999999812 +857.9999999999811 +858.5199999999811 +862.159999999981 +864.7599999999809 +867.8799999999808 +868.3999999999808 +871.5199999999807 +872.0399999999806 +873.0799999999806 +876.7199999999805 +879.3199999999804 +887.6399999999801 +894.3999999999799 +895.4399999999798 +896.4799999999798 +896.9999999999798 +897.5199999999797 +903.7599999999795 +915.1999999999791 +916.7599999999791 +918.319999999979 +922.9999999999789 +925.5999999999788 +927.6799999999787 +940.1599999999783 +947.959999999978 +949.5199999999779 +951.5999999999779 +952.1199999999778 +952.6399999999778 +955.7599999999777 +960.9599999999775 +961.4799999999775 +970.8399999999772 +973.4399999999771 +979.1599999999769 +979.6799999999769 +980.1999999999769 +982.7999999999768 +983.8399999999767 +985.3999999999767 +986.9599999999766 +989.0399999999765 +990.0799999999765 +991.1199999999765 +991.6399999999765 +992.1599999999764 +992.6799999999764 +994.2399999999764 +997.8799999999762 +998.3999999999762 +1010.3599999999758 +1010.8799999999758 +1014.5199999999757 +1016.0799999999756 +1020.7599999999754 +1024.3999999999753 +1025.9599999999753 +1029.0799999999751 +1031.679999999975 +1033.759999999975 +1035.319999999975 +1037.9199999999748 +1039.4799999999748 +1039.9999999999748 +1044.6799999999746 +1048.3199999999745 +1050.9199999999744 +1055.5999999999742 +1058.719999999974 +1060.279999999974 +1065.9999999999739 +1068.0799999999738 +1069.6399999999737 +1072.7599999999736 +1073.2799999999736 +1078.9999999999734 +1083.1599999999733 +1084.1999999999732 +1085.7599999999732 +1093.039999999973 +1095.1199999999728 +1095.6399999999728 +1097.1999999999728 +1100.3199999999727 +1100.8399999999726 +1101.3599999999726 +1106.5599999999724 +1108.1199999999724 +1110.1999999999723 +1113.3199999999722 +1114.8799999999721 +1116.439999999972 +1120.599999999972 +1125.2799999999718 +1127.8799999999717 +1131.5199999999716 +1134.1199999999715 +1136.1999999999714 +1136.7199999999714 +1142.4399999999712 +1142.9599999999712 +1151.7999999999709 +1152.3199999999708 +1155.4399999999707 +1158.0399999999706 +1160.6399999999705 +1161.6799999999705 +1162.1999999999705 +1163.7599999999704 +1166.3599999999703 +1169.9999999999702 +1172.5999999999701 +1173.11999999997 +1177.27999999997 +1178.31999999997 +1183.5199999999697 +1185.0799999999697 +1195.4799999999693 +1196.5199999999693 +1199.6399999999692 +1201.1999999999691 +1203.279999999969 +1205.879999999969 +1206.399999999969 +1208.4799999999689 +1208.9999999999688 +1212.1199999999687 +1214.1999999999687 +1217.3199999999686 +1217.8399999999685 +1221.9999999999684 +1228.2399999999682 +1229.7999999999681 +1230.839999999968 +1231.879999999968 +1234.999999999968 +1238.1199999999678 +1240.1999999999678 +1246.4399999999675 +1247.9999999999675 +1249.0399999999674 +1251.6399999999674 +1253.7199999999673 +1257.8799999999671 +1259.439999999967 +1260.999999999967 +1264.119999999967 +1265.6799999999669 +1266.1999999999668 +1267.7599999999668 +1272.4399999999666 +1277.1199999999665 +1278.1599999999664 +1280.2399999999664 +1283.3599999999662 +1284.3999999999662 +1289.079999999966 +1290.119999999966 +1292.199999999966 +1293.7599999999659 +1294.2799999999659 +1296.3599999999658 +1298.9599999999657 +1300.5199999999656 +1308.3199999999654 +1315.0799999999651 +1315.5999999999651 +1319.759999999965 +1321.839999999965 +1325.9999999999648 +1329.1199999999646 +1330.1599999999646 +1330.6799999999646 +1332.7599999999645 +1333.2799999999645 +1333.7999999999645 +1334.3199999999645 +1334.8399999999644 +1337.9599999999643 +1339.5199999999643 +1341.0799999999642 +1341.5999999999642 +1344.719999999964 +1346.799999999964 +1356.1599999999637 +1358.2399999999636 +1358.7599999999636 +1372.7999999999631 +1374.879999999963 +1377.479999999963 +1378.519999999963 +1379.039999999963 +1380.5999999999628 +1381.1199999999628 +1384.2399999999627 +1385.2799999999627 +1388.3999999999626 +1389.9599999999625 +1394.6399999999624 +1402.959999999962 +1405.039999999962 +1406.599999999962 +1410.2399999999618 +1411.7999999999618 +1412.3199999999617 +1413.3599999999617 +1414.3999999999617 +1415.9599999999616 +1419.0799999999615 +1420.6399999999614 +1421.1599999999614 +1429.4799999999611 +1437.2799999999609 +1441.9599999999607 +1445.5999999999606 +1446.6399999999605 +1451.8399999999604 +1453.9199999999603 +1458.0799999999601 +1458.5999999999601 +1459.63999999996 +1461.71999999996 +1463.79999999996 +1468.9999999999598 +1469.5199999999597 +1471.5999999999597 +1472.1199999999596 +1474.7199999999596 +1478.8799999999594 +1479.9199999999594 +1480.4399999999594 +1483.5599999999592 +1484.0799999999592 +1489.279999999959 +1489.799999999959 +1492.919999999959 +1493.439999999959 +1494.9999999999588 +1496.0399999999588 +1496.5599999999588 +1497.5999999999588 +1500.7199999999586 +1504.8799999999585 +1505.3999999999585 +1506.4399999999584 +1512.6799999999582 +1513.7199999999582 +1514.2399999999582 +1516.319999999958 +1516.839999999958 +1517.359999999958 +1518.919999999958 +1520.479999999958 +1522.039999999958 +1524.6399999999578 +1525.6799999999578 +1526.1999999999578 +1529.3199999999576 +1536.0799999999574 +1537.1199999999574 +1541.2799999999572 +1544.3999999999571 +1549.079999999957 +1551.6799999999569 +1554.7999999999568 +1558.9599999999566 +1559.9999999999566 +1563.1199999999565 +1568.3199999999563 +1570.9199999999562 +1574.559999999956 +1577.159999999956 +1578.719999999956 +1586.5199999999556 +1590.1599999999555 +1592.7599999999554 +1594.8399999999554 +1596.3999999999553 +1597.4399999999553 +1597.9599999999552 +1598.4799999999552 +1598.9999999999552 +1602.119999999955 +1602.639999999955 +1603.159999999955 +1606.279999999955 +1610.4399999999548 +1613.0399999999547 +1614.0799999999547 +1615.1199999999546 +1615.6399999999546 +1618.2399999999545 +1619.2799999999545 +1621.8799999999544 +1624.9999999999543 +1625.5199999999543 +1626.0399999999543 +1629.6799999999541 +1633.319999999954 +1635.399999999954 +1637.4799999999539 +1639.5599999999538 +1645.7999999999536 +1646.3199999999536 +1647.8799999999535 +1650.4799999999534 +1651.5199999999534 +1652.5599999999533 +1656.1999999999532 +1657.7599999999532 +1660.359999999953 +1669.1999999999528 +1669.7199999999527 +1672.3199999999526 +1673.3599999999526 +1674.9199999999526 +1679.5999999999524 +1685.3199999999522 +1687.3999999999521 +1687.919999999952 +1689.999999999952 +1694.1599999999519 +1697.7999999999518 +1699.8799999999517 +1701.4399999999516 +1707.1599999999514 +1710.2799999999513 +1718.079999999951 +1719.119999999951 +1720.159999999951 +1721.719999999951 +1722.7599999999509 +1726.9199999999507 +1727.9599999999507 +1731.0799999999506 +1732.1199999999506 +1732.6399999999505 +1734.1999999999505 +1736.2799999999504 +1736.7999999999504 +1741.4799999999502 +1744.0799999999501 +1745.11999999995 +1745.63999999995 +1750.83999999995 +1753.4399999999498 +1754.4799999999498 +1758.1199999999496 +1764.3599999999494 +1770.5999999999492 +1773.1999999999491 +1775.279999999949 +1776.319999999949 +1781.5199999999488 +1782.0399999999488 +1788.7999999999486 +1790.3599999999485 +1793.4799999999484 +1794.5199999999484 +1795.0399999999483 +1796.5999999999483 +1798.1599999999482 +1798.6799999999482 +1800.2399999999482 +1800.7599999999481 +1801.799999999948 +1802.319999999948 +1808.039999999948 +1813.7599999999477 +1814.2799999999477 +1815.8399999999476 +1816.3599999999476 +1825.1999999999473 +1828.3199999999472 +1831.439999999947 +1835.079999999947 +1836.119999999947 +1836.639999999947 +1838.7199999999468 +1839.2399999999468 +1840.2799999999468 +1844.4399999999466 +1847.0399999999465 +1849.6399999999464 +1850.1599999999464 +1850.6799999999464 +1852.2399999999463 +1853.2799999999463 +1855.3599999999462 +1862.119999999946 +1866.7999999999458 +1878.7599999999454 +1880.8399999999453 +1883.4399999999453 +1884.4799999999452 +1884.9999999999452 +1887.599999999945 +1894.3599999999449 +1899.0399999999447 +1901.1199999999446 +1903.7199999999445 +1904.2399999999445 +1904.7599999999445 +1906.3199999999445 +1906.8399999999444 +1907.3599999999444 +1910.4799999999443 +1912.5599999999442 +1919.319999999944 +1920.879999999944 +1921.399999999944 +1923.4799999999439 +1925.0399999999438 +1927.1199999999437 +1934.3999999999435 +1935.4399999999434 +1936.4799999999434 +1936.9999999999434 +1940.1199999999433 +1942.7199999999432 +1944.2799999999431 +1948.959999999943 +1949.999999999943 +1950.519999999943 +1954.1599999999428 +1957.2799999999427 +1958.3199999999426 +1963.5199999999425 +1971.3199999999422 +1975.999999999942 +1976.519999999942 +1979.639999999942 +1985.8799999999417 +1986.3999999999417 +1988.4799999999416 +1989.5199999999415 +1993.6799999999414 +1995.7599999999413 +1997.8399999999413 +1998.8799999999412 +2005.119999999941 +2006.159999999941 +2014.4799999999407 +2014.9999999999407 +2017.0799999999406 +2017.5999999999406 +2018.6399999999405 +2023.8399999999403 +2027.4799999999402 +2030.0799999999401 +2032.67999999994 +2035.27999999994 +2036.31999999994 +2038.3999999999398 +2040.9999999999397 +2043.5999999999397 +2044.1199999999396 +2044.6399999999396 +2050.8799999999396 +2051.3999999999396 +2053.9999999999395 +2054.5199999999395 +2055.0399999999395 +2059.7199999999393 +2065.439999999939 +2067.519999999939 +2069.599999999939 +2070.119999999939 +2070.639999999939 +2072.199999999939 +2074.799999999939 +2078.4399999999387 +2082.0799999999385 +2085.7199999999384 +2087.2799999999384 +2090.3999999999382 +2091.959999999938 +2093.519999999938 +2096.119999999938 +2097.159999999938 +2097.679999999938 +2105.9999999999377 +2111.7199999999375 +2113.2799999999374 +2114.3199999999374 +2118.4799999999373 +2122.639999999937 +2123.679999999937 +2125.239999999937 +2131.479999999937 +2135.1199999999367 +2138.2399999999366 +2146.0399999999363 +2149.159999999936 +2150.199999999936 +2152.799999999936 +2154.359999999936 +2155.919999999936 +2159.039999999936 +2162.1599999999357 +2172.0399999999354 +2172.5599999999354 +2175.1599999999353 +2178.799999999935 +2179.319999999935 +2180.359999999935 +2189.199999999935 +2192.3199999999347 +2205.839999999934 +2209.479999999934 +2209.999999999934 +2212.079999999934 +2214.679999999934 +2217.799999999934 +2218.8399999999338 +2221.9599999999336 +2222.9999999999336 +2225.0799999999335 +2230.2799999999334 +2230.7999999999333 +2234.959999999933 +2237.559999999933 +2238.599999999933 +2243.799999999933 +2244.319999999933 +2247.4399999999328 +2248.9999999999327 +2250.0399999999327 +2251.0799999999326 +2256.7999999999324 +2258.3599999999324 +2259.3999999999323 +2259.9199999999323 +2263.559999999932 +2264.599999999932 +2267.199999999932 +2269.279999999932 +2271.879999999932 +2273.959999999932 +2276.5599999999317 +2277.0799999999317 +2278.1199999999317 +2283.8399999999315 +2284.3599999999315 +2288.5199999999313 +2289.0399999999313 +2290.5999999999312 +2295.279999999931 +2298.919999999931 +2299.439999999931 +2299.959999999931 +2304.6399999999308 +2310.3599999999306 +2313.9999999999304 +2317.1199999999303 +2319.71999999993 +2322.83999999993 +2323.87999999993 +2324.91999999993 +2328.03999999993 +2329.59999999993 +2330.63999999993 +2331.67999999993 +2332.19999999993 +2333.2399999999298 +2334.2799999999297 +2334.7999999999297 +2337.3999999999296 +2338.4399999999296 +2340.5199999999295 +2345.7199999999293 +2346.7599999999293 +2348.319999999929 +2349.879999999929 +2350.919999999929 +2355.079999999929 +2355.599999999929 +2357.159999999929 +2357.679999999929 +2360.799999999929 +2364.9599999999286 +2370.1599999999285 +2371.7199999999284 +2374.3199999999283 +2377.439999999928 +2377.959999999928 +2378.479999999928 +2379.519999999928 +2380.039999999928 +2381.079999999928 +2381.599999999928 +2383.159999999928 +2384.719999999928 +2385.759999999928 +2387.839999999928 +2394.5999999999276 +2395.6399999999276 +2396.6799999999275 +2397.1999999999275 +2405.519999999927 +2407.599999999927 +2409.159999999927 +2411.239999999927 +2416.439999999927 +2417.999999999927 +2422.6799999999266 +2427.3599999999265 +2428.9199999999264 +2432.0399999999263 +2434.119999999926 +2439.319999999926 +2445.559999999926 +2447.6399999999257 +2449.1999999999257 +2450.2399999999257 +2451.2799999999256 +2454.3999999999255 +2455.9599999999255 +2457.5199999999254 +2461.1599999999253 +2462.719999999925 +2464.799999999925 +2467.399999999925 +2473.639999999925 +2474.679999999925 +2477.2799999999247 +2477.7999999999247 +2485.0799999999244 +2489.7599999999243 +2491.319999999924 +2493.919999999924 +2495.479999999924 +2496.519999999924 +2498.079999999924 +2498.599999999924 +2502.239999999924 +2503.799999999924 +2507.4399999999237 +2510.0399999999236 +2513.6799999999234 +2515.7599999999234 +2516.7999999999233 +2520.439999999923 +2521.479999999923 +2529.279999999923 +2538.1199999999226 +2539.1599999999225 +2539.6799999999225 +2541.7599999999225 +2542.7999999999224 +2545.3999999999223 +2551.639999999922 +2552.679999999922 +2560.479999999922 +2562.0399999999217 +2562.5599999999217 +2565.1599999999216 +2569.8399999999215 +2570.3599999999215 +2571.9199999999214 +2573.9999999999213 +2574.5199999999213 +2577.639999999921 +2585.439999999921 +2590.6399999999207 +2594.2799999999206 +2595.3199999999206 +2597.9199999999205 +2598.4399999999205 +2605.71999999992 +2606.75999999992 +2609.35999999992 +2609.87999999992 +2610.39999999992 +2610.91999999992 +2611.95999999992 +2612.99999999992 +2617.67999999992 +2619.2399999999197 +2624.4399999999196 +2624.9599999999195 +2631.1999999999193 +2632.7599999999193 +2634.319999999919 +2638.479999999919 +2638.999999999919 +2643.679999999919 +2646.279999999919 +2648.8799999999187 +2650.9599999999186 +2655.6399999999185 +2687.8799999999173 +2689.9599999999173 +2698.799999999917 +2700.879999999917 +2705.0399999999167 +2707.1199999999167 +2709.7199999999166 +2710.7599999999165 +2712.8399999999165 +2719.5999999999162 +2720.119999999916 +2725.319999999916 +2726.359999999916 +2728.959999999916 +2731.039999999916 +2731.559999999916 +2738.3199999999156 +2741.9599999999155 +2742.4799999999154 +2745.0799999999153 +2759.119999999915 +2770.0399999999145 +2772.6399999999144 +2783.039999999914 +2783.559999999914 +2787.199999999914 +2790.8399999999137 +2791.8799999999137 +2796.5599999999135 +2800.1999999999134 +2801.2399999999134 +2802.2799999999133 +2805.3999999999132 +2806.959999999913 +2808.519999999913 +2810.599999999913 +2811.119999999913 +2813.199999999913 +2813.719999999913 +2815.799999999913 +2816.839999999913 +2819.4399999999127 +2820.4799999999127 +2821.5199999999127 +2824.1199999999126 +2826.7199999999125 +2827.7599999999125 +2829.3199999999124 +2831.9199999999123 +2834.519999999912 +2839.719999999912 +2841.279999999912 +2841.799999999912 +2844.399999999912 +2848.0399999999117 +2849.5999999999117 +2851.1599999999116 +2853.7599999999115 +2856.8799999999114 +2861.0399999999113 +2861.5599999999113 +2862.0799999999113 +2866.239999999911 +2866.759999999911 +2867.799999999911 +2874.039999999911 +2874.559999999911 +2875.599999999911 +2882.3599999999105 +2885.4799999999104 +2888.5999999999103 +2896.91999999991 +2900.03999999991 +2908.3599999999096 +2910.4399999999096 +2916.6799999999093 +2917.1999999999093 +2918.7599999999093 +2920.839999999909 +2927.079999999909 +2929.159999999909 +2931.239999999909 +2933.3199999999088 +2933.8399999999087 +2935.9199999999087 +2940.5999999999085 +2942.1599999999085 +2946.3199999999083 +2949.439999999908 +2949.959999999908 +2953.599999999908 +2954.639999999908 +2960.879999999908 +2961.9199999999078 +2966.0799999999076 +2966.5999999999076 +2967.1199999999076 +2967.6399999999076 +2969.1999999999075 +2981.159999999907 +2983.759999999907 +2984.799999999907 +2987.399999999907 +2988.959999999907 +2989.999999999907 +2992.0799999999067 +2994.1599999999066 +2997.2799999999065 +3000.3999999999064 +3000.9199999999064 +3001.9599999999064 +3002.9999999999063 +3005.5999999999062 +3009.759999999906 +3014.959999999906 +3016.519999999906 +3020.1599999999057 +3020.6799999999057 +3021.1999999999057 +3022.2399999999056 +3024.8399999999056 +3025.8799999999055 +3027.9599999999054 +3028.4799999999054 +3030.0399999999054 +3032.1199999999053 +3034.719999999905 +3035.759999999905 +3038.359999999905 +3041.999999999905 +3043.039999999905 +3048.2399999999047 +3050.3199999999047 +3050.8399999999046 +3052.9199999999046 +3053.9599999999045 +3054.9999999999045 +3056.0399999999045 +3057.5999999999044 +3059.1599999999044 +3063.839999999904 +3065.919999999904 +3070.599999999904 +3071.119999999904 +3082.5599999999035 +3083.5999999999035 +3085.6799999999034 +3088.2799999999033 +3090.8799999999032 +3091.919999999903 +3092.439999999903 +3092.959999999903 +3093.479999999903 +3097.639999999903 +3103.359999999903 +3103.879999999903 +3104.399999999903 +3111.6799999999025 +3115.3199999999024 +3115.8399999999024 +3116.3599999999024 +3116.8799999999023 +3121.039999999902 +3124.159999999902 +3125.199999999902 +3127.279999999902 +3129.359999999902 +3129.879999999902 +3130.919999999902 +3131.959999999902 +3135.5999999999017 +3138.7199999999016 +3139.2399999999016 +3139.7599999999015 +3140.2799999999015 +3140.7999999999015 +3141.3199999999015 +3142.3599999999014 +3146.5199999999013 +3148.0799999999012 +3149.119999999901 +3151.199999999901 +3151.719999999901 +3153.279999999901 +3154.319999999901 +3161.079999999901 +3162.1199999999008 +3163.1599999999007 +3174.5999999999003 +3175.1199999999003 +3175.6399999999003 +3180.8399999999 +3183.4399999999 +3184.4799999999 +3188.1199999999 +3190.7199999998998 +3192.7999999998997 +3197.4799999998995 +3199.0399999998995 +3204.7599999998993 +3205.7999999998992 +3206.839999999899 +3208.399999999899 +3211.519999999899 +3212.039999999899 +3217.759999999899 +3219.8399999998987 +3222.4399999998986 +3223.4799999998986 +3227.1199999998985 +3228.1599999998984 +3229.7199999998984 +3231.2799999998983 +3232.8399999998983 +3235.439999999898 +3235.959999999898 +3241.679999999898 +3242.199999999898 +3242.719999999898 +3245.839999999898 +3246.359999999898 +3247.399999999898 +3248.9599999998977 +3249.4799999998977 +3251.0399999998976 +3254.6799999998975 +3256.2399999998975 +3258.3199999998974 +3262.4799999998972 +3265.079999999897 +3265.599999999897 +3268.199999999897 +3269.759999999897 +3273.399999999897 +3283.2799999998965 +3287.9599999998964 +3291.599999999896 +3296.799999999896 +3298.879999999896 +3300.959999999896 +3302.519999999896 +3304.079999999896 +3304.5999999998958 +3305.1199999998958 +3305.6399999998957 +3306.1599999998957 +3306.6799999998957 +3309.2799999998956 +3311.3599999998955 +3313.4399999998955 +3320.199999999895 +3321.759999999895 +3326.439999999895 +3327.479999999895 +3328.519999999895 +3333.7199999998948 +3334.2399999998947 +3336.3199999998947 +3344.1199999998944 +3349.839999999894 +3350.359999999894 +3353.479999999894 +3356.079999999894 +3356.599999999894 +3361.7999999998938 +3364.9199999998937 +3371.1599999998934 +3371.6799999998934 +3373.2399999998934 +3377.399999999893 +3377.919999999893 +3379.479999999893 +3380.519999999893 +3381.039999999893 +3385.719999999893 +3391.9599999998927 +3398.1999999998925 +3399.7599999998924 +3401.8399999998924 +3402.3599999998924 +3405.999999999892 +3408.079999999892 +3409.119999999892 +3411.199999999892 +3412.759999999892 +3413.279999999892 +3414.319999999892 +3414.839999999892 +3416.399999999892 +3418.479999999892 +3418.9999999998918 +3423.1599999998916 +3433.0399999998913 +3433.5599999998913 +3437.719999999891 +3443.439999999891 +3443.959999999891 +3444.479999999891 +3446.559999999891 +3447.5999999998908 +3449.1599999998907 +3450.7199999998907 +3452.2799999998906 +3452.7999999998906 +3458.5199999998904 +3460.5999999998903 +3461.1199999998903 +3462.6799999998902 +3466.31999999989 +3467.87999999989 +3472.55999999989 +3476.7199999998898 +3477.7599999998897 +3485.5599999998894 +3486.0799999998894 +3486.5999999998894 +3487.6399999998894 +3488.1599999998894 +3494.919999999889 +3499.599999999889 +3500.119999999889 +3501.159999999889 +3502.719999999889 +3503.239999999889 +3503.759999999889 +3504.279999999889 +3505.8399999998887 +3508.4399999998886 +3508.9599999998886 +3511.0399999998886 +3516.2399999998884 +3518.8399999998883 +3520.919999999888 +3524.039999999888 +3527.159999999888 +3529.759999999888 +3534.9599999998877 +3535.4799999998877 +3535.9999999998877 +3540.1599999998875 +3542.2399999998875 +3548.999999999887 +3551.079999999887 +3556.799999999887 +3560.959999999887 +3563.5599999998867 +3564.0799999998867 +3566.1599999998866 +3568.2399999998865 +3568.7599999998865 +3571.8799999998864 +3576.0399999998863 +3579.159999999886 +3580.719999999886 +3581.239999999886 +3582.279999999886 +3593.7199999998857 +3597.8799999998855 +3599.9599999998854 +3603.5999999998853 +3604.6399999998853 +3609.319999999885 +3609.839999999885 +3610.879999999885 +3615.559999999885 +3616.079999999885 +3623.8799999998846 +3624.3999999998846 +3625.4399999998845 +3626.9999999998845 +3629.0799999998844 +3631.6799999998843 +3632.1999999998843 +3635.319999999884 +3635.839999999884 +3638.959999999884 +3643.119999999884 +3645.199999999884 +3649.8799999998837 +3650.3999999998837 +3651.4399999998836 +3651.9599999998836 +3656.6399999998835 +3657.1599999998834 +3664.959999999883 +3665.999999999883 +3667.559999999883 +3674.839999999883 +3675.359999999883 +3678.9999999998827 +3684.1999999998825 +3685.2399999998825 +3685.7599999998824 +3687.3199999998824 +3694.079999999882 +3702.919999999882 +3704.479999999882 +3707.5999999998817 +3714.3599999998814 +3720.0799999998812 +3720.599999999881 +3724.759999999881 +3725.799999999881 +3726.839999999881 +3729.959999999881 +3730.999999999881 +3731.519999999881 +3733.5999999998808 +3736.1999999998807 +3739.3199999998806 +3739.8399999998805 +3740.3599999998805 +3747.6399999998803 +3748.1599999998803 +3749.71999999988 +3750.23999999988 +3751.27999999988 +3752.83999999988 +3754.39999999988 +3757.51999999988 +3758.55999999988 +3759.07999999988 +3762.1999999998798 +3763.7599999998797 +3764.2799999998797 +3774.6799999998793 +3776.7599999998793 +3777.2799999998792 +3789.239999999879 +3791.8399999998787 +3795.9999999998786 +3796.5199999998786 +3797.5599999998785 +3801.1999999998784 +3802.2399999998784 +3805.8799999998782 +3809.519999999878 +3817.319999999878 +3817.839999999878 +3820.9599999998777 +3828.7599999998774 +3831.3599999998773 +3832.3999999998773 +3837.599999999877 +3842.799999999877 +3843.319999999877 +3848.5199999998767 +3849.0399999998767 +3850.5999999998767 +3851.1199999998767 +3852.1599999998766 +3854.2399999998765 +3854.7599999998765 +3856.8399999998765 +3860.9999999998763 +3862.5599999998763 +3865.679999999876 +3867.759999999876 +3868.799999999876 +3873.479999999876 +3875.039999999876 +3878.1599999998757 +3879.7199999998757 +3882.3199999998756 +3883.3599999998755 +3886.4799999998754 +3888.5599999998753 +3897.399999999875 +3899.999999999875 +3902.599999999875 +3907.2799999998747 +3910.3999999998746 +3912.4799999998745 +3914.5599999998744 +3916.6399999998744 +3920.2799999998742 +3921.839999999874 +3923.399999999874 +3924.439999999874 +3925.999999999874 +3930.679999999874 +3933.7999999998738 +3937.9599999998736 +3940.0399999998735 +3944.1999999998734 +3944.7199999998734 +3945.2399999998734 +3947.8399999998733 +3953.559999999873 +3955.639999999873 +3956.159999999873 +3957.199999999873 +3958.239999999873 +3960.839999999873 +3961.879999999873 +3963.4399999998727 +3975.9199999998723 +3979.039999999872 +3980.599999999872 +3984.759999999872 +3985.279999999872 +3990.479999999872 +3991.5199999998717 +3992.0399999998717 +4000.8799999998714 +4003.4799999998713 +4005.0399999998713 +4005.5599999998713 +4007.639999999871 +4009.199999999871 +4011.279999999871 +4015.439999999871 +4016.479999999871 +4021.6799999998707 +4022.1999999998707 +4023.7599999998706 +4028.4399999998705 +4029.9999999998704 +4030.5199999998704 +4034.1599999998703 +4037.79999999987 +4038.31999999987 +4040.91999999987 +4045.07999999987 +4047.15999999987 +4051.8399999998696 +4055.9999999998695 +4056.5199999998695 +4057.0399999998695 +4059.6399999998694 +4064.319999999869 +4075.239999999869 +4076.279999999869 +4076.7999999998688 +4080.9599999998686 +4083.0399999998685 +4086.1599999998684 +4087.1999999998684 +4087.7199999998684 +4090.3199999998683 +4094.479999999868 +4096.039999999868 +4099.159999999871 +4100.199999999872 +4101.239999999872 +4107.479999999878 +4110.59999999988 +4111.119999999881 +4114.759999999884 +4115.799999999885 +4126.199999999893 +4126.719999999894 +4132.439999999899 +4133.4799999998995 +4136.599999999902 +4139.199999999904 +4140.239999999905 +4141.279999999906 +4145.43999999991 +4145.95999999991 +4146.47999999991 +4151.159999999914 +4152.199999999915 +4153.7599999999165 +4155.319999999918 +4158.959999999921 +4159.999999999922 +4167.279999999928 +4168.319999999929 +4169.35999999993 +4172.999999999933 +4176.639999999936 +4177.159999999936 +4178.199999999937 +4179.239999999938 +4181.31999999994 +4189.119999999946 +4193.79999999995 +4194.839999999951 +4197.959999999954 +4200.039999999955 +4204.199999999959 +4205.23999999996 +4206.279999999961 +4209.399999999963 +4214.079999999967 +4215.6399999999685 +4216.159999999969 +4216.679999999969 +4217.71999999997 +4221.879999999974 +4225.519999999977 +4228.119999999979 +4229.67999999998 +4231.759999999982 +4232.2799999999825 +4236.439999999986 +4237.479999999987 +4237.999999999987 +4245.799999999994 +4250.479999999998 +4254.120000000001 +4255.160000000002 +4259.320000000005 +4263.480000000009 +4266.080000000011 +4267.120000000012 +4268.160000000013 +4269.2000000000135 +4270.760000000015 +4271.800000000016 +4275.960000000019 +4280.640000000023 +4281.1600000000235 +4281.680000000024 +4282.720000000025 +4288.44000000003 +4291.560000000032 +4296.240000000036 +4302.480000000041 +4304.040000000043 +4311.320000000049 +4316.520000000053 +4331.080000000065 +4333.160000000067 +4335.240000000069 +4343.040000000075 +4345.120000000077 +4345.640000000078 +4348.76000000008 +4354.480000000085 +4356.560000000087 +4361.240000000091 +4362.280000000092 +4362.800000000092 +4368.520000000097 +4375.2800000001025 +4376.840000000104 +4377.360000000104 +4378.400000000105 +4383.6000000001095 +4386.720000000112 +4391.9200000001165 +4399.200000000123 +4401.280000000124 +4402.320000000125 +4402.840000000126 +4404.920000000127 +4407.000000000129 +4408.04000000013 +4410.640000000132 +4412.720000000134 +4416.880000000137 +4422.600000000142 +4423.120000000143 +4425.200000000144 +4429.880000000148 +4433.520000000151 +4435.080000000153 +4435.600000000153 +4439.760000000157 +4443.92000000016 +4448.080000000164 +4448.600000000164 +4450.680000000166 +4454.840000000169 +4463.160000000176 +4465.240000000178 +4465.7600000001785 +4467.84000000018 +4470.960000000183 +4472.000000000184 +4472.520000000184 +4474.600000000186 +4475.120000000186 +4475.640000000187 +4476.680000000188 +4478.760000000189 +4480.840000000191 +4483.440000000193 +4487.080000000196 +4492.800000000201 +4493.840000000202 +4497.480000000205 +4500.080000000207 +4501.120000000208 +4502.160000000209 +4504.760000000211 +4507.880000000214 +4508.400000000214 +4513.600000000219 +4519.320000000223 +4526.080000000229 +4527.12000000023 +4528.680000000231 +4530.760000000233 +4531.800000000234 +4532.320000000234 +4544.800000000245 +4545.320000000245 +4546.880000000247 +4549.480000000249 +4552.080000000251 +4554.680000000253 +4555.720000000254 +4558.320000000256 +4559.360000000257 +4560.920000000258 +4564.560000000261 +4566.120000000263 +4566.640000000263 +4568.720000000265 +4571.320000000267 +4576.000000000271 +4577.040000000272 +4577.560000000272 +4579.640000000274 +4580.1600000002745 +4580.680000000275 +4581.720000000276 +4582.240000000276 +4583.280000000277 +4586.92000000028 +4587.960000000281 +4589.000000000282 +4593.680000000286 +4595.240000000287 +4595.760000000288 +4596.280000000288 +4600.440000000292 +4603.040000000294 +4606.160000000296 +4609.280000000299 +4611.360000000301 +4616.560000000305 +4620.720000000309 +4621.240000000309 +4623.840000000311 +4625.920000000313 +4626.960000000314 +4628.000000000315 +4632.680000000319 +4634.24000000032 +4638.400000000323 +4642.0400000003265 +4646.20000000033 +4646.72000000033 +4647.760000000331 +4649.840000000333 +4655.040000000337 +4656.080000000338 +4656.600000000339 +4657.64000000034 +4659.720000000341 +4662.840000000344 +4663.880000000345 +4665.960000000347 +4669.080000000349 +4674.2800000003535 +4675.840000000355 +4676.880000000356 +4677.920000000357 +4684.680000000362 +4690.9200000003675 +4692.480000000369 +4697.680000000373 +4698.720000000374 +4708.080000000382 +4711.720000000385 +4715.8800000003885 +4717.44000000039 +4722.640000000394 +4726.800000000398 +4727.320000000398 +4729.4000000004 +4730.440000000401 +4736.1600000004055 +4746.040000000414 +4746.560000000414 +4747.600000000415 +4749.680000000417 +4751.240000000418 +4752.280000000419 +4756.960000000423 +4759.560000000425 +4763.200000000428 +4773.0800000004365 +4775.160000000438 +4781.4000000004435 +4782.960000000445 +4787.120000000448 +4787.640000000449 +4788.160000000449 +4791.280000000452 +4793.880000000454 +4806.360000000464 +4810.520000000468 +4812.60000000047 +4815.720000000472 +4816.760000000473 +4817.800000000474 +4819.880000000476 +4820.920000000477 +4826.640000000481 +4831.840000000486 +4832.360000000486 +4833.400000000487 +4833.920000000488 +4834.960000000488 +4837.560000000491 +4839.640000000492 +4844.840000000497 +4846.400000000498 +4847.440000000499 +4849.520000000501 +4857.840000000508 +4861.480000000511 +4862.520000000512 +4864.080000000513 +4865.120000000514 +4866.160000000515 +4867.2000000005155 +4867.720000000516 +4868.240000000516 +4868.760000000517 +4870.320000000518 +4873.960000000521 +4874.480000000522 +4876.040000000523 +4880.200000000526 +4881.240000000527 +4881.760000000528 +4890.080000000535 +4894.760000000539 +4895.8000000005395 +4897.360000000541 +4900.480000000543 +4902.040000000545 +4908.80000000055 +4911.920000000553 +4915.560000000556 +4916.0800000005565 +4930.120000000568 +4930.640000000569 +4932.7200000005705 +4934.800000000572 +4941.0400000005775 +4942.080000000578 +4943.64000000058 +4948.840000000584 +4955.60000000059 +4957.6800000005915 +4959.240000000593 +4964.960000000598 +4965.480000000598 +4966.520000000599 +4970.680000000602 +4971.200000000603 +4971.720000000603 +4973.280000000605 +4976.920000000608 +4977.440000000608 +4979.000000000609 +4979.52000000061 +4983.680000000613 +4984.720000000614 +4987.320000000616 +4990.960000000619 +4996.680000000624 +4997.200000000625 +4998.760000000626 +4999.800000000627 +5001.360000000628 +5001.880000000629 +5009.680000000635 +5011.240000000636 +5011.760000000637 +5012.800000000638 +5015.40000000064 +5017.480000000642 +5018.000000000642 +5020.080000000644 +5021.640000000645 +5022.160000000646 +5023.2000000006465 +5026.320000000649 +5028.920000000651 +5034.640000000656 +5042.440000000663 +5043.4800000006635 +5052.840000000671 +5053.360000000672 +5054.920000000673 +5058.040000000676 +5061.680000000679 +5063.7600000006805 +5064.280000000681 +5066.360000000683 +5068.960000000685 +5073.640000000689 +5075.720000000691 +5076.240000000691 +5076.760000000691 +5077.280000000692 +5079.360000000694 +5079.880000000694 +5082.480000000696 +5085.080000000698 +5085.600000000699 +5086.120000000699 +5087.1600000007 +5087.680000000701 +5088.200000000701 +5090.280000000703 +5092.880000000705 +5093.920000000706 +5096.000000000708 +5107.440000000717 +5111.08000000072 +5111.600000000721 +5112.120000000721 +5114.200000000723 +5115.760000000724 +5116.280000000725 +5117.840000000726 +5120.440000000728 +5123.560000000731 +5125.120000000732 +5126.160000000733 +5127.720000000734 +5128.760000000735 +5129.2800000007355 +5132.400000000738 +5134.48000000074 +5136.560000000742 +5138.120000000743 +5143.320000000747 +5143.840000000748 +5145.400000000749 +5146.96000000075 +5147.480000000751 +5148.520000000752 +5149.040000000752 +5151.120000000754 +5151.640000000754 +5154.760000000757 +5156.840000000759 +5158.40000000076 +5163.600000000764 +5164.640000000765 +5165.160000000766 +5168.280000000768 +5169.320000000769 +5170.36000000077 +5171.400000000771 +5171.920000000771 +5175.560000000774 +5176.080000000775 +5177.640000000776 +5179.200000000777 +5180.240000000778 +5184.400000000782 +5187.000000000784 +5188.560000000785 +5192.720000000789 +5194.28000000079 +5197.920000000793